Skip to content

Commit 5798baa

Browse files
rustyconoverclaude
andcommitted
test(table_buffering): pin the empty-input lifecycle the extension now drives
vgi 63eb257 makes the DuckDB extension run init, combine with an EMPTY state_ids list, then finalize when a table-buffering input is empty at runtime. vgi-python needed no change for it: table_buffering_combine passes state_ids straight through, finalize needs no sink state, and the sum_all_columns fixture already writes its zero row from combine. table_buffering_empty_input.test passes on the launch and HTTP lanes against a unittest built from vgi 6033a63, and table_in_out/* passes on the launch lane. vgi-python's CI fails that file only because it runs vgi main's tests against the published extension, which predates 63eb257. These tests drive the same sequence through the Python client with zero process calls: sum_all_columns answers 0 0, buffer_input returns no rows, and a combine() error still surfaces. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 49ff227 commit 5798baa

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
2+
3+
"""A table-buffering function whose input is empty still runs init, combine and finalize.
4+
5+
The DuckDB extension (vgi 63eb257) now drives the whole lifecycle when a
6+
table-buffering input is empty at runtime: the TABLE_BUFFERING init, then
7+
``table_buffering_combine`` with an EMPTY ``state_ids`` list, then finalize. These
8+
drive that exact sequence (no ``process`` call at all) through the Python client,
9+
mirroring ``test/sql/integration/table_in_out/table_buffering_empty_input.test``.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import pyarrow as pa
15+
import pytest
16+
17+
from vgi.client import Client, ClientError
18+
19+
_SCHEMA = pa.schema([pa.field("a", pa.int64()), pa.field("b", pa.int64())])
20+
21+
22+
def _run(worker: str, function_name: str) -> list[pa.RecordBatch]:
23+
"""Invoke *function_name* with zero input batches, so combine sees no state_ids."""
24+
with Client(worker) as client:
25+
return list(
26+
client.table_buffering_function(
27+
function_name=function_name,
28+
schema_path=["main"],
29+
input=iter(()),
30+
input_schema=_SCHEMA,
31+
)
32+
)
33+
34+
35+
def _rows(batches: list[pa.RecordBatch]) -> list[dict[str, object]]:
36+
return [row for batch in batches for row in batch.to_pylist()]
37+
38+
39+
class TestEmptyInputRunsTheLifecycle:
40+
"""combine([]) and finalize with no sink states."""
41+
42+
def test_reduction_answers_with_its_zero_row(self, fixture_worker: str) -> None:
43+
"""``sum_all_columns`` emits ``0 0`` for empty input, not zero rows."""
44+
assert _rows(_run(fixture_worker, "sum_all_columns")) == [{"a": 0, "b": 0}]
45+
46+
def test_function_with_nothing_to_say_returns_no_rows(self, fixture_worker: str) -> None:
47+
"""``buffer_input`` still returns no rows."""
48+
assert _rows(_run(fixture_worker, "buffer_input")) == []
49+
50+
def test_combine_errors_surface_on_empty_input(self, fixture_worker: str) -> None:
51+
"""Combine really runs: its exception reaches the caller."""
52+
with pytest.raises(ClientError, match="Intentional exception during combine"):
53+
_run(fixture_worker, "crash_on_combine")

0 commit comments

Comments
 (0)