Add PG version test matrix (14-18)

Shell script drives the Dockerfile builder stage across PG versions,
capturing pass/fail + timing per version. Makefile targets: test-matrix,
test-pg%, test-matrix-clean. Also runs standalone DE reader test in the
builder stage to catch compiler-version regressions.

Fix pork chop grid test: add ORDER BY to CROSS JOIN (optimizer chooses
different join nesting across PG versions, reordering rows).
This commit is contained in:
Ryan Malloy 2026-02-17 14:26:53 -07:00
parent 4f8ad7cea1
commit b18cded4c2
7 changed files with 137 additions and 4 deletions

4
.gitignore vendored
View File

@ -18,6 +18,10 @@ log/
.vscode/
.idea/
# Test artifacts
test/matrix-logs/
test/test_de_reader
# Docs site
docs/node_modules/
docs/dist/

View File

@ -257,6 +257,21 @@ All numerical logic is byte-identical to upstream. Verified against 518 Vallado
| de_ephemeris | DE function fallback to VSOP87, cross-provider consistency, error handling |
| vallado_518 | 518 Vallado test vectors (AIAA 2006-6753-Rev1), per-satellite breakdown |
### PG Version Matrix
Test all 13 regression suites + DE reader unit test across PostgreSQL 14-18 using Docker:
```bash
make test-matrix # Full matrix (PG 14-18)
make test-pg18 # Single version
PG_TEST_VERSIONS="16 17" make test-matrix # Subset
make test-matrix-clean # Remove logs + test images
```
Logs saved to `test/matrix-logs/pg${ver}.log`. The script reuses the Dockerfile `builder` stage as the test engine — no additional test infrastructure.
**Adding a new PG version:** Update `PG_TEST_VERSIONS` default in `Makefile` and `PG_VERSIONS` default in `test/pg-version-matrix.sh`.
## Error Handling Patterns
- `_safe()` variants (`sgp4_propagate_safe`, `observe_safe`, `star_observe_safe`) return NULL on error instead of raising exceptions. Use these for batch queries over potentially invalid data.

View File

@ -42,6 +42,9 @@ RUN su postgres -c "/usr/lib/postgresql/${PG_MAJOR}/bin/initdb -D /tmp/pgtest" &
make PG_CONFIG=${PG_CONFIG} installcheck && \
su postgres -c "/usr/lib/postgresql/${PG_MAJOR}/bin/pg_ctl -D /tmp/pgtest stop"
# Standalone DE reader unit test (no PostgreSQL dependency)
RUN make test-de-reader
# Capture artifacts under /pg_orrery prefix for the next stage
RUN make PG_CONFIG=${PG_CONFIG} DESTDIR=/pg_orrery install

View File

@ -50,6 +50,23 @@ test-de-reader: test/test_de_reader.c src/de_reader.c src/de_reader.h
.PHONY: test-de-reader
# ── PG version test matrix ─────────────────────────────────
PG_TEST_VERSIONS ?= 14 15 16 17 18
test-matrix:
PG_VERSIONS="$(PG_TEST_VERSIONS)" bash test/pg-version-matrix.sh
test-pg%:
PG_VERSIONS="$*" bash test/pg-version-matrix.sh
test-matrix-clean:
rm -rf test/matrix-logs
@for v in $(PG_TEST_VERSIONS); do \
docker rmi "pg_orrery-test:pg$$v" 2>/dev/null || true; \
done
.PHONY: test-matrix test-matrix-clean
# ── Docker packaging ────────────────────────────────────────
REGISTRY ?= git.supported.systems/warehack.ing
IMAGE ?= pg_orrery

View File

@ -125,14 +125,15 @@ SELECT 'pork_chop_mini' AS test,
arr_date::date AS arr,
round(lambert_c3(3, 4, dep_date, arr_date)::numeric, 1) AS c3
FROM generate_series('2026-04-01'::timestamptz, '2026-06-01'::timestamptz, interval '30 days') dep_date
CROSS JOIN generate_series('2027-01-01'::timestamptz, '2027-03-01'::timestamptz, interval '30 days') arr_date;
CROSS JOIN generate_series('2027-01-01'::timestamptz, '2027-03-01'::timestamptz, interval '30 days') arr_date
ORDER BY dep_date, arr_date;
test | dep | arr | c3
----------------+------------+------------+-------
pork_chop_mini | 04-01-2026 | 01-01-2027 | 287.5
pork_chop_mini | 05-01-2026 | 01-01-2027 | 215.8
pork_chop_mini | 05-31-2026 | 01-01-2027 | 203.2
pork_chop_mini | 04-01-2026 | 01-31-2027 | 353.9
pork_chop_mini | 05-01-2026 | 01-01-2027 | 215.8
pork_chop_mini | 05-01-2026 | 01-31-2027 | 215.2
pork_chop_mini | 05-31-2026 | 01-01-2027 | 203.2
pork_chop_mini | 05-31-2026 | 01-31-2027 | 172.0
(6 rows)

92
test/pg-version-matrix.sh Executable file
View File

@ -0,0 +1,92 @@
#!/usr/bin/env bash
# test/pg-version-matrix.sh — Test pg_orrery across PostgreSQL versions
#
# Uses the Dockerfile builder stage as the test engine: compile, install,
# initdb, installcheck, and standalone DE reader test for each PG version.
#
# Usage:
# ./test/pg-version-matrix.sh # Test PG 14-18
# PG_VERSIONS="16 17" ./test/pg-version-matrix.sh # Subset
# ./test/pg-version-matrix.sh --no-cache # Force fresh builds
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
LOG_DIR="$SCRIPT_DIR/matrix-logs"
PG_VERSIONS="${PG_VERSIONS:-14 15 16 17 18}"
# Parse flags
DOCKER_EXTRA_ARGS=()
for arg in "$@"; do
case "$arg" in
--no-cache) DOCKER_EXTRA_ARGS+=(--no-cache) ;;
*) echo "Unknown flag: $arg" >&2; exit 1 ;;
esac
done
mkdir -p "$LOG_DIR"
# Track results: "version status seconds"
declare -a RESULTS=()
FAILURES=0
echo "pg_orrery version matrix"
echo "========================"
echo "Versions: $PG_VERSIONS"
echo "Logs: $LOG_DIR/"
echo ""
for ver in $PG_VERSIONS; do
log="$LOG_DIR/pg${ver}.log"
printf "Testing PG %-4s... " "$ver"
start=$(date +%s)
if docker build \
--build-arg PG_MAJOR="$ver" \
--target builder \
"${DOCKER_EXTRA_ARGS[@]}" \
-t "pg_orrery-test:pg${ver}" \
"$PROJECT_DIR" \
> "$log" 2>&1; then
end=$(date +%s)
elapsed=$((end - start))
printf "PASS (%ds)\n" "$elapsed"
RESULTS+=("$ver PASS $elapsed")
else
end=$(date +%s)
elapsed=$((end - start))
printf "FAIL (%ds) -> %s\n" "$elapsed" "$log"
RESULTS+=("$ver FAIL $elapsed")
FAILURES=$((FAILURES + 1))
fi
done
# Summary table
echo ""
echo "Summary"
echo "-------"
printf "%-6s %-6s %s\n" "PG" "Result" "Time"
printf "%-6s %-6s %s\n" "---" "------" "----"
for entry in "${RESULTS[@]}"; do
read -r ver status secs <<< "$entry"
printf "%-6s %-6s %ds\n" "$ver" "$status" "$secs"
done
# Cleanup test images
echo ""
echo "Cleaning test images..."
for ver in $PG_VERSIONS; do
docker rmi "pg_orrery-test:pg${ver}" >/dev/null 2>&1 || true
done
if [ "$FAILURES" -gt 0 ]; then
echo ""
echo "$FAILURES version(s) failed. Check logs in $LOG_DIR/"
exit 1
fi
echo ""
echo "All versions passed."

View File

@ -98,4 +98,5 @@ SELECT 'pork_chop_mini' AS test,
arr_date::date AS arr,
round(lambert_c3(3, 4, dep_date, arr_date)::numeric, 1) AS c3
FROM generate_series('2026-04-01'::timestamptz, '2026-06-01'::timestamptz, interval '30 days') dep_date
CROSS JOIN generate_series('2027-01-01'::timestamptz, '2027-03-01'::timestamptz, interval '30 days') arr_date;
CROSS JOIN generate_series('2027-01-01'::timestamptz, '2027-03-01'::timestamptz, interval '30 days') arr_date
ORDER BY dep_date, arr_date;