fix(scripts): verify_service_booking — general orphan-FK sweep + test port fix + scoped tags

Hardened after the first real clone-verify on odoo-westin:
- Cleanup now generates an orphan-delete for EVERY single-column FK from PROD's
  pg_constraint and applies it to the clone (was tax-tables-only). westin-v19 also
  has deleted-company (payslip_tags_table, account_account_res_company_rel) and
  deleted-journal (account_payment_method_line) orphans that broke the clone -u.
- run_odoo passes --http-port=0 --gevent-port=0 so --test-enable (which forces
  http_spawn even with --no-http in Odoo 19) doesn't die on 'Address already in use'.
- TEST_TAGS scoped to this feature's classes (the broad tag also runs pre-existing
  dashboard/wizard tests that fail in this prod-config runner, unrelated to this work).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-04 06:10:25 -04:00
parent 252716156c
commit 63694eccb1

View File

@@ -37,7 +37,11 @@ PGPW="${PGPW:-DevSecure2025!}"
PGUSER="${PGUSER:-odoo}"
MODULES="${MODULES:-fusion_tasks,fusion_claims}" # comma list for -u
TEST_TAGS="${TEST_TAGS:-/fusion_tasks,/fusion_claims}"
# Scope to THIS feature's test classes — the broad /fusion_claims tag also runs
# pre-existing dashboard/wizard tests that fail in this prod-config runner
# (CLAUDE.md fusion_repairs note: post_install trips on a pre-existing module),
# which is unrelated to this feature. Override TEST_TAGS to widen if desired.
TEST_TAGS="${TEST_TAGS:-/fusion_tasks:TestTaskTz,/fusion_claims:TestServiceRate,/fusion_claims:TestServiceBooking}"
MOD_DIRS=(fusion_tasks fusion_claims) # dirs to stage/deploy
BRANCH="${BRANCH:-claude/technician-service-booking}"
@@ -91,17 +95,21 @@ dexec -e PGPASSWORD="$PGPW" "$DBC" sh -c \
>>"$LOG" 2>&1
ok "Cloned."
# ----------------------------- 2. ORPHAN-TAX-FK CLEANUP (clone only) ---------
# westin-v19 has ~3300 orphaned tax m2m rows under validated FKs; a plain
# pg_dump|psql clone can't rebuild the validating FK over them -> Odoo fails to
# load the registry. Safe to delete ON THE CLONE only. (CLAUDE.md gotcha.)
c "Orphaned-tax-FK cleanup (clone only)"
psql_clone -c "DELETE FROM product_taxes_rel WHERE tax_id NOT IN (SELECT id FROM account_tax);" >>"$LOG" 2>&1 || true
psql_clone -c "DELETE FROM product_supplier_taxes_rel WHERE tax_id NOT IN (SELECT id FROM account_tax);" >>"$LOG" 2>&1 || true
# sweep any other %_rel table carrying a tax_id column
psql_clone -t -A -c "SELECT table_name FROM information_schema.columns WHERE column_name='tax_id' AND table_name LIKE '%\\_rel';" 2>/dev/null \
| while read -r t; do [[ -n "$t" ]] && psql_clone -c "DELETE FROM ${t} WHERE tax_id NOT IN (SELECT id FROM account_tax);" >>"$LOG" 2>&1 || true; done
ok "Orphan FKs cleared on clone."
# ----------------------------- 2. ORPHANED-FK CLEANUP (clone only) -----------
# westin-v19 has orphaned rows under VALIDATED FKs (deleted taxes, companies,
# journals, ...). A plain pg_dump|psql clone cannot rebuild a validating FK over
# orphans, so the clone is MISSING those FKs; Odoo's check_foreign_keys then
# re-adds them and fails (e.g. payslip_tags_table.res_company_id=3,
# account_payment_method_line.journal_id=35). Generate an orphan-delete for EVERY
# single-column FK that exists on PROD (read-only SELECT on prod) and apply it to
# the clone. The clone is a throwaway; prod is never modified.
# (CLAUDE.md orphan-FK gotcha, generalised beyond the tax tables.)
c "Orphaned-FK cleanup (clone only) — general sweep from prod's FK definitions"
FKSQL="/tmp/svcbook_fkclean_${STAMP}.sql"
printf '%s\n' '\set ON_ERROR_STOP off' > "$FKSQL"
dexec -e PGPASSWORD="$PGPW" "$DBC" psql -U "$PGUSER" -d "$PROD_DB" -t -A -c "SELECT format('DELETE FROM %I a WHERE a.%I IS NOT NULL AND NOT EXISTS (SELECT 1 FROM %I b WHERE b.%I = a.%I);', src.relname, srcatt.attname, tgt.relname, tgtatt.attname, srcatt.attname) FROM pg_constraint con JOIN pg_class src ON src.oid=con.conrelid JOIN pg_namespace ns ON ns.oid=src.relnamespace AND ns.nspname='public' JOIN pg_class tgt ON tgt.oid=con.confrelid JOIN pg_attribute srcatt ON srcatt.attrelid=con.conrelid AND srcatt.attnum=con.conkey[1] JOIN pg_attribute tgtatt ON tgtatt.attrelid=con.confrelid AND tgtatt.attnum=con.confkey[1] WHERE con.contype='f' AND array_length(con.conkey,1)=1;" >> "$FKSQL" 2>>"$LOG" || true
dexec -i -e PGPASSWORD="$PGPW" "$DBC" psql -U "$PGUSER" -d "$CLONE_DB" < "$FKSQL" >>"$LOG" 2>&1 || true
ok "Orphan FKs cleared on clone (general sweep, $(grep -c '^DELETE' "$FKSQL" 2>/dev/null || echo 0) FK relations)."
# ----------------------------- 3. STAGE MODULES (shadow) ---------------------
c "Stage modules into $STAGE (shadows prod, prod files untouched)"
@@ -114,9 +122,12 @@ ok "Staged: ${MOD_DIRS[*]}"
# --test-enable SILENTLY SKIPS without --workers 0; log_level=warn hides test
# output -> add --log-level=test. The EXIT CODE is authoritative.
run_odoo() { # $1 = extra args
# --test-enable forces http_spawn() even with --no-http (Odoo 19), so the test
# run binds 8069 (held by the live app) and dies with "Address already in use".
# --http-port=0 --gevent-port=0 makes it pick ephemeral ports. (CLAUDE.md gotcha.)
dexec "$APP" odoo -d "$CLONE_DB" \
--db_host db --db_port 5432 --db_user "$PGUSER" --db_password "$PGPW" \
--addons-path="$ADDONS_PATH" --stop-after-init --no-http $1
--addons-path="$ADDONS_PATH" --stop-after-init --no-http --http-port=0 --gevent-port=0 $1
}
c "Install/upgrade on clone (catches install/render errors)"