Skip to content

Commit efc2fa7

Browse files
committed
test: exercise real database worker artifacts
1 parent 7c29d6a commit efc2fa7

10 files changed

Lines changed: 395 additions & 5 deletions

File tree

CLAUDE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ the matrix had the machinery but nothing ran it, so the failures accumulated
9292
silently. It is not wired into CI yet (four toolchains on one runner); until it
9393
is, running it locally is the guardrail.
9494

95+
### Database-packaged worker artifacts (`make test_database_workers`)
96+
97+
The ordinary `database_worker/package.test` uses a tiny executable wrapper so
98+
the default regression stays fast. The explicit heavyweight target builds and
99+
packages three real workers, then restores and executes each one through
100+
`database://`: a PyInstaller-frozen Python/PyArrow worker, Bun's standalone
101+
build of `~/Development/vgi-open-meteo`, and a `.tar.gz` containing the native
102+
Rust example worker plus a metadata file. It expects the sibling vgi-python,
103+
vgi-open-meteo, and vgi-rust repositories and the `uv`, `bun`, `cargo`, and
104+
`tar` tools. Repository paths and cross-build targets are overrideable; see
105+
`docs/database-worker-transport.md`.
106+
95107
### HTTP Transport
96108

97109
```bash

Makefile

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,33 @@ VGI_JAVA_DIR ?= $(HOME)/vgi-java
446446
VGI_RUST_DIR ?= $(HOME)/Development/vgi-rust
447447
VGI_CSHARP_DIR ?= $(HOME)/Development/vgi-csharp
448448

449-
.PHONY: test_python test_python_crash test_go test_typescript test_java test_rust test_csharp test_languages
449+
.PHONY: test_python test_python_crash test_go test_typescript test_java test_rust test_csharp test_languages \
450+
build_database_workers test_database_workers
451+
452+
# Real database-package demonstrations. These are intentionally separate from
453+
# the fast default suite: PyInstaller and Bun each produce a ~100 MiB native
454+
# executable, and Rust compiles the full example worker. The resulting tests do
455+
# not delegate to VGI_TEST_WORKER; the BLOB restored from DuckDB is the worker.
456+
VGI_DATABASE_WORKER_FIXTURE_DIR ?= $(PROJ_DIR)build/database-worker-fixtures
457+
VGI_DATABASE_PYTHON_DIR ?= $(HOME)/Development/vgi-python
458+
VGI_DATABASE_RUST_DIR ?= $(HOME)/Development/vgi-rust
459+
VGI_DATABASE_OPEN_METEO_DIR ?= $(HOME)/Development/vgi-open-meteo
460+
461+
build_database_workers:
462+
VGI_DATABASE_WORKER_FIXTURE_DIR="$(VGI_DATABASE_WORKER_FIXTURE_DIR)" \
463+
VGI_DATABASE_PYTHON_DIR="$(VGI_DATABASE_PYTHON_DIR)" \
464+
VGI_DATABASE_RUST_DIR="$(VGI_DATABASE_RUST_DIR)" \
465+
VGI_DATABASE_OPEN_METEO_DIR="$(VGI_DATABASE_OPEN_METEO_DIR)" \
466+
./scripts/build_database_worker_fixtures.sh
467+
468+
test_database_workers: build_database_workers
469+
database_worker_cache="$$(mktemp -d)"; \
470+
trap 'rm -rf "$$database_worker_cache"' 0; \
471+
XDG_CACHE_HOME="$$database_worker_cache" \
472+
VGI_DATABASE_PYTHON_WORKER="$(VGI_DATABASE_WORKER_FIXTURE_DIR)/vgi-python-worker" \
473+
VGI_DATABASE_BUN_WORKER="$(VGI_DATABASE_WORKER_FIXTURE_DIR)/vgi-open-meteo" \
474+
VGI_DATABASE_RUST_WORKER="$(VGI_DATABASE_WORKER_FIXTURE_DIR)/vgi-rust-worker.tar.gz" \
475+
./build/release/test/unittest "test/sql/integration/database_worker/real_*"
450476

451477
# Python uses this repo's default worker set. It does NOT just chain to
452478
# test_launcher, which invokes `unittest` bare: a lane that stops running tests

docs/database-worker-transport.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,41 @@ before retrying when immediate deletion is required.
127127
This feature executes native code stored in the selected database. Only attach
128128
package registries whose writers you trust, and use the digest pin when the
129129
expected build is known out of band.
130+
131+
## Real-worker packaging tests
132+
133+
The fast default regression packages a small executable wrapper so cache and
134+
lease behavior stays cheap. The explicit heavyweight lane builds and executes
135+
three complete artifacts instead:
136+
137+
```bash
138+
make test_database_workers
139+
```
140+
141+
- Python: `uv` runs PyInstaller against
142+
`test/support/database_workers/python_worker.py`, producing one executable
143+
containing CPython, PyArrow, VGI, and its dependencies.
144+
- JavaScript/TypeScript: Bun compiles the actual
145+
`~/Development/vgi-open-meteo/src/bin/worker.ts` graph and the Bun runtime
146+
into one executable. The test restores it from the BLOB and queries the
147+
real catalog's static `weather_codes` view.
148+
- Rust: Cargo builds `vgi-example-worker`; the lane places it and a metadata
149+
file in a `.tar.gz`, then proves VGI extracts `bin/vgi-rust-worker` and runs
150+
`sequence(7)`.
151+
152+
Override `VGI_DATABASE_PYTHON_DIR`, `VGI_DATABASE_OPEN_METEO_DIR`, or
153+
`VGI_DATABASE_RUST_DIR` when the sibling repositories live elsewhere. Bun can
154+
cross-compile by setting `VGI_BUN_TARGET` (for example,
155+
`bun-linux-arm64`). Rust cross-compilation uses `VGI_RUST_TARGET` and
156+
`cargo-zigbuild`, for example:
157+
158+
```bash
159+
CARGO_ZIGBUILD_ZIG_COMMAND="$(command -v python-zig)" \
160+
VGI_RUST_TARGET=x86_64-unknown-linux-gnu make build_database_workers
161+
```
162+
163+
Use `build_database_workers`, rather than `test_database_workers`, when the
164+
target architecture differs from the build host. The builder itself is a
165+
POSIX-host development tool. PyInstaller artifacts always target the build OS
166+
and architecture; `VGI_BUN_TARGET` affects only the Bun artifact and
167+
`VGI_RUST_TARGET` affects only the Rust artifact.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env bash
2+
# Build real, self-contained database:// worker artifacts for the host platform.
3+
4+
set -euo pipefail
5+
6+
project_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
7+
output_dir=${VGI_DATABASE_WORKER_FIXTURE_DIR:-"${project_dir}/build/database-worker-fixtures"}
8+
python_dir=${VGI_DATABASE_PYTHON_DIR:-"${HOME}/Development/vgi-python"}
9+
rust_dir=${VGI_DATABASE_RUST_DIR:-"${HOME}/Development/vgi-rust"}
10+
open_meteo_dir=${VGI_DATABASE_OPEN_METEO_DIR:-"${HOME}/Development/vgi-open-meteo"}
11+
12+
for command_name in uv bun cargo tar; do
13+
if ! command -v "${command_name}" >/dev/null 2>&1; then
14+
echo "database-worker fixtures require ${command_name} on PATH" >&2
15+
exit 1
16+
fi
17+
done
18+
for source_dir in "${python_dir}" "${rust_dir}" "${open_meteo_dir}"; do
19+
if [[ ! -d "${source_dir}" ]]; then
20+
echo "database-worker fixture source directory does not exist: ${source_dir}" >&2
21+
exit 1
22+
fi
23+
done
24+
25+
mkdir -p "${output_dir}"
26+
scratch_dir=$(mktemp -d "${TMPDIR:-/tmp}/vgi-database-workers.XXXXXX")
27+
cleanup() {
28+
rm -rf "${scratch_dir}"
29+
}
30+
trap cleanup EXIT
31+
32+
echo "Building frozen Python worker"
33+
uv run --project "${python_dir}" --locked --with pyinstaller==6.22.2 \
34+
pyinstaller --clean --onefile --name vgi-python-worker \
35+
--distpath "${output_dir}" \
36+
--workpath "${scratch_dir}/pyinstaller-build" \
37+
--specpath "${scratch_dir}/pyinstaller-spec" \
38+
"${project_dir}/test/support/database_workers/python_worker.py"
39+
40+
if [[ -n "${VGI_BUN_TARGET:-}" ]]; then
41+
bun_target=${VGI_BUN_TARGET}
42+
else
43+
host_os=$(uname -s)
44+
host_arch=$(uname -m)
45+
case "${host_os}/${host_arch}" in
46+
Darwin/arm64) bun_target=bun-darwin-arm64 ;;
47+
Darwin/x86_64) bun_target=bun-darwin-x64 ;;
48+
Linux/aarch64|Linux/arm64) bun_target=bun-linux-arm64 ;;
49+
Linux/x86_64) bun_target=bun-linux-x64-baseline ;;
50+
*)
51+
echo "cannot infer a Bun executable target for ${host_os}/${host_arch}" >&2
52+
exit 1
53+
;;
54+
esac
55+
fi
56+
57+
echo "Building Open Meteo worker for ${bun_target}"
58+
(
59+
cd "${open_meteo_dir}"
60+
bun install --frozen-lockfile
61+
bun build src/bin/worker.ts --compile --target="${bun_target}" \
62+
--outfile "${output_dir}/vgi-open-meteo"
63+
)
64+
65+
echo "Building Rust worker"
66+
if [[ -n "${VGI_RUST_TARGET:-}" ]]; then
67+
if ! command -v cargo-zigbuild >/dev/null 2>&1; then
68+
echo "VGI_RUST_TARGET requires cargo-zigbuild on PATH" >&2
69+
exit 1
70+
fi
71+
(
72+
cd "${rust_dir}"
73+
cargo zigbuild --locked --release --target "${VGI_RUST_TARGET}" -p vgi-example-worker
74+
)
75+
rust_binary="${rust_dir}/target/${VGI_RUST_TARGET}/release/vgi-example-worker"
76+
else
77+
(
78+
cd "${rust_dir}"
79+
cargo build --locked --release -p vgi-example-worker
80+
)
81+
rust_binary="${rust_dir}/target/release/vgi-example-worker"
82+
fi
83+
84+
# Use a multi-file archive for Rust so this lane proves safe tar.gz extraction,
85+
# relative entrypoint selection, and execution rather than only raw BLOB copies.
86+
mkdir -p "${scratch_dir}/rust-package/bin"
87+
install -m 0755 "${rust_binary}" "${scratch_dir}/rust-package/bin/vgi-rust-worker"
88+
printf '%s\n' "VGI Rust database-worker fixture" > "${scratch_dir}/rust-package/BUILD.txt"
89+
tar -czf "${output_dir}/vgi-rust-worker.tar.gz" \
90+
-C "${scratch_dir}/rust-package" bin BUILD.txt
91+
92+
echo "Built database-worker artifacts in ${output_dir}"
93+
ls -lh "${output_dir}/vgi-python-worker" \
94+
"${output_dir}/vgi-open-meteo" \
95+
"${output_dir}/vgi-rust-worker.tar.gz"

test/sql/integration/database_worker/errors.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# name: test/sql/integration/database_worker/errors.test
2-
# description: database:// URI, row uniqueness, digest, size, and archive-path validation
2+
# description: database:// URI, row uniqueness, digest, size, and entrypoint validation
33
# group: [vgi_integration_database_worker]
44

55
require vgi

test/sql/integration/database_worker/package.test

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
# name: test/sql/integration/database_worker/package.test
2-
# description: package macro + database:// resolve/cache/lease/cleanup lifecycle
2+
# description: fast wrapper smoke + database:// resolve/cache/lease/cleanup lifecycle
33
# group: [vgi_integration_database_worker]
44

55
require-env VGI_TEST_WORKER
66

77
require vgi
88

9+
# Keep cache lifecycle tests out of the developer's persistent cache. Pointing
10+
# both transports at the same test-local root preserves the namespace-safety
11+
# assertion below.
12+
statement ok
13+
SET vgi_worker_cache_dir = '__TEST_DIR__/vgi_database_worker_package_cache';
14+
15+
statement ok
16+
SET vgi_github_cache_dir = '__TEST_DIR__/vgi_database_worker_package_cache';
17+
918
statement ok
1019
CREATE TABLE worker_packages AS
1120
SELECT * FROM vgi_worker_package(
1221
'test/support/database_worker_fixture.sh',
1322
'fixture',
1423
'test-1');
1524

25+
# This fast default-suite fixture is an executable wrapper around
26+
# VGI_TEST_WORKER. The heavyweight test_database_workers target separately
27+
# packages and runs self-contained Python, Bun, and Rust worker artifacts.
28+
1629
query TITT
1730
SELECT worker_name, platform = (SELECT platform FROM pragma_platform()), package_version, package_format
1831
FROM worker_packages;
@@ -61,7 +74,9 @@ SELECT count(*) FROM db_worker_2.main.sequence(3);
6174
3
6275

6376
query II
64-
SELECT count(*), bool_and(in_use) FROM vgi_worker_cache();
77+
SELECT count(*), bool_and(in_use)
78+
FROM vgi_worker_cache()
79+
WHERE source = 'database://memory/main/worker_packages/fixture?package_version=test-1';
6580
----
6681
1 true
6782

@@ -89,6 +104,8 @@ SELECT removed, skipped_in_use FROM vgi_worker_cache_flush();
89104
1 0
90105

91106
query I
92-
SELECT count(*) FROM vgi_worker_cache();
107+
SELECT count(*)
108+
FROM vgi_worker_cache()
109+
WHERE source = 'database://memory/main/worker_packages/fixture?package_version=test-1';
93110
----
94111
0
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# name: test/sql/integration/database_worker/real_bun_open_meteo.test
2+
# description: compile the real Bun Open Meteo worker, store it as a BLOB, and execute it
3+
# group: [vgi_integration_database_worker]
4+
5+
require-env VGI_DATABASE_BUN_WORKER
6+
7+
require vgi
8+
9+
statement ok
10+
SET vgi_worker_cache_dir = '__TEST_DIR__/vgi_database_worker_bun_cache';
11+
12+
statement ok
13+
CREATE TABLE bun_worker_packages AS
14+
SELECT * FROM vgi_worker_package(
15+
'${VGI_DATABASE_BUN_WORKER}',
16+
'open-meteo',
17+
'bun-standalone-1');
18+
19+
query IT
20+
SELECT octet_length(contents) > 1000000, package_format
21+
FROM bun_worker_packages;
22+
----
23+
true executable
24+
25+
statement ok
26+
ATTACH 'open_meteo' AS meteo (
27+
TYPE vgi,
28+
LOCATION 'database://memory/main/bun_worker_packages/open-meteo?package_version=bun-standalone-1');
29+
30+
# Static catalog data makes this deterministic and proves the real worker runs
31+
# without making the test depend on the live Open-Meteo API.
32+
query I
33+
SELECT count(*) > 0 FROM meteo.main.weather_codes;
34+
----
35+
true
36+
37+
statement ok
38+
DETACH meteo;
39+
40+
query I
41+
SELECT flushed >= 1 FROM vgi_worker_pool_flush();
42+
----
43+
true
44+
45+
query II
46+
SELECT removed, skipped_in_use FROM vgi_worker_cache_flush();
47+
----
48+
1 0
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# name: test/sql/integration/database_worker/real_python.test
2+
# description: freeze a real Python VGI worker, store it as a BLOB, and execute it
3+
# group: [vgi_integration_database_worker]
4+
5+
require-env VGI_DATABASE_PYTHON_WORKER
6+
7+
require vgi
8+
9+
statement ok
10+
SET vgi_worker_cache_dir = '__TEST_DIR__/vgi_database_worker_python_cache';
11+
12+
statement ok
13+
CREATE TABLE python_worker_packages AS
14+
SELECT * FROM vgi_worker_package(
15+
'${VGI_DATABASE_PYTHON_WORKER}',
16+
'python-fixture',
17+
'pyinstaller-1');
18+
19+
query IITT
20+
SELECT octet_length(contents) > 1000000,
21+
length(sha256),
22+
package_format,
23+
platform = (SELECT platform FROM pragma_platform())
24+
FROM python_worker_packages;
25+
----
26+
true 64 executable true
27+
28+
statement ok
29+
ATTACH 'python_package' AS python_worker (
30+
TYPE vgi,
31+
LOCATION 'database://memory/main/python_worker_packages/python-fixture?package_version=pyinstaller-1');
32+
33+
query II
34+
SELECT count(*), sum(n) FROM python_worker.main.series(6);
35+
----
36+
6 15
37+
38+
statement ok
39+
DETACH python_worker;
40+
41+
query I
42+
SELECT flushed >= 1 FROM vgi_worker_pool_flush();
43+
----
44+
true
45+
46+
query II
47+
SELECT removed, skipped_in_use FROM vgi_worker_cache_flush();
48+
----
49+
1 0

0 commit comments

Comments
 (0)