diff --git a/ROADMAP.md b/ROADMAP.md index 2e132a7..bba49b4 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -432,6 +432,14 @@ charges. Fix as part of 1.1. table and the packer so 3.2 has somewhere to land. - `[ ]` 5.7 — Commercial rules duplicated between `FAIXAS` (JS) and `TIERS` (Python) `(F26)`. `test_pricing` guards parity; generate one from the other instead. +- `[ ]` 5.11 — Nothing tests the schema against an empty database. The 4.1 indexes + were added next to the existing one, which sits before the tables they name, so + `CREATE INDEX ... ON dtf_local.order_files` ran before that table existed. Every + local run passed because the volume already had the tables; CI caught it on its + clean volume. A migration is only really exercised from nothing, so the + integration job should run `down -v` before `up` — or a dedicated step should + apply `schema.sql` twice to a fresh database, proving both a first install and + a re-run. - `[ ]` 5.10 — The browser suites do not run in CI. Chrome runs in the runner container and can only reach the stack through ports published on the host, which is a different network namespace when the runner is itself a container. The API, diff --git a/local/schema.sql b/local/schema.sql index 6ef21c3..a4414a5 100644 --- a/local/schema.sql +++ b/local/schema.sql @@ -68,6 +68,22 @@ CREATE TABLE IF NOT EXISTS dtf_local.security_events ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, event text NOT NULL, details jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); +ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS expires_at timestamptz; +ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS purged_at timestamptz; +ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scan_state text NOT NULL DEFAULT 'pending'; +ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scan_reason text; +ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scanned_at timestamptz; +ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scan_after timestamptz NOT NULL DEFAULT now(); +UPDATE dtf_local.uploads SET expires_at=created_at + interval '30 days' WHERE expires_at IS NULL; +ALTER TABLE dtf_local.uploads ALTER COLUMN expires_at SET DEFAULT now() + interval '30 days'; +CREATE TABLE IF NOT EXISTS dtf_local.order_files ( + id uuid PRIMARY KEY, order_id uuid NOT NULL REFERENCES dtf_local.orders(id), + upload_id uuid NOT NULL REFERENCES dtf_local.uploads(id), item_index integer NOT NULL, + kind text NOT NULL CHECK(kind IN ('final','correction')), active boolean NOT NULL DEFAULT true, + note text NOT NULL, created_by text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), + UNIQUE(order_id,upload_id,kind) +); + CREATE INDEX IF NOT EXISTS uploads_owner ON dtf_local.uploads(owner); -- Indexes follow the queries the application actually issues. Only these; every @@ -102,18 +118,3 @@ CREATE INDEX IF NOT EXISTS operator_sessions_username ON dtf_local.operator_sess -- security_status reads recent events; the worker prunes old ones by age. CREATE INDEX IF NOT EXISTS security_events_created ON dtf_local.security_events(created_at); -ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS expires_at timestamptz; -ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS purged_at timestamptz; -ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scan_state text NOT NULL DEFAULT 'pending'; -ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scan_reason text; -ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scanned_at timestamptz; -ALTER TABLE dtf_local.uploads ADD COLUMN IF NOT EXISTS scan_after timestamptz NOT NULL DEFAULT now(); -UPDATE dtf_local.uploads SET expires_at=created_at + interval '30 days' WHERE expires_at IS NULL; -ALTER TABLE dtf_local.uploads ALTER COLUMN expires_at SET DEFAULT now() + interval '30 days'; -CREATE TABLE IF NOT EXISTS dtf_local.order_files ( - id uuid PRIMARY KEY, order_id uuid NOT NULL REFERENCES dtf_local.orders(id), - upload_id uuid NOT NULL REFERENCES dtf_local.uploads(id), item_index integer NOT NULL, - kind text NOT NULL CHECK(kind IN ('final','correction')), active boolean NOT NULL DEFAULT true, - note text NOT NULL, created_by text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(order_id,upload_id,kind) -);