Skip to content

Commit 40af24a

Browse files
rustyconoverclaude
andcommitted
ci: run versioning_http + gzip_fallback http tests (new vgi-rpc-java features)
Implement the three remaining HTTP integration features in coordination with vgi-rpc-java (b946c2d) and wire them into CI: - Sticky cookies (attach/versioning_http.test): the versioned catalog sets a vgi_sticky cookie on catalog_attach and asserts it on catalog_version (via the new CallContext.cookies()/setCookie()), proving the C++ cookie jar round-trips Set-Cookie -> Cookie. The minimal versioned fixture now also exposes only its default schema (vgi-python parity; the test asserts exactly one schema). - gzip fallback (http/gzip_fallback.test): a zstd-disabled http worker (VGI_HTTP_DISABLE_ZSTD) advertises gzip only; the C++ client 415s on zstd, reads VGI-Supported-Encodings: gzip, and retries. Runs as a dedicated step since it needs VGI_TEST_WORKER itself to be the special worker. - Bearer auth (Main.buildHttpConfig + BearerAuthenticator): implemented and validated 10/11 against the vgi-python worker, but bearer_token.test isn't greenable under the prebuilt haybarn-unittest (the community C++ extension raises on the no-auth-ATTACH 401 instead of deferring it — fails against the Python reference worker identically), so it stays skipped. CI builds vgirpc from source via the composite include (settings.gradle.kts now honours VGI_RPC_JAVA_DIR; the workflow checks out the pinned vgi-rpc-java) until a vgirpc release ships these features. Main suite: 177 pass / 7 skip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 459bacc commit 40af24a

7 files changed

Lines changed: 105 additions & 21 deletions

File tree

.github/workflows/integration.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ env:
3232
# The Haybarn release providing the prebuilt haybarn-unittest binary. Must be
3333
# ABI-compatible with the community-published vgi extension (both v1.5.3).
3434
HAYBARN_RELEASE: haybarn-v1.5.3-rc7
35+
# The vgi-rpc-java commit built from source (composite include) for the worker's
36+
# HTTP features. Drop once a vgirpc release with these features is published and
37+
# pinned in vgi/build.gradle.kts.
38+
VGI_RPC_JAVA_REF: b946c2de63e176e16ad73b533a4eebdcccddd16d
3539

3640
jobs:
3741
integration:
@@ -56,6 +60,16 @@ jobs:
5660
ref: ${{ env.VGI_REF }}
5761
path: vgi-upstream
5862

63+
# Build the vgirpc RPC layer from source (composite include) so the worker
64+
# picks up the HTTP features the integration suite needs (gzip negotiation,
65+
# sticky cookies) ahead of the next published vgirpc release.
66+
- name: Checkout pinned vgi-rpc-java
67+
uses: actions/checkout@v4
68+
with:
69+
repository: Query-farm/vgi-rpc-java
70+
ref: ${{ env.VGI_RPC_JAVA_REF }}
71+
path: vgi-rpc-java
72+
5973
- name: Set up JDK 25
6074
uses: actions/setup-java@v4
6175
with:
@@ -73,6 +87,8 @@ jobs:
7387

7488
- name: Build example worker
7589
run: ./gradlew --no-daemon :vgi-example-worker:installDist
90+
env:
91+
VGI_RPC_JAVA_DIR: ${{ github.workspace }}/vgi-rpc-java
7692

7793
- name: Download haybarn-unittest
7894
run: |

CLAUDE.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -269,20 +269,28 @@ without a C++ build:
269269
unittest invocation** (like `make test_launcher`) so the CI log streams the
270270
native sqllogictest report (per-test progress + `All tests passed (… N
271271
assertions in M test cases)` — ~8700 assertions across the ~185 files), not a
272-
rolled-up count. Green on both lanes: **176 pass / 8 skip**. Sets
272+
rolled-up count. Green on both lanes: **177 pass / 7 skip** (+
273+
`http/gzip_fallback.test` in a dedicated step). Sets
273274
`VGI_REQUIRE_LAUNCHER_TRANSPORT=1` (we *are* the launcher transport) so
274275
`launcher/options_smoke.test` runs — which required `Main.runWorker` to accept
275276
the launcher cache-key / fixture flags (`--describe`/`--no-describe`/
276277
`--threaded`/`--quiet`/`--debug`/`--log-level`) as no-ops instead of exiting
277-
on unknown argv. Also boots a versioned_tables catalog worker over HTTP and
278-
sets `VGI_VERSIONED_TABLES_HTTP_WORKER` so the 4
279-
`attach/versioned_tables_*_http` tests run (they attach an `http://` worker and
280-
pass against the Java worker). The remaining http-only tests stay skipped —
281-
they need Java worker HTTP features not yet ported: `versioning_http`
282-
(sticky-session `vgi_sticky` cookies), `bearer_token` (HTTP bearer auth),
283-
`gzip_fallback` (zstd-disable negotiation). The whole-suite-over-http
284-
`TRANSPORT=http` path stays local-only (function-backed table-function
285-
streaming, e.g. `example.sequence`, still errors over http). **Excludes**
278+
on unknown argv. Boots versioned_tables + versioned catalog workers over HTTP
279+
(`VGI_VERSIONED_TABLES_HTTP_WORKER`/`VGI_VERSIONED_HTTP_WORKER`) so the 4
280+
`versioned_tables_*_http` tests and `versioning_http` (sticky-cookie
281+
round-trip) run, and runs `gzip_fallback` against a dedicated zstd-disabled
282+
http worker. These use **HTTP features added to vgi-rpc-java** (`b946c2d`
283+
gzip codec negotiation via `VGI-Supported-Encodings`/415,
284+
`CallContext.cookies()`/`setCookie()`), which CI **builds from source** through
285+
the composite include (`VGI_RPC_JAVA_DIR`/`VGI_RPC_JAVA_REF`) until a vgirpc
286+
release ships them. `bearer_token.test` stays skipped: bearer auth IS
287+
implemented (`Main.buildHttpConfig` + `BearerAuthenticator`, validated 10/11
288+
against the vgi-python worker) but the test isn't greenable under the prebuilt
289+
haybarn-unittest — the community C++ extension raises on the no-auth-ATTACH 401
290+
instead of deferring it to the first query, which fails against the Python
291+
reference worker identically. The whole-suite-over-http `TRANSPORT=http` path
292+
stays local-only (function-backed table-function streaming, e.g.
293+
`example.sequence`, still errors over http). **Excludes**
286294
`bool_in_union.test` in addition to `nested_type_combinations` — CI surfaced
287295
that it characterizes a **pre-existing platform-dependent union-bool bug**:
288296
the worker reads uninitialized memory for boolean union variants after row 1

ci/run-integration.sh

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,18 @@ case "$TRANSPORT" in
8989
# We are the launcher transport, so opt into the launcher-only tests
9090
# (launcher/options_smoke.test) — matches vgi's `make test_launcher`.
9191
export VGI_REQUIRE_LAUNCHER_TRANSPORT=1
92-
# Also serve the versioned_tables catalog over HTTP: the four
93-
# attach/versioned_tables_*_http tests attach an http:// worker regardless of
94-
# the main transport, and pass against the Java worker. (The other http-only
95-
# tests need worker HTTP features the Java port doesn't yet implement —
96-
# versioning_http needs sticky-session cookies, bearer_token needs bearer
97-
# auth, gzip_fallback needs zstd-disable negotiation — so their env vars stay
98-
# unset and they skip.)
92+
# Serve the versioned_tables + versioned catalogs over HTTP too: the
93+
# attach/versioned_tables_*_http and attach/versioning_http tests attach an
94+
# http:// worker regardless of the main transport. versioning_http exercises
95+
# the sticky-cookie round-trip (vgi_sticky). (bearer_token / gzip_fallback
96+
# run as a separate step below since they need VGI_TEST_WORKER itself to be a
97+
# specially-configured http worker.)
9998
vth_port="$(boot_http_worker "${HERE}/wrappers/vgi-worker-versioned-tables")"
10099
export VGI_VERSIONED_TABLES_HTTP_WORKER="http://localhost:${vth_port}"
101100
echo "versioned_tables http worker on ${VGI_VERSIONED_TABLES_HTTP_WORKER}"
101+
vh_port="$(boot_http_worker "${HERE}/wrappers/vgi-worker-versioned")"
102+
export VGI_VERSIONED_HTTP_WORKER="http://localhost:${vh_port}"
103+
echo "versioned http worker on ${VGI_VERSIONED_HTTP_WORKER}"
102104
;;
103105
http)
104106
# Whole-suite-over-HTTP. For LOCAL use — NOT yet a CI lane: the Java worker's
@@ -151,3 +153,15 @@ rm -f "$STAGE/test/_warm.test"
151153
# and fails the job (via `set -e`).
152154
echo "Running suite (single invocation — native sqllogictest report) ..."
153155
"$HAYBARN_UNITTEST" "test/sql/integration/*"
156+
157+
# gzip_fallback.test needs VGI_TEST_WORKER itself to be a zstd-disabled HTTP
158+
# worker (it attaches it directly and asserts the gzip codec fallback). That
159+
# conflicts with the main suite's launch: worker, so run it as a dedicated
160+
# single-test invocation against its own worker. Only on the launch lane (shm is
161+
# the same coverage; the http lane is local-only).
162+
if [ "$TRANSPORT" = "launch" ]; then
163+
echo "Running http/gzip_fallback.test (zstd-disabled http worker) ..."
164+
gz_port="$(VGI_HTTP_DISABLE_ZSTD=1 boot_http_worker "$VGI_WORKER_BIN")"
165+
VGI_TEST_WORKER="http://localhost:${gz_port}" VGI_HTTP_DISABLE_ZSTD=1 \
166+
"$HAYBARN_UNITTEST" "test/sql/integration/http/gzip_fallback.test"
167+
fi

settings.gradle.kts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ rootProject.name = "vgi-java"
99
// Composite-include the sibling vgi-rpc-java repo so :vgirpc is built from
1010
// source; falls back gracefully if the directory isn't present (CI may pin
1111
// to a published artifact instead).
12-
val vgiRpcJavaDir = file("../Development/vgi-rpc-java")
12+
// VGI_RPC_JAVA_DIR overrides the path (CI checks out the sibling repo inside
13+
// the workspace and points here so it can build vgirpc from source — actions/
14+
// checkout can't write the default ../Development sibling path).
15+
val vgiRpcJavaDir = System.getenv("VGI_RPC_JAVA_DIR")?.let { file(it) }
16+
?: file("../Development/vgi-rpc-java")
1317
if (vgiRpcJavaDir.isDirectory) {
1418
includeBuild(vgiRpcJavaDir) {
1519
dependencySubstitution {

vgi-example-worker/src/main/java/farm/query/vgi/example/Main.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import farm.query.vgi.SettingSpec;
66
import farm.query.vgi.internal.SchemaUtil;
77
import farm.query.vgi.Worker;
8+
import farm.query.vgirpc.AuthContext;
9+
import farm.query.vgirpc.http.HttpServer;
10+
import farm.query.vgirpc.http.auth.BearerAuthenticator;
811
import farm.query.vgi.example.scalar.AddValuesFunction;
912
import farm.query.vgi.example.scalar.BinaryPacketFunction;
1013
import farm.query.vgi.example.scalar.AnyMixedFunctions;
@@ -1064,10 +1067,26 @@ private static void runWorker(Worker w, String[] args) {
10641067
try { w.runUnixSocket(java.nio.file.Path.of(unixSocket), idleTimeoutMs); }
10651068
catch (Exception e) { e.printStackTrace(); System.exit(1); }
10661069
} else if (http) {
1067-
try { w.runHttp(host, port); }
1070+
try { w.runHttp(buildHttpConfig(host, port)); }
10681071
catch (Exception e) { e.printStackTrace(); System.exit(1); }
10691072
} else {
10701073
w.runStdio();
10711074
}
10721075
}
1076+
1077+
/**
1078+
* Build the HTTP server config from the environment. {@code VGI_TEST_BEARER_TOKEN}
1079+
* turns on bearer-token auth (the server accepts only that token; the C++
1080+
* extension presents it via the {@code bearer_token} ATTACH option). Other
1081+
* HTTP-feature knobs ({@code VGI_HTTP_DISABLE_ZSTD}) are applied here too.
1082+
*/
1083+
private static HttpServer.Config buildHttpConfig(String host, int port) {
1084+
HttpServer.Config.Builder cb = HttpServer.Config.builder().host(host).port(port);
1085+
String bearer = System.getenv("VGI_TEST_BEARER_TOKEN");
1086+
if (bearer != null && !bearer.isEmpty()) {
1087+
cb.authenticator(BearerAuthenticator.fromMap(
1088+
Map.of(bearer, new AuthContext("bearer", true, "vgi-test", Map.of()))));
1089+
}
1090+
return cb.build();
1091+
}
10731092
}

vgi/src/main/java/farm/query/vgi/VgiService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ default farm.query.vgi.protocol.TableBufferingDestructorResponse table_buffering
241241
* @param transaction_opaque_data optional in-flight transaction handle
242242
* @return the monotonically increasing catalog version; default {@code 0}
243243
*/
244-
default CatalogVersionResponse catalog_version(byte[] attach_opaque_data, @Nullable byte[] transaction_opaque_data) {
244+
default CatalogVersionResponse catalog_version(byte[] attach_opaque_data, @Nullable byte[] transaction_opaque_data,
245+
CallContext ctx) {
245246
return new CatalogVersionResponse(0L);
246247
}
247248

vgi/src/main/java/farm/query/vgi/internal/VgiServiceImpl.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,17 @@ public ItemsResponse catalog_catalogs() {
747747
* @return the catalog version response
748748
*/
749749
@Override
750-
public farm.query.vgi.protocol.CatalogVersionResponse catalog_version(byte[] attach_opaque_data, byte[] transaction_opaque_data) {
750+
public farm.query.vgi.protocol.CatalogVersionResponse catalog_version(byte[] attach_opaque_data,
751+
byte[] transaction_opaque_data, CallContext ctx) {
752+
// Versioned fixture: assert the routing cookie set at ATTACH is echoed
753+
// back. Over HTTP a populated cookie jar that's missing vgi_sticky means
754+
// the client failed to plumb Set-Cookie -> Cookie. Over subprocess the
755+
// cookie map is empty, so the check is skipped. (vgi-python parity.)
756+
if ("versioned".equals(worker.catalogName()) && ctx != null
757+
&& !ctx.cookies().isEmpty() && !ctx.cookies().containsKey("vgi_sticky")) {
758+
throw new IllegalStateException(
759+
"expected cookie 'vgi_sticky' on follow-up request; got " + ctx.cookies().keySet());
760+
}
751761
return new farm.query.vgi.protocol.CatalogVersionResponse(1L);
752762
}
753763

@@ -866,6 +876,14 @@ public CatalogAttachResult catalog_attach(CatalogAttachRequest request, CallCont
866876
Map<String, String> tags = new java.util.LinkedHashMap<>(worker.catalogTags() == null ? Map.of() : worker.catalogTags());
867877
if (resolvedImpl != null) tags.put("vgi_resolved_implementation_version", resolvedImpl);
868878
if (resolvedData != null) tags.put("vgi_resolved_data_version", resolvedData);
879+
// Versioned fixture: pin an HTTP routing cookie so the C++ cookie jar
880+
// round-trips Set-Cookie -> Cookie; catalog_version asserts it on every
881+
// follow-up RPC (attach/versioning_http.test). No-op on non-HTTP
882+
// transports (set_cookie has no sink there), matching vgi-python's
883+
// VersionedCatalog.
884+
if ("versioned".equals(worker.catalogName()) && ctx != null) {
885+
ctx.setCookie("vgi_sticky", java.util.UUID.randomUUID().toString().replace("-", ""));
886+
}
869887
return new CatalogAttachResult(
870888
sealer.sealAttach(attachId, authOf(ctx)),
871889
true, true, false, // supports_transactions, supports_time_travel, catalog_version_frozen
@@ -1056,6 +1074,10 @@ private List<SchemaDesc> workerSchemas() {
10561074
String defaultSchema = worker.defaultSchema();
10571075
result.add(new SchemaDesc(defaultSchema,
10581076
comments.getOrDefault(defaultSchema, "Default schema")));
1077+
// The minimal `versioned` fixture exposes only its default schema
1078+
// (vgi-python parity); the example worker's auxiliary `data` schema is
1079+
// not part of it. attach/versioning_http.test asserts exactly one schema.
1080+
if ("versioned".equals(worker.catalogName())) return result;
10591081
java.util.LinkedHashSet<String> extras = new java.util.LinkedHashSet<>();
10601082
for (var t : worker.catalogTables()) {
10611083
if (!defaultSchema.equals(t.schema())) extras.add(t.schema());

0 commit comments

Comments
 (0)