Summary
The E2E test environment is starting up with only requirements seeded — the 5 seeded test_cases (TC-001..TC-005) and their 3 requirement_test_case_links are missing from the database by the time the Traceability Matrix tests run. This causes the long-running flake on traceability.spec.ts:37 (seeded test case "TC-004" is visible in the matrix) to fail deterministically, not intermittently.
Reproduces consistently on PR #310 (both initial run and re-run) and the bug is not in #310's diff — #310 only adds the external_case_artifacts table and artifact upload endpoint, neither of which touches test_cases, requirement_test_case_links, seed loading, or the frontend. The same failure would reproduce against main.
Supersedes the pollution-theory hypothesis from #308 — that fix landed but did not address the actual root cause.
Evidence
Screenshot of the Traceability Matrix page from the failing test attachment (error-context.md on the TC-004 retries):
| Metric |
Observed |
Expected (from seed.sql) |
| Coverage Percentage |
0.0% |
~60% (3 of 5 reqs linked) |
| Covered Requirements |
0 / 8 |
3 / 5 |
| Uncovered Requirements |
8 |
2 |
| Orphan Test Cases |
2 (only E2E Test Case + E2E Edit TC Target ... (edited)) |
should also include TC-003, TC-005 (unlinked seeded cases) |
| Seeded TC-001..TC-005 visible? |
No — none of them present |
All 5 should appear |
| Requirements present? |
✅ All 5 seeded requirements visible (User Authentication, Dashboard Overview, Export Traceability Matrix, Role-Based Access Control, Performance Under Load) plus 3 from CRUD tests |
— |
Key observation: requirement seeding works; test-case seeding does not. Both INSERTs live in the same seed.sql file, which strongly suggests the INSERT INTO test_cases block is the failure point — and if it aborts mid-file, the subsequent INSERT INTO requirement_test_case_links block is also skipped, which exactly matches what we see (0 links).
Failing test:
1) [chromium] › tests/e2e/traceability.spec.ts:37:3 › Traceability Matrix › seeded test case "TC-004" is visible in the matrix
Error: expect(locator).toBeVisible() failed
Locator: getByText(/TC-004/i)
Expected: visible
Timeout: 10000ms
Error: element(s) not found
Relevant fixture: frontend/tests/e2e/fixtures/seed.sql (specifically the INSERT INTO test_cases block starting around line 63 and the links block around line 91).
Three hypotheses for triage
Hypothesis 1 — Schema mismatch in seed.sql (most likely)
The INSERT INTO test_cases block in seed.sql omits an external_id column, but the actual test_cases schema may require it (NOT NULL, unique, or referenced by the matrix UI as the visible ID). If the insert raises (NOT NULL violation, missing column, etc.), the SQL block aborts mid-file, skipping both the rest of the test_cases inserts and the subsequent requirement_test_case_links block.
This matches every observable: requirements (inserted first) succeed, test_cases (inserted second) silently fail, links (inserted third) never run.
Diagnostic step: Inspect the bgstm-test-db container logs from a failing CI run for any INSERT INTO test_cases ... ERROR. Or run psql -f frontend/tests/e2e/fixtures/seed.sql against a fresh test DB locally and look at exit code + stderr.
Hypothesis 2 — sample_data.py runs after seed.sql and overwrites with a different dataset, then something else clears the resulting rows
backend/app/db/sample_data.py defines its own TC-001..TC-004 with different UUIDs. If it runs as part of container startup after seed.sql, the ON CONFLICT (id) DO NOTHING in seed.sql is irrelevant because the IDs are different. If a later step (test fixture, migration, beforeAll) then truncates test_cases, all rows are wiped while the requirements (which sample_data.py may not insert) survive.
Diagnostic step: Check the docker-compose / backend startup sequence to confirm whether sample_data.py is invoked, and in what order relative to seed.sql.
Hypothesis 3 — A spec file's beforeAll truncates test_cases
A test in another spec (e.g., test_cases CRUD) may truncate the table to assert empty-state behavior, leaving the requirements untouched but the test_cases gone. This would also explain why the orphan list contains only the 2 CRUD-created E2E test cases — they were created after the truncation.
Diagnostic step: git grep -n -E "TRUNCATE|DELETE FROM test_cases" frontend/tests and the backend test fixtures.
Suggested investigation order
- DB container logs — fastest path to confirming or eliminating Hypothesis 1.
- Run
seed.sql locally against a fresh test DB and check stderr.
- If 1 and 2 are clean: trace startup order (Hypothesis 2) and grep for truncations (Hypothesis 3).
Acceptance criteria
Impact
Out of scope
- Refactoring the entire seed/fixture strategy (separate epic if needed)
- Migrating to a programmatic seed (Python script) vs. raw SQL — track separately if desired
- Fixing
sample_data.py if it's the culprit — handle in a follow-up if Hypothesis 2 is confirmed
References
Summary
The E2E test environment is starting up with only requirements seeded — the 5 seeded
test_cases(TC-001..TC-005) and their 3requirement_test_case_linksare missing from the database by the time the Traceability Matrix tests run. This causes the long-running flake ontraceability.spec.ts:37(seeded test case "TC-004" is visible in the matrix) to fail deterministically, not intermittently.Reproduces consistently on PR #310 (both initial run and re-run) and the bug is not in #310's diff — #310 only adds the
external_case_artifactstable and artifact upload endpoint, neither of which touchestest_cases,requirement_test_case_links, seed loading, or the frontend. The same failure would reproduce againstmain.Supersedes the pollution-theory hypothesis from #308 — that fix landed but did not address the actual root cause.
Evidence
Screenshot of the Traceability Matrix page from the failing test attachment (
error-context.mdon the TC-004 retries):E2E Test Case+E2E Edit TC Target ... (edited))User Authentication,Dashboard Overview,Export Traceability Matrix,Role-Based Access Control,Performance Under Load) plus 3 from CRUD testsKey observation: requirement seeding works; test-case seeding does not. Both INSERTs live in the same
seed.sqlfile, which strongly suggests theINSERT INTO test_casesblock is the failure point — and if it aborts mid-file, the subsequentINSERT INTO requirement_test_case_linksblock is also skipped, which exactly matches what we see (0 links).Failing test:
Relevant fixture:
frontend/tests/e2e/fixtures/seed.sql(specifically theINSERT INTO test_casesblock starting around line 63 and the links block around line 91).Three hypotheses for triage
Hypothesis 1 — Schema mismatch in seed.sql (most likely)
The
INSERT INTO test_casesblock inseed.sqlomits anexternal_idcolumn, but the actualtest_casesschema may require it (NOT NULL, unique, or referenced by the matrix UI as the visible ID). If the insert raises (NOT NULL violation, missing column, etc.), the SQL block aborts mid-file, skipping both the rest of the test_cases inserts and the subsequentrequirement_test_case_linksblock.This matches every observable: requirements (inserted first) succeed, test_cases (inserted second) silently fail, links (inserted third) never run.
Diagnostic step: Inspect the
bgstm-test-dbcontainer logs from a failing CI run for anyINSERT INTO test_cases ... ERROR. Or runpsql -f frontend/tests/e2e/fixtures/seed.sqlagainst a fresh test DB locally and look at exit code + stderr.Hypothesis 2 —
sample_data.pyruns afterseed.sqland overwrites with a different dataset, then something else clears the resulting rowsbackend/app/db/sample_data.pydefines its own TC-001..TC-004 with different UUIDs. If it runs as part of container startup afterseed.sql, theON CONFLICT (id) DO NOTHINGinseed.sqlis irrelevant because the IDs are different. If a later step (test fixture, migration, beforeAll) then truncatestest_cases, all rows are wiped while the requirements (whichsample_data.pymay not insert) survive.Diagnostic step: Check the docker-compose / backend startup sequence to confirm whether
sample_data.pyis invoked, and in what order relative toseed.sql.Hypothesis 3 — A spec file's
beforeAlltruncatestest_casesA test in another spec (e.g., test_cases CRUD) may truncate the table to assert empty-state behavior, leaving the requirements untouched but the test_cases gone. This would also explain why the orphan list contains only the 2 CRUD-created E2E test cases — they were created after the truncation.
Diagnostic step:
git grep -n -E "TRUNCATE|DELETE FROM test_cases" frontend/testsand the backend test fixtures.Suggested investigation order
seed.sqllocally against a fresh test DB and check stderr.Acceptance criteria
seed.sqlcorrection, but defer specifics until diagnosis)Impact
Out of scope
sample_data.pyif it's the culprit — handle in a follow-up if Hypothesis 2 is confirmedReferences
frontend/tests/e2e/traceability.spec.ts#L37frontend/tests/e2e/fixtures/seed.sqlbackend/app/db/sample_data.py