# SowAI Fiscal Service — dev workflow (tests run IN-CLUSTER on the sow-dev # k8s cluster, in the SAME `auto-tests` pod/Postgres sidecar the `auto` # backend's own suite uses — see `auto/Makefile`'s header comment for that # pod's shape). This repo syncs into its OWN tree (`/app/fiscal-svc`, never # touching `/app/backend`) and runs against its OWN database # (`fiscal_svc_test`, created on first use below, since the sidecar's # `POSTGRES_DB` only pre-creates `auto_test`) — but is serialized by the # SAME lock file (`/tmp/auto-k8s-test.lock`) as the auto's own `make # k8s-test`, on purpose (plan 2026-07-17-fiscal-svc-f2-servico.md, Task 1): # one pod, one Postgres sidecar, one shared lock across both repos' test # runs, for free — a `make k8s-test` here and one in `auto/` from the same # machine can never race each other in the pod. NS := autopecas-dev POD = $(shell kubectl -n $(NS) get pod -l app=auto-tests -o jsonpath='{.items[0].metadata.name}') FISCAL_SVC_TEST_DATABASE_URL := postgresql+asyncpg://postgres:postgres@localhost:5432/fiscal_svc_test .PHONY: k8s-sync k8s-deps k8s-test k8s-test-file k8s-shell k8s-status # Stream this repo into /app/fiscal-svc in the runner (excludes .venv/.git/ # caches). Also ensures `git` (this service's pyproject depends on # `sowai-fiscal` via git+https, EMENDA F1 -- `uv sync` needs the binary at # resolve time) and `psql`/`createdb` (the migration tests shell out to # them) are present in the runner container, and that the `fiscal_svc_test` # database itself exists on the Postgres sidecar. All three are lost # whenever the runner container restarts (none ship in the base image / # get created for us), so all three are (idempotently, fast when already # present/existing) redone on every sync -- same rationale as the auto's own # k8s-sync ensuring `psql`/WeasyPrint libs. # # The tar OVERLAYS (never removes) -- same reasoning as the auto's own # k8s-sync: the pod is SHARED across worktrees/branches (of EITHER repo), so # `src/`/`tests/`/`alembic/` are pruned before extracting, to avoid a ghost # file from a previous sync poisoning this run. k8s-sync: @echo "→ ensure git in $(POD)" @kubectl -n $(NS) exec $(POD) -c runner -- bash -c 'command -v git >/dev/null 2>&1 || (apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq git >/dev/null 2>&1)' 2>/dev/null @echo "→ ensure psql/createdb in $(POD)" @kubectl -n $(NS) exec $(POD) -c runner -- bash -c 'command -v psql >/dev/null 2>&1 || (apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq postgresql-client >/dev/null 2>&1)' 2>/dev/null @echo "→ ensure database fiscal_svc_test in $(POD)" @kubectl -n $(NS) exec $(POD) -c runner -- bash -c 'createdb -h localhost -U postgres fiscal_svc_test 2>/dev/null || true' @echo "→ prune stale src/tests/alembic in $(POD) (o tar sobrepõe, não remove)" @kubectl -n $(NS) exec $(POD) -c runner -- bash -c 'mkdir -p /app/fiscal-svc && rm -rf /app/fiscal-svc/src /app/fiscal-svc/tests /app/fiscal-svc/alembic' 2>/dev/null @echo "→ sync repo into $(POD)" @tar czf - --exclude='.venv' --exclude='.git' --exclude='__pycache__' --exclude='*.pyc' --exclude='.pytest_cache' -C . . 2>/dev/null \ | kubectl -n $(NS) exec -i $(POD) -c runner -- tar xzf - -C /app/fiscal-svc 2>/dev/null @echo "✅ synced" # Re-resolve deps in the pod (run after changing pyproject/uv.lock). k8s-deps: @kubectl -n $(NS) exec $(POD) -c runner -- bash -c 'cd /app/fiscal-svc && uv sync' 2>&1 | grep -v xattr # --------------------------------------------------------------------------- # SERIALIZAÇÃO DO POD (lock local): MESMO arquivo de lock do `auto/Makefile`, # de propósito (ver o comentário do topo). O `pkill -f pytest` abaixo é # seguro precisamente PORQUE o lock é compartilhado -- por construção, nenhum # pytest legítimo (deste repo OU do auto) pode estar rodando quando esta # seção crítica começa, já que ambos os `make k8s-test` respeitam o mesmo # arquivo antes de disparar pytest. # --------------------------------------------------------------------------- K8S_TEST_LOCK := /tmp/auto-k8s-test.lock define K8S_LOCKED_RUN @bash -c 'while ! mkdir "$(K8S_TEST_LOCK)" 2>/dev/null; do \ p=$$(cat "$(K8S_TEST_LOCK)/pid" 2>/dev/null); \ if [ -n "$$p" ] && ! kill -0 "$$p" 2>/dev/null; then \ echo "🔓 lock órfão (pid $$p morto) — assumindo"; rm -rf "$(K8S_TEST_LOCK)"; continue; \ fi; \ echo "⏳ pod de testes em uso (pid $${p:-?}) — aguardando 20s..."; sleep 20; \ done; \ echo $$$$ > "$(K8S_TEST_LOCK)/pid"; \ trap "rm -rf $(K8S_TEST_LOCK)" EXIT INT TERM; \ $(MAKE) k8s-sync && \ kubectl -n $(NS) exec $(POD) -c runner -- bash -c "pkill -f [p]ytest 2>/dev/null; true" && \ kubectl -n $(NS) exec $(POD) -c runner -- bash -c "cd /app/fiscal-svc && TEST_DATABASE_URL=$(FISCAL_SVC_TEST_DATABASE_URL) uv run pytest -q $(1)" 2>&1 | grep -v xattr' endef # Full suite in-cluster (serializada pelo lock). NOTE: `TEST_DATABASE_URL` # is passed explicitly on the pytest invocation above, NOT left to the # container's own env -- the runner container's env already carries a # `TEST_DATABASE_URL` pointing at the auto's `auto_test` database (baked # into the shared pod spec, `k8s/test-runner.yaml` in the auto repo); without # this override this service's suite would silently run against the WRONG # database (or a database it has no schema in). k8s-test: $(call K8S_LOCKED_RUN,) # Single file/selector (serializada pelo lock): make k8s-test-file file=tests/test_health.py k8s-test-file: $(call K8S_LOCKED_RUN,$(file)) k8s-shell: @kubectl -n $(NS) exec -it $(POD) -c runner -- bash k8s-status: @kubectl -n $(NS) get pods,svc