feat(test): add multi-engine integration matrix
This commit is contained in:
116
Makefile
116
Makefile
@@ -10,6 +10,26 @@
|
||||
# network; installs dev dependencies first if missing)
|
||||
# ...then develop on develop and commit there as usual.
|
||||
#
|
||||
# Multi-engine regression net (run it whenever Model/Database change):
|
||||
# make integration full matrix: brings the test services up,
|
||||
# runs the Integration suite on sqlite, mysql
|
||||
# and pgsql, then takes the services down even
|
||||
# if a leg fails or you interrupt it
|
||||
# make integration-engine ENGINE=mysql
|
||||
# single leg (assumes services are already up);
|
||||
# the fastest inner loop while iterating
|
||||
# make db-up / make db-down manage the rootless podman test containers
|
||||
# (mariadb:11, postgres:16-alpine); idempotent
|
||||
#
|
||||
# Hard port constraint: the services must bind default ports 3306/5432 on
|
||||
# localhost because Database.php's DSN has no port field; stop any local
|
||||
# server on those ports first (db-up aborts naming the port).
|
||||
#
|
||||
# Known red: ModelFilterTest::orderByRandReturnsEveryRow fails on the
|
||||
# sqlite and pgsql legs BY DESIGN until the core RAND translation is fixed
|
||||
# (finding F1 in openspec/changes/*/add-multi-engine-model-tests/findings.md);
|
||||
# the mysql leg passes it and everything else.
|
||||
#
|
||||
# Publishing ritual (human only — AI agents must never run it; it commits):
|
||||
# 1. make publish MSG="feat: ..." (MSG optional, defaults to "sync: <date>")
|
||||
# 2. review the new master commit (git log -1 refs/heads/master); if the
|
||||
@@ -35,10 +55,13 @@
|
||||
|
||||
SHELL := /bin/bash
|
||||
|
||||
.PHONY: test publish
|
||||
.PHONY: test publish db-up db-up-mariadb db-up-postgres db-down integration integration-engine
|
||||
|
||||
PHPUNIT := vendor/bin/phpunit
|
||||
|
||||
# Engine for single-leg integration runs (services must already be up).
|
||||
ENGINE ?= sqlite
|
||||
|
||||
# --- publish configuration -------------------------------------------------
|
||||
WHITELIST := src config.php index.php autoload.php .htaccess readme.org
|
||||
BLACKLIST :=
|
||||
@@ -49,11 +72,22 @@ MASTER_REF := refs/heads/$(MASTER_BRANCH)
|
||||
LAST_SYNC_TAG := last-sync
|
||||
MSG ?= sync: $(shell date +%Y-%m-%d)
|
||||
|
||||
test: $(PHPUNIT)
|
||||
./$(PHPUNIT)
|
||||
# --- integration database services (podman rootless) -----------------------
|
||||
# Default ports are non-negotiable: Database.php's DSN carries no port field,
|
||||
# so the engines must answer on 3306/5432. If a local server owns those
|
||||
# ports, stop it before `make db-up`.
|
||||
MARIADB_NAME := duckbrain-test-mariadb
|
||||
POSTGRES_NAME := duckbrain-test-postgres
|
||||
MARIADB_IMAGE := docker.io/library/mariadb:11
|
||||
POSTGRES_IMAGE := docker.io/library/postgres:16-alpine
|
||||
TEST_DB_NAME := duckbrain_test
|
||||
TEST_DB_USER := duckbrain
|
||||
TEST_DB_PASS := duckbrain
|
||||
DB_READY_SECS := 240
|
||||
|
||||
$(PHPUNIT): composer.json composer.lock
|
||||
composer install --no-interaction
|
||||
test:
|
||||
@test -x $(PHPUNIT) || composer install --no-interaction
|
||||
./$(PHPUNIT) --testsuite Unit
|
||||
|
||||
publish:
|
||||
@if [ "$$(git branch --show-current)" != "$(DEVELOP_BRANCH)" ]; then \
|
||||
@@ -94,3 +128,75 @@ publish:
|
||||
@echo "root entries left behind in $(DEVELOP_BRANCH) (add to WHITELIST if they belong to the artifact):"; \
|
||||
comm -23 <(git ls-tree --name-only $(DEVELOP_REF) | sort) \
|
||||
<(for x in $(WHITELIST) $(BLACKLIST); do echo $$x; done | sort) | sed 's/^/ - /'
|
||||
|
||||
db-up: db-up-mariadb db-up-postgres
|
||||
|
||||
db-up-mariadb:
|
||||
@if podman ps -a --format '{{.Names}}' | grep -qx $(MARIADB_NAME); then \
|
||||
echo "reusing $(MARIADB_NAME)"; podman start $(MARIADB_NAME) >/dev/null; \
|
||||
else \
|
||||
(echo > /dev/tcp/127.0.0.1/3306) 2>/dev/null && { \
|
||||
echo "ABORT: port 3306 already in use; stop the local MySQL/MariaDB server or free the port before db-up"; exit 1; } || true; \
|
||||
podman run -d --name $(MARIADB_NAME) \
|
||||
-e MARIADB_ROOT_PASSWORD=$(TEST_DB_PASS) \
|
||||
-e MARIADB_DATABASE=$(TEST_DB_NAME) \
|
||||
-e MARIADB_USER=$(TEST_DB_USER) \
|
||||
-e MARIADB_PASSWORD=$(TEST_DB_PASS) \
|
||||
-p 127.0.0.1:3306:3306 $(MARIADB_IMAGE) >/dev/null; \
|
||||
fi
|
||||
@echo "waiting for $(MARIADB_NAME) to accept connections..."; \
|
||||
for i in $$(seq 1 $(DB_READY_SECS)); do \
|
||||
podman exec $(MARIADB_NAME) mariadb-admin ping -h 127.0.0.1 -u $(TEST_DB_USER) -p$(TEST_DB_PASS) --silent >/dev/null 2>&1 \
|
||||
&& echo "$(MARIADB_NAME) ready" && exit 0; \
|
||||
sleep 2; \
|
||||
done; \
|
||||
echo "TIMEOUT: $(MARIADB_NAME) never became ready; inspect: podman logs $(MARIADB_NAME)"; exit 1
|
||||
|
||||
db-up-postgres:
|
||||
@if podman ps -a --format '{{.Names}}' | grep -qx $(POSTGRES_NAME); then \
|
||||
echo "reusing $(POSTGRES_NAME)"; podman start $(POSTGRES_NAME) >/dev/null; \
|
||||
else \
|
||||
(echo > /dev/tcp/127.0.0.1/5432) 2>/dev/null && { \
|
||||
echo "ABORT: port 5432 already in use; stop the local PostgreSQL server or free the port before db-up"; exit 1; } || true; \
|
||||
podman run -d --name $(POSTGRES_NAME) \
|
||||
-e POSTGRES_DB=$(TEST_DB_NAME) \
|
||||
-e POSTGRES_USER=$(TEST_DB_USER) \
|
||||
-e POSTGRES_PASSWORD=$(TEST_DB_PASS) \
|
||||
-p 127.0.0.1:5432:5432 $(POSTGRES_IMAGE) >/dev/null; \
|
||||
fi
|
||||
@echo "waiting for $(POSTGRES_NAME) to accept connections..."; \
|
||||
for i in $$(seq 1 $(DB_READY_SECS)); do \
|
||||
podman exec $(POSTGRES_NAME) pg_isready -U $(TEST_DB_USER) -d $(TEST_DB_NAME) >/dev/null 2>&1 \
|
||||
&& echo "$(POSTGRES_NAME) ready" && exit 0; \
|
||||
sleep 2; \
|
||||
done; \
|
||||
echo "TIMEOUT: $(POSTGRES_NAME) never became ready; inspect: podman logs $(POSTGRES_NAME)"; exit 1
|
||||
|
||||
db-down:
|
||||
@for c in $(MARIADB_NAME) $(POSTGRES_NAME); do \
|
||||
if podman ps -a --format '{{.Names}}' | grep -qx $$c; then \
|
||||
podman stop $$c >/dev/null 2>&1; podman rm $$c >/dev/null 2>&1; echo "removed $$c"; \
|
||||
else \
|
||||
echo "$$c not present"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
integration:
|
||||
@bash -c ' \
|
||||
trap "$(MAKE) db-down" EXIT; \
|
||||
trap "exit 130" INT; \
|
||||
trap "exit 143" TERM; \
|
||||
$(MAKE) --no-print-directory db-up || exit 1; \
|
||||
rc=0; \
|
||||
for e in sqlite mysql pgsql; do \
|
||||
echo "============ leg: $$e ============"; \
|
||||
$(MAKE) --no-print-directory integration-engine ENGINE=$$e || rc=1; \
|
||||
done; \
|
||||
echo "=================================="; \
|
||||
if [ $$rc -eq 0 ]; then echo "integration: all legs green"; else echo "integration: at least one leg FAILED (rc=1)"; fi; \
|
||||
exit $$rc \
|
||||
'
|
||||
|
||||
integration-engine:
|
||||
@test -x $(PHPUNIT) || composer install --no-interaction
|
||||
DUCKBRAIN_TEST_DB=$(ENGINE) ./$(PHPUNIT) --testsuite Integration --cache-directory .phpunit.cache/$(ENGINE)
|
||||
|
||||
Reference in New Issue
Block a user