Linkr
Home Resources Tools Documentation Blog Demo
FR
  • What is Linkr?
  • Deployment modes
  • Quick start
  • Local install
  • With Docker
  • Manual install
  • Client-only
  • Install LibreChat
  • Your first project
  • Linkr in a clinical data warehouse
  • Workspaces and projects
  • The data pipeline
  • Entities and sharing
  • Versioning and collaboration
  • Overview
  • Projects
  • Wiki
  • Plugins
  • Members and roles
  • Settings
  • Schemas
  • Getting and exploring
  • Mapping
  • Databases
  • Derived sub-databases
  • Data quality
  • Data catalog
  • Build and publish
  • Anonymize
  • SQL script collections
  • ETL pipelines
  • Building and running
  • Generating the scripts
  • Overview
  • Mapping projects
  • Global view
  • Target concepts
  • Mapping editor
  • Suggestions
  • AI agent
  • Evaluation
  • Export
  • Overview
  • Databases
  • Concepts
  • Cohorts
  • Building
  • Results, SQL and report
  • Patient data
  • Pipeline
  • Datasets
  • IDE
  • Web apps
  • Versioning
  • Overview
  • Tabs and widgets
  • Built-in widgets
  • Analysis widgets
  • Control charts (SPC)
  • Surveys and eCRF
  • R and Python code
  • Filters, settings and export
  • Overview
  • Presentation mode
  • Exporting a report
  • Agents
  • MCP server
  • Skills
  • Import and export
  • Git versioning
  • Community catalog
  • Creating your own catalog
  • Publishing content
  • Production install
  • Configuration
  • Database connectors
  • Authentication and permissions
  • Files on the server
  • Backup and restore
  • Contributing code
  • Automated tests
  • Glossary
  • Keyboard shortcuts
  • Release notes
Documentation Contributing Automated tests

Automated tests

Linkr's automated tests, by category — unit tests, browser journeys, the database matrix, hospital data warehouse scenarios: what each one checks, where it lives in the code, how to run it, and how to keep it up to date with the run-tests and update-tests skills.

Summary

Linkr is checked by four levels of tests, from the fastest to the most thorough: unit tests, run on every git push; end-to-end journeys, which drive a real browser; the database matrix, which runs every query the app sends on every engine; and the SPE scenarios, which install Linkr inside a simulated hospital data warehouse. Two skills, /run-tests and /update-tests, pick the tests to run and keep them up to date.

Overview

LevelWhat it provesCommandDurationWhen
Unitpure logic: SQL building, escaping, import/export, API routes and servicessee Unit tests2 to 5 minon every git push
End-to-enda user’s journeys, in a real browsernpm run test:e2e1 to 2 minon demand, in review, before a release
Database matrixevery query gives the same answer on every engine, and how fastnpm run test:matrix1 min (×1) to 20 min (×50)after an SQL or connector change, before a release
SPE scenariosLinkr works behind every proxy, network and machine variantnpm run test:spe~3 min per scenarioafter a deployment change, before a release

A full campaign, before a release, takes about an hour. The developer-side reference is docs/testing.md, in the repository.

Unit tests

They check pure logic, without the interface: SQL query building, escaping, fuzzy search, entity import and export, formatting. What deserves a unit test — and what does not, such as interface components that change often — is described in docs/conventions.md.

  • Frontend — next to the code, as *.test.ts in apps/web/src/; run by npm run test (Vitest).
  • API — in apps/api/tests/; run by npm run test:api (pytest). They run in parallel on four processes, each test with its own SQLite database, and none may last more than two minutes.
  • Packages — each package in packages/ has its tests: linkr-format (the export format), linkr-mcp (the MCP server), linkr-py (the Python client).

At push time

A hook — a script git runs before sending your commits — runs these tests, but only those of what the push changes: a change in apps/api runs the API tests, a change in apps/web the frontend’s tests, lint and type checking. If one fails, the push is blocked. The script is scripts/git-hooks/pre-push, installed by npm install; LINKR_PREPUSH_ALL=1 git push runs everything. The slower levels are never in the hook.

End-to-end journeys

A journey is a user scenario replayed in a real browser by Playwright: open the home page, search for a concept, run a cohort, display a dashboard… It checks what a user would see, and also fails on any JavaScript error in the page.

  • client — the browser-only (WASM) build, with the demo data, served on port 4317. The data must have been fetched once with npm run data:fetch.
  • server — a full instance, frontend and API; the SPE scenarios provide it. The journey starts with the setup wizard, which creates the first account: it needs a disposable instance.

The journeys live in apps/web/e2e/, the configuration in apps/web/playwright.config.ts. They find elements by their role and visible text, never by a CSS class: a style change does not break them, a renamed button does. After a failure, the trace and the page’s accessibility tree are kept in apps/web/test-results/ (npx playwright show-trace …).

A known bug stays visible

A journey that reproduces a bug still open carries the @known-bug tag in its title: it is left out of ordinary runs, and E2E_KNOWN_BUGS=1 runs it again. Once the bug is fixed, the tag is removed — the journey then becomes the regression test.

Database matrix

Linkr reads databases on several engines — a Parquet folder, a DuckDB or SQLite file, a PostgreSQL, MySQL or Doris server. The matrix checks that the SQL the app actually sends gives the same answer everywhere. It runs the real query builders — patient record, overview, cohorts, concept counts, catalog — then runs each query on each engine, through the API’s connection code, and compares the rows with the reference: DuckDB on the Parquet.

  • different — an engine answered otherwise: always a bug, in the generated SQL or in the connector;
  • error — an engine refused the app’s SQL;
  • the timings per query and per engine point at slow queries.

The data is the public MIMIC-IV demo in OMOP format (100 patients), cloned into .cache/test-data/ on the first run. --scale N makes N copies of it, under new identifiers, for a bigger database — on the file engines and PostgreSQL: MySQL only checks the dialect, which the normal-size database is enough for.

npm run test:matrix                                    # file engines, on this machine
npm run test:matrix -- --spe                           # + PostgreSQL and MySQL, inside the SPE simulation
npm run test:matrix -- --spe --machine tiny --scale 50 # a small machine, a ×50 database

The code is in tests/db-matrix/ and scripts/test-matrix.mjs. The report, .cache/db-matrix/<machine>-x<scale>/report.md, gives per engine the number of identical, different and failed queries, the total time and the slowest query.

SPE scenarios

An SPE — secure processing environment — is the isolated space where a hospital data warehouse runs Linkr for a project: no internet, a proxy in front of the app, a local package mirror. The repository holds a simulation of one, in docker/spe-sim/. Each scenario starts from a blank install, starts it under a variant, runs a smoke test — does the app answer, under the right prefix? — then, with --e2e, the server journeys.

The scenarios, in docker/spe-sim/scenarios/:

  • default — the SPE as most hospitals set it up: WebSockets forwarded, prefix stripped, a package mirror, generous timeouts;
  • no-websockets — a proxy that drops WebSockets: the app loads, and the terminal, the live ETL log and notifications fail with a message naming the proxy;
  • short-timeout — a proxy that cuts an idle connection after a minute: the app says so instead of hanging;
  • small-uploads — uploads capped at 1 MB, nginx’s default: a bigger upload fails with an error the app shows;
  • no-strip-prefix — a proxy that forwards the path without stripping the prefix: the app works as with the default scenario;
  • offline — no package mirror: project environments cannot install anything, and say why.
npm run test:spe -- default                 # one scenario
npm run test:spe -- --machine tiny --e2e    # all of them, on a small machine, with the journeys

The script is scripts/test-spe.mjs; the report is .cache/spe/report-<machine>.md.

Machine profiles

A fast development Mac hides slow queries. The profiles in docker/spe-sim/machines/ — tiny, laptop, small-server, unlimited — cap the CPU and memory of the API container and of the databases; DuckDB reads the memory cap and sizes itself on it. Compare a capped run with unlimited before calling a query slow. Docker Desktop’s own virtual machine limits remain the ceiling of every profile.

Flaky tests

A test that fails one run in five reveals a race condition — two operations overtaking each other depending on timing —, in the test or in the app. No level retries a failed test: a flaky test shows as a failure instead of hiding. Repeating everything every time would cost too much; repeat where races are born:

  • after changing how databases are mounted, how the demo data is loaded, background jobs, cancellation or WebSockets, run that area’s tests five times (--repeat-each 5 for Playwright, a loop for pytest);
  • before a release, run the client journeys five times;
  • a failure seen once is investigated — not rerun until it passes.

The skills

Two Claude Code skills, in .claude/skills/, carry these test levels.

/run-tests

Picks the levels to run from what the change touches — an SQL builder calls for the matrix, a file in docker/ for the SPE scenarios, a page or a store for the journeys —, runs them, then gives a verdict: one table per level with results and durations, and every failure, saying whether it comes from the change or was already there. It also takes an argument: quick, e2e, matrix, spe or full. /code-review calls it at the end of every review.

/update-tests

Brings the tests up to date after the code has moved: it finds what changed since the last audit, adds the missing tests — unit tests, matrix queries, journeys, scenarios —, fixes those the change made wrong, retires those no longer useful and re-checks the known bugs. Each audit is logged in AUDIT-LOG.md, so the next one starts where this one stopped. Run it when a feature lands without tests, or before a release.

Going further

  • Contributing code — installing from source, branches, worktrees and the other skills.
  • Deployment modes — why the journeys exist in a browser-only and a server version.
  • Database connectors — the engines the matrix checks.
PreviousContributing codeNextGlossary

Product

  • Home
  • Demo

Resources

  • Documentation
  • Resources
  • Tools
  • Blog

Community

  • Framagit source code
  • Github source code

About

  • InterHop.org
  • Contact

2021–2026 InterHop — CC BY-NC-SA 4.0 (site) · AGPLv3 (software)