Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9236aae
feat(test): replace the two bash test runners with one cross-platform…
cevheri Sep 15, 2026
72247fd
fix(test): make the suite runnable on Windows and macOS, not just on …
cevheri Sep 15, 2026
6d6101b
test(components): make a failing poll cheap, and one vacuous test real
cevheri Sep 15, 2026
cc6a06e
fix(windows): close what the tests measured on windows-latest, includ…
cevheri Sep 15, 2026
4592cc3
test: wait on the fact, not on the clock, in the tests a busy machine…
cevheri Sep 15, 2026
68e4141
docs: state the counts and timings this branch measures
cevheri Sep 15, 2026
906ffe2
test: close the three races CI found, and ask each platform what it c…
cevheri Sep 15, 2026
6f40dfa
test: make the BRE translation's escape set complete on its own terms
cevheri Sep 15, 2026
b351ef3
fix(sqlite): release the file when connect() fails after opening it
cevheri Sep 15, 2026
ff28af9
fix(test): close the gaps peer review found in the runner
cevheri Sep 15, 2026
8691c11
docs: correct the counts peer review checked against the code
cevheri Sep 15, 2026
ecb60f6
test(distribution-check): stop failing a reachable pin on a slow machine
cevheri Sep 15, 2026
a58fe66
fix(sqlite): drop the handle reference even when close(true) raises
cevheri Sep 15, 2026
fb9f53f
ci: keep the release validation on the same Helm and Node as the test…
cevheri Sep 15, 2026
15f1f40
fix(test): close what an external review of the runner found
cevheri Sep 15, 2026
c584e78
fix(test): resolve selectors in real-path space, so two spellings of …
cevheri Sep 15, 2026
3eab7a2
docs: counts after rebasing onto #817
cevheri Sep 15, 2026
cb09dec
fix(test): leave the chart tests out where Helm is unusable, and requ…
cevheri Sep 15, 2026
47770cd
chore(test): name why the runner's worker awaits inside its loop
cevheri Sep 15, 2026
c0912da
fix(test): refuse a link under tests/, and resolve every selector in …
cevheri Sep 15, 2026
945b106
fix(test): take a file's verdict from its junit report, and end a run…
cevheri Sep 15, 2026
ad95f08
docs: the runner's verdict, signals and concurrency as measured
cevheri Sep 15, 2026
1c9dacf
Merge remote-tracking branch 'origin/main' into fix/cross-platform-te…
cevheri Sep 15, 2026
48eba24
fix(test): spell two paths from #839 POSIX-style, so Windows reads th…
cevheri Sep 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .claude/skills/cut-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ commit, treat it as blocking anyway.
(Run this same query after tagging and `Release Artifacts` joins the list: the tag points at the bump
commit, so it shares the SHA.)

`helm repo add bitnami` (seven workflow sites: `ci.yml` x2, `helm-release.yml` x3, `npm-publish.yml`,
`helm repo add bitnami` (eight workflow sites: `ci.yml` x3, `helm-release.yml` x3, `npm-publish.yml`,
`operator-release.yml`) fetches a 27 MB index with no retry and flakes with
`connection reset by peer`. Verify the repo really is reachable, then re-run only the failed job:

Expand All @@ -122,7 +122,7 @@ curl -sSL -o /dev/null -w '%{http_code}\n' https://charts.bitnami.com/bitnami/in
gh run rerun <run-id> --failed
```

Those seven sites do **not** all pin the same Helm CLI. Six run Helm 4.1.3; `helm-release.yml`'s
Those eight sites do **not** all pin the same Helm CLI. Seven run Helm 4.1.3; `helm-release.yml`'s
`lint-test` job stays on Helm 3.16 on purpose, because its two `ct install` runs are the only place
the chart is installed into a cluster and our users install with Helm 3. The split is enforced by
`tests/unit/helm-pin-matrix.test.ts` in the required test lane - do not unify the odd one out.
Expand Down Expand Up @@ -247,7 +247,7 @@ true on a tag ref.
| `gh release create --target <short-sha>` | Rejected ("target_commitish is invalid") - use `--target main` |
| A `release-artifacts` run reporting `failure` | The release may still have published fine; check `Verify assets and publish release` before assuming otherwise |
| Reusing a failed release's version after Snap published | Snap store revisions are immutable per version; bump the patch instead |
| Renaming or removing the `test:ci` script | `npm-publish.yml` validates with `bun run test:ci` (per-file process isolation via `tests/run-core.sh`), NOT `bun run test`. Losing that script breaks every release and every re-dispatch |
| Renaming or removing the `test` script | `npm-publish.yml` validates with `bun run test`, which is `bun tests/run-tests.ts` (one bun process per test file). Losing that script breaks every release and every re-dispatch |
| Recreating a draft after a failed run | The hand-written notes are gone with it. Keep the notes file in the scratchpad and re-apply with `gh release edit <version> --notes-file <f>` |
| A release that touches `packaging/`, the Dockerfile or the payload scripts | The chain builds channels you cannot see locally. Validate them locally first (tarball/npx/docker build+run, deb/rpm with the CI-pinned nfpm) - that local pass is what separated the clean one-attempt releases from the four-attempt one |

Expand Down
4 changes: 2 additions & 2 deletions .devin/wiki.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"author": "Maintainer"
},
{
"content": "Testing is multi-layered: unit tests (tests/unit/), API route handler tests (tests/api/), integration tests with mocked database drivers (tests/integration/), React hook tests (tests/hooks/), component tests with happy-dom (tests/components/), and Playwright E2E tests (e2e/). Component tests use isolated execution groups via tests/run-components.sh to prevent mock.module() cross-contamination. CI pipeline runs lint, typecheck, test, and build.",
"content": "Testing is multi-layered: unit tests (tests/unit/), API route handler tests (tests/api/), integration tests with mocked database drivers (tests/integration/), React hook tests (tests/hooks/), component tests with happy-dom (tests/components/), and Playwright E2E tests (e2e/). Every test file runs in its own bun process (tests/run-tests.ts, which `bun run test` invokes) to prevent mock.module() cross-contamination. CI pipeline runs lint, typecheck, test, and build.",
"author": "Maintainer"
},
{
Expand Down Expand Up @@ -158,7 +158,7 @@
},
{
"title": "Testing Strategy",
"purpose": "Document the comprehensive multi-layer testing strategy. Cover: test setup files (tests/setup.ts for env/localStorage mocks, tests/setup-dom.ts for happy-dom), test helpers (tests/helpers/ with mock-monaco, mock-next, mock-navigation, mock-provider, mock-fetch, mock-sonner, render-with-providers), test fixtures (tests/fixtures/ with connections, schemas, query-results, masking-configs), and the component test isolation script (tests/run-components.sh). Explain why component tests need isolation (bun:test mock.module cross-contamination)."
"purpose": "Document the comprehensive multi-layer testing strategy. Cover: test setup files (tests/setup.ts for env/localStorage mocks, tests/setup-dom.ts for happy-dom), test helpers (tests/helpers/ with mock-monaco, mock-next, mock-navigation, mock-provider, mock-fetch, mock-sonner, render-with-providers), test fixtures (tests/fixtures/ with connections, schemas, query-results, masking-configs), and the cross-platform test runner (tests/run-tests.ts plus tests/runner/). Explain why every test file gets its own bun process (bun:test mock.module is process-wide, with no undo)."
},
{
"title": "Unit & API Tests",
Expand Down
9 changes: 5 additions & 4 deletions .github/curated-issue-footer.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
Curated for Hacktoberfest 2026. Comment to claim the issue before you start so two people do not
work on the same change. A PR must reference the issue and include tests for executable changes;
see [CONTRIBUTING.md](https://github.com/libredb/libredb-studio/blob/main/CONTRIBUTING.md).
Run `bun run test`, never bare `bun test`, so component
tests use their isolated execution groups. The 100% line-coverage gate must stay green.
Run `bun run test`, never bare `bun test` over a directory: the runner gives each test file its own
process, and `bun test tests/api` shares one, where a mock set up by one file leaks into the next.
The 100% line-coverage gate must stay green.

**CI is the merge gate.** If you cannot run a command locally, list that command and the reason
under a `Testing` heading in your PR body; submit the PR, and a maintainer will approve the fork's
workflow run so CI can verify it. You do not need to withdraw correct work because a local tool
is unavailable.

If your sandbox can reach the npm registry, `npm install -g bun` is another way to install Bun.
Helm is only needed for the chart tests in the test suite. The repository's devcontainer provides
Bun and Helm and installs the JavaScript and chart dependencies automatically.
Helm is only needed to run the chart tests: without it `bun run test` leaves those files out and names them, and CI runs them.
The repository's devcontainer provides Bun and Helm and installs the JavaScript and chart dependencies automatically.
90 changes: 89 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,17 @@ jobs:
- name: Build chart dependencies
run: helm dependency build charts/libredb-studio --skip-refresh

- name: Run merged test coverage (core + components)
- name: Run the test suite with coverage
# tests/run-tests.ts runs every test file in its own bun process, several
# at a time, and merges the per-file lcov reports. It is the same command
# a contributor runs (`bun run test`) with coverage turned on, which is
# the point: the gate and the documented command cannot drift apart.
run: bun run test:coverage
env:
# Optional on a contributor's machine, mandatory here: this job installs Helm and
# builds the chart dependency, so a chart test that did not run is a defect, and the
# runner refuses the run instead of listing it as not run (tests/runner/requirements.ts).
LIBREDB_REQUIRE_HELM: "1"
JWT_SECRET: test-secret-for-ci-build-only-32ch
ADMIN_EMAIL: admin@libredb.org
ADMIN_PASSWORD: test-admin
Expand Down Expand Up @@ -196,6 +204,86 @@ jobs:
disable_search: true
fail_ci_if_error: false

# The same suite, on the two platforms the project has never measured. Linux is
# covered by the `test` job above, which runs the identical runner with coverage.
#
# A SEPARATE job rather than a matrix on `test`: GitHub appends the matrix values
# to a matrix job's name, so matrixing `test` would rename the required check
# "Unit & Integration Tests" out of existence and every pull request would wait
# forever for a check that can no longer report. This job is deliberately not a
# required check yet; promote it once it has a history of being green.
#
# It uses no secrets, so it runs on fork pull requests like any other job.
test-cross-platform:
name: Cross-platform Tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6

- name: Setup Node
# Both images ship Node 22 by default, below the product's floor, and the
# tests that spawn bin/studio.js get whatever `node` is on PATH (#709).
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.4.2"

- name: Install dependencies
uses: ./.github/actions/bun-install

- name: Set up Helm
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
with:
version: v4.1.3

- name: Add Bitnami repo
run: helm repo add bitnami https://charts.bitnami.com/bitnami --timeout 5m

- name: Build chart dependencies
run: helm dependency build charts/libredb-studio --skip-refresh

- name: Report the toolchain
# A red leg on a platform nobody here runs is only actionable if the run
# says what it had: a missing 7z or an older bash changes what "red" means.
shell: bash
run: |
echo "platform: $(uname -s 2>/dev/null || echo windows) $(uname -m 2>/dev/null || echo unknown)"
echo "bun: $(bun --version)"
echo "node: $(node --version)"
echo "bash: ${BASH_VERSION:-unknown}"
echo "helm: $(helm version --short)"
for tool in sh bash tar unzip 7z git; do
printf '%-9s %s\n' "$tool:" "$(command -v "$tool" || echo "not on PATH")"
done

- name: Run the test suite
# Deliberately NOT `shell: bash`. On Windows that would be Git Bash, whose
# PATH carries the whole unix toolset, and the contributor this job stands
# in for runs the command from PowerShell. Running it the way they do is
# what makes this job evidence.
run: bun run test
env:
# Optional on a contributor's machine, mandatory here: this job installs Helm and
# builds the chart dependency, so a chart test that did not run is a defect, and the
# runner refuses the run instead of listing it as not run (tests/runner/requirements.ts).
LIBREDB_REQUIRE_HELM: "1"
JWT_SECRET: test-secret-for-ci-build-only-32ch
ADMIN_EMAIL: admin@libredb.org
ADMIN_PASSWORD: test-admin
USER_EMAIL: user@libredb.org
USER_PASSWORD: test-user

e2e:
name: E2E Tests
needs: [lint-and-build]
Expand Down
24 changes: 20 additions & 4 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ jobs:
- name: Checkout code
uses: actions/checkout@v7

- name: Setup Node
# The suite spawns `node bin/studio.js`, and the launcher refuses anything below
# its own Node 24 floor, so the test that asserts its startup URL fails on the
# runner image's default Node. ci.yml's test job was pinned for exactly this in
# #709 and #737; this job runs the same suite before every npm release and was
# not, and the tests that need it arrived after the last release it validated.
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
Expand Down Expand Up @@ -61,10 +71,16 @@ jobs:
run: bun run typecheck

- name: Test
# Per-file process isolation (run-core.sh) — bun's mock.module() is
# process-wide, so the single-process `bun run test` is load-order flaky
# here. This mirrors the reliable path ci.yml uses via test:coverage.
run: bun run test:ci
# `bun run test` is tests/run-tests.ts: one bun process per test file, so
# the process-wide mock.module() cannot leak between files. There is no
# separate test:ci script any more, because there is no longer a
# shared-process form of the suite for it to be the alternative to.
run: bun run test
env:
# Optional on a contributor's machine, mandatory here: this job installs Helm and
# builds the chart dependency, so a chart test that did not run is a defect, and the
# runner refuses the run instead of listing it as not run (tests/runner/requirements.ts).
LIBREDB_REQUIRE_HELM: "1"

- name: Build
run: bun run build
Expand Down
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ bun run format # Biome formatter check (format:fix to write); CSS/JSON
bun run lint # oxlint (fast, syntactic) then ESLint 9
bun run lint:oxc # oxlint only
bun run typecheck # TypeScript strict
bun run test # all layers: unit + api + integration + hooks + security + evals + components
bun run test # every test file under tests/ (except tests/live/), one bun process per file
bun run test:unit # one layer; also test:api, test:integration, test:hooks, test:security, test:evals, test:components
bun run test:e2e # Playwright (builds and starts its own servers; see playwright.config.ts)
bun run test:coverage # coverage report (merged lcov)
bun run coverage:check # enforce 100% line coverage on the merged lcov
Expand All @@ -49,9 +50,9 @@ bun run security:check # security posture drift guard

> **Run `build:lib` after changing anything reachable from `src/exports/`** (workspace, providers, components, security, …) — `bun run build` (Next.js) does NOT update the package dist.

> **Testsalways `bun run test`, never bare `bun test`.** Component tests need isolated execution groups (`tests/run-components.sh`) to avoid `mock.module()` cross-contamination.
> **Tests, always `bun run test`, never bare `bun test` over a directory.** The runner ([`tests/run-tests.ts`](tests/run-tests.ts)) discovers every test file and runs each one in its own bun process, several at a time (`--jobs=N`, `--list`). `bun test tests/api` puts all of them in one process instead, where one file's `mock.module()` becomes every file's, because bun's module mocks are process-wide with no undo. To run one file, name it: `bun tests/run-tests.ts tests/unit/x.test.ts`.

> **Coverage isolation:** `bun`'s `mock.module()` is process-wide, so `test:coverage:core` runs each core test file in its own process (`tests/run-core.sh`) and `test:coverage` merges the per-file lcov. Do NOT collapse it into one `bun test` invocation. Rationale: [`docs/TOOLCHAIN.md`](docs/TOOLCHAIN.md).
> **Coverage:** `bun run test:coverage` is the same runner with `--coverage --merge-into=coverage/lcov.info`: one lcov per test file, merged by `scripts/merge-lcov.mjs`. Two files run without coverage on purpose; they are `COVERAGE_EXEMPT_FILES` in [`tests/runner/discover.ts`](tests/runner/discover.ts), with the reason in its docblock. Rationale: [`docs/TOOLCHAIN.md`](docs/TOOLCHAIN.md).

## Pre-Commit Verification (MANDATORY)

Expand Down
Loading
Loading