d2e5274f8bce5ec23782431780e19e1e02b98376
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f7d85736dd |
ci: fold the Tests workflow into SonarQube Analysis and delete it (#123)
tests.yml and sonarqube.yml had identical triggers and ran the same tests. Where tests.yml ran test:unit:json and playwright test, sonarqube.yml ran test:unit:cov and test:e2e:cov: the same two suites, differing only in reporter and instrumentation. Every pull request therefore installed dependencies twice, migrated twice, built and started the backend twice, installed a Playwright browser twice and ran the whole end-to-end suite twice. With one runner the second copy did not run in parallel, it queued. Two things in tests.yml were not duplicates and are folded in rather than dropped. The summarize steps, which render pass and fail counts and name the failing tests, where sonarqube.yml offered only raw jest and Playwright output. And the continue-on-error plus fail-at-the-end pattern that lets those summaries render on a failing run at all: sonarqube.yml used to abort at the first failing step, so a broken unit test meant no end-to-end results, no coverage and no scan, which is one fact per run when a run costs many minutes. The trade-off is fast feedback. A broken unit test used to fail tests.yml in a couple of minutes; now it is reported when the whole pipeline finishes. That is worth it with one runner, where the fast job was queued behind the slow one anyway, and where a single run reporting unit results, end-to-end results, coverage and the scan together beats two runs each reporting a fragment. Worth revisiting if the runner count changes. Two details that would each have silently broken something: The coverage scripts do not emit the JSON the summarizers read - test:unit:cov has no --json, and test:e2e:cov does not set the JSON reporter. Both are appended at the step rather than baked into package.json, since coverage and a results file are only wanted together in this one place. The Playwright step uses --reporter=list,json so the log still names the failing test rather than only writing a file. The "Backend log" step was keyed on failure(). With continue-on-error the job is not in a failed state when it runs, so failure() would never fire and the log explaining an end-to-end failure would have gone unprinted exactly when it was wanted. It is now keyed on the step outcome. The header comment also records something the old comments obscured: the integration suite runs here on every push and pull request, despite backend-integration.yml describing it as manual. That quarantine only ever applied to tests.yml. Verification, run rather than assumed: both suites executed locally with the exact commands the workflow now uses. The unit run wrote unit-results.json alongside its coverage and summarize-jest.js rendered 199 passed from it. The end-to-end run wrote playwright-results.json and summarize-playwright.js rendered 100 passed, 3 failed, 2 skipped and named all three failures - a failing run, which is the case if: always() exists for. The three failures are pre-existing and fail identically on main. Parsing the remaining workflows confirms sonarqube.yml declares one job carrying both new step ids, lint.yml and backend-integration.yml are untouched, and tests.yml is gone. The two results files are added to .gitignore. CI never committed them, but the commands are now documented and runnable locally, which makes an accidental commit a matter of time. Closes #123 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
40b483fc30 |
feat(scripts): a PowerShell script to start the local environment (#125)
Bringing the app up locally was seven or eight commands in a particular order: a Postgres container on a port that is not blocked, the six environment variables the backend refuses to boot without, migrations, a TypeScript build, the backend, then Vite. None of it hard, all of it tedious, and documented nowhere outside CI workflows written for a Linux runner. scripts/start-local.ps1 does the sequence. It reuses an existing container rather than recreating one, skips npm install when node_modules is already there, leaves alone anything already listening on a port it wanted, and waits on pg_isready and a 200 from /api/config rather than sleeping a fixed number of seconds. -Fresh recreates the database, -Stop tears everything down by the process ids it recorded rather than by port, since killing by port would also kill whatever else happened to be listening. Two things this got wrong first time round, both found by running it rather than by reading it. $ErrorActionPreference = 'Stop' does not stop a PowerShell script when a native executable exits non-zero, only when a cmdlet throws. Every command here is node, npm or docker, so the first run printed a stack trace from a failed migration, carried straight on, and reported a healthy stack sitting on a database with no tables in it. That is the worst kind of wrong: a green summary over a broken environment. Native calls now go through Invoke-Checked, which tests $LASTEXITCODE and throws. The migration failed because node on the PATH was v18.16.1. node-pg-migrate pulls in an lru-cache that calls diagnostics_channel.tracingChannel, which does not exist before Node 20, and the failure surfaces as "(0 , U.tracingChannel) is not a function" from a minified file - which says nothing whatsoever about Node versions. The script now checks the major version first and says what to do about it, so the confusing crash becomes one clear line before anything else runs. The port default is 55500 rather than anything near 55432, which is reserved by Hyper-V on this machine. Docker's message when it cannot bind a reserved port does not mention reservations, so the failure path names the likely cause and prints the netsh command that lists the reserved ranges. Verification, all observed rather than assumed: the version guard was made to fire on Node 18 and produced the intended message. A -Fresh run on Node 24 applied all six migrations, and psql then showed thirteen tables where the broken run had none. /api/config and the storefront both answer 200. -Stop stopped both tracked processes and the container. A second run with the dev server already up detected it and left it alone rather than failing. .local/ holds the logs, pids and uploads, and is gitignored. Closes #125 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
332c1e7cd0 |
feat(ci): import test coverage into SonarQube (#61)
SonarQube reported 0% coverage for 78 unit, 134 integration and 83 end-to-end tests, so the coverage-on-new-code gate — the most useful thing SonarQube offers a project this size — has been failing permanently while looking configured. It now reports 69.6%, verified by a real scan. Backend coverage comes from both suites, written to separate directories because jest writes coverage/lcov.info by default and the second run would silently overwrite the first. Both are needed rather than just the fast one: the unit suite alone reports 11%, because everything in src/routes is exercised by the integration suite. That suite is manual-only after hanging for 3h12m post-run, so it runs here with --forceExit and the job carries a hard timeout; jest confirmed during testing that it would otherwise have hung. The frontend had no unit tests at all, so its coverage comes from Playwright driving an istanbul-instrumented dev server, collected per test by an auto-fixture and merged with nyc. The 17 specs now import from a local fixtures module that re-exports @playwright/test, which is what lets the fixture attach without touching each test body. Instrumentation is gated behind COVERAGE=true and loaded by dynamic import, since vite-plugin-istanbul is ESM-only while vite.config.ts evaluates as CommonJS. Both directions were checked rather than assumed: a normal build contains no instrumentation, and the dev server instruments nested modules as well as top-level ones — the first attempt used an include glob of src/* which would have silently missed everything under src/admin and src/cart. coverage:report fails when nothing was collected instead of writing an empty report, and that guard was fired deliberately to confirm it works. This project has been bitten twice by tools succeeding while measuring nothing — SonarQube skipping the whole frontend and still exiting EXECUTION SUCCESS in #67, and an ESLint matcher silently matching no files during #60 — and coverage has exactly that shape: an uninstrumented dev server lets every test pass while gathering nothing, and the 0% that follows reads as lost coverage rather than broken collection. Worth knowing when reading the numbers: end-to-end coverage flatters. Istanbul marks a line covered when the browser ran it, so a component rendered during a test counts as covered with nothing asserting anything about it. Recorded in the design doc and the project context rather than left to be discovered. Also declares sonar.tests so test files are analysed under the test rule set rather than as production code. Closes #61 |
||
|
|
ae8f0eb12c |
fix(ci): make SonarQube actually analyse the frontend (#67)
SonarQube was analysing none of frontend/src. All 34 files were indexed and reported ncloc 0, so the quality gate had been measuring roughly a third of the codebase while appearing to cover it. The scanner log gives the cause: frontend/tsconfig.json sets "moduleResolution": "bundler", which is correct for Vite, and the TypeScript bundled with SonarQube 9.9 predates 5.0 and rejects it. Building the frontend program throws, every frontend file is dropped, and the scan still exits EXECUTION SUCCESS — which is why a green job hid this indefinitely. Adds frontend/tsconfig.sonar.json, an analysis-only mirror that differs only in using "node", and points the scan at it via sonar.typescript.tsconfigPaths. The app's own tsconfig is deliberately untouched: "bundler" is right for the build, and changing it to satisfy an old analyser would let the tool dictate the build. The mirror cannot use `extends` — the old compiler validates the base file while reading it, so the error just moves to pointing at tsconfig.json. Verified locally against a scratch project: 59/59 files analysed, no skips. Analysed lines go from 2,069 to 5,481, code smells from 2 to 14, security hotspots from 3 to 4, and technical debt from 21 to 85 minutes. The frontend had been hiding twelve code smells and a hotspot, which is part of why the React problems behind #60 and #62 had to be found by hand. A standalone copy drifts, and drift here does not fail anything — it silently returns to skipping the frontend while reporting success. scripts/check-sonar-tsconfig.js compares the two and fails when they diverge in anything but moduleResolution, and runs before the scan so the scan is never what discovers it. Confirmed it catches drift by introducing some. Moves scan settings into sonar-project.properties at the repo root so a local scan and the CI scan analyse the same thing, leaving only the host and token in secrets. Adds scripts/scan-local.sh, which runs the scanner in Docker because it needs Java 11+ and the dev machine has Java 8, and which defaults to a scratch project key: the server is Community edition with no branch analysis, so any scan overwrites the single main analysis of whichever key it is given. Closes #67 |
||
|
|
766358a9fe |
docs: design spec for categories and tags (#23)
Records the resolved requirements for issue #23: manual category tree (no rule engine), single category per item with descendant matching, central tag registry with hashed-then-overridable colours, AND semantics for multi-tag filtering, and a drawer-plus-chips storefront filter UI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
921022c658 |
Add backend unit/integration tests, Playwright e2e tests, README, cookie Secure fix
SonarQube Analysis / sonarqube (push) Successful in 4m20s
|
||
|
|
9be4986dd3 | Initial commit: redefined-designs storefront |