@owlmeans/test 0.1.18-rc.7 → 0.1.18-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
18
18
  your project's skill store (`.agents/skills/`):
19
19
 
20
20
  ```sh
21
- npx @owlmeans/agent-skills
21
+ npx @owlmeans/agent-skills@^0.1.18-rc.12
22
22
  ```
23
23
 
24
24
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/test",
4
- "version": "0.1.18-rc.7",
5
- "generatedAt": "2026-09-01T16:28:56.985Z",
4
+ "version": "0.1.18-rc.8",
5
+ "generatedAt": "2026-09-04T22:43:25.438Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,6 +8,10 @@ description: Decision matrix for which testing strategy to use per package in th
8
8
 
9
9
  Every package falls into exactly one of four testing categories. The category drives which `@owlmeans/test*` package(s) the tests depend on, where tests live, and what mocking is allowed.
10
10
 
11
+ `@owlmeans/test` is the base every category builds on — env loading, env gates and fixture loading
12
+ (`testing-unit` documents its exports). `@owlmeans/test-auth`, `@owlmeans/test-integration` and
13
+ `@owlmeans/test-ui` each add one category's helpers on top of it.
14
+
11
15
  ## Categories
12
16
 
13
17
  | Cat | Strategy | Tests location | Runtime | Mocks? | Skill |
@@ -19,28 +23,50 @@ Every package falls into exactly one of four testing categories. The category dr
19
23
 
20
24
  ## Package → category map
21
25
 
22
- - **A** (~31): `basic-ids`, `context`, `config`, `error`, `flow`, `i18n`, `module`, `route`, `router`, `resource`, `socket`, `state`, `static-resource`, `client-route`, `client-config`, `client-context`, `client-resource`, `client-module`, `client-flow`, `client-socket`, `server-route`, `server-config`, `server-context`, `server-module`, `payment`, `api`, `api-config`, `api-config-client`, `api-config-server`, `web-router`, `web-db`
23
- - **B** (~13): `auth`, `auth-common`, `basic-keys`, `basic-envelope`, `did`, `client-auth`, `client-did`, `server-auth`, `oidc`, `server-oidc-rp`, `server-oidc-provider`, `web-oidc-rp`, `web-oidc-provider`, `wled`, `client-payment`
24
- - **C** (~11): `mongo`, `mongo-resource`, `redis`, `redis-resource`, `kluster`, `storage-common`, `storage-resource`, `image-resource`, `server-api`, `server-app`, `queue`, `llm` (live inference providers)
25
- - **D** (~8): `client`, `client-i18n`, `client-panel`, `client-wl`, `web-client`, `web-flow`, `web-panel`, `web-wl`
26
- - **None**: `dep-config`, `_tpl`
26
+ All 101 packages, one category each.
27
+
28
+ - **A** (50): `agent`, `agent-common`, `agent-skills`, `api`, `api-config`, `api-config-client`, `api-config-server`, `astro`, `basic-ids`, `client-config`, `client-context`, `client-entrypoint`, `client-flow`, `client-job`, `client-resource`, `client-route`, `client-socket`, `config`, `consent`, `context`, `create-app`, `entrypoint`, `error`, `flow`, `i18n`, `iam`, `image-resource`, `llm-common`, `mailer`, `payment`, `queue`, `resource`, `route`, `router`, `server-auth-identity`, `server-context`, `server-entrypoint`, `server-iam`, `server-job`, `server-route`, `server-config`, `server-socket`, `server-wl`, `socket`, `state`, `static-resource`, `storage-common`, `web-db`, `web-gtm`, `web-router-react-router`
29
+ - **B** (20): `auth`, `auth-common`, `auth-otp`, `basic-envelope`, `basic-keys`, `client-auth`, `client-did`, `client-iam`, `client-payment`, `did`, `mui-oidc-rp`, `oidc`, `server-auth`, `server-auth-otp`, `server-oidc-provider`, `server-oidc-rp`, `web-auth`, `web-oidc-provider`, `web-oidc-rp`, `wled`
30
+ - **C** (14): `kluster`, `llm` (live inference providers), `mailer-smtp`, `mongo`, `mongo-resource`, `postgres`, `postgres-resource`, `redis`, `redis-queue`, `redis-resource`, `server-api`, `server-app`, `server-mailer-mailgun`, `storage-resource`
31
+ - **D** (11): `client`, `client-i18n`, `client-panel`, `client-wl`, `mui-panel`, `web-client`, `web-consent`, `web-flow`, `web-panel`, `web-router`, `web-wl`
32
+ - **None** (6): `dep-config`, `_tpl`, and the four harness packages themselves — `test`, `test-auth`, `test-integration`, `test-ui`
33
+
34
+ A **contract** package and its **backend** split across categories. `queue`, `resource`,
35
+ `storage-common` and `image-resource` are A — types, AJV schemas, error classes and declarations,
36
+ with nothing to connect to — while `redis-queue`, `mongo`, `postgres` and `storage-resource` (which
37
+ carries the S3 client) are C. Same for mail: `mailer` is A, `mailer-smtp` and
38
+ `server-mailer-mailgun` are C. The test is what the package's own code does at runtime, not what
39
+ its manifest lists: a package that only declares types, schemas and errors has no gate to write,
40
+ while one whose code issues real commands against the service is C even when it names no driver.
41
+ `redis-resource` imports `ioredis` for types alone and drives the client the db service hands it;
42
+ `mongo-resource` carries its driver as a devDependency, for its specs only. Both are C.
43
+
44
+ `web-router` is D, not A: it drives the History API, so its routing behaviour is only observable in
45
+ a browser and its specs mount a real harness in chromium. A client-side package stays in A when it
46
+ ships no component at all — `client-config`, `client-context`, `client-entrypoint`, `client-flow`,
47
+ `client-job`, `client-resource`, `client-route`, `client-socket`, `web-db`, `web-gtm` and
48
+ `web-router-react-router` are services, models and adapters, so `bun test` alone covers them.
49
+
50
+ A new package takes the category its own behaviour implies: an external service it cannot fake ⇒ C,
51
+ a rendered React surface ⇒ D, an authenticated identity it must stand in for ⇒ B, otherwise A.
27
52
 
28
53
  ## Eight invariants (apply everywhere)
29
54
 
30
- 1. **Tests live next to `src/` in `<your-package>/tests/`**, named `*.spec.ts`. They are outside `rootDir: ./src/` so `tsc -b` already excludes them.
55
+ 1. **Tests live next to `src/` in `<your-package>/tests/`**, named `*.spec.ts`. They sit outside `rootDir: ./src/`, so the package `tsconfig.json` must list `"./tests/**/*"` in `exclude` — otherwise `tsc -b` tries to compile them and fails on the rootDir boundary.
31
56
  2. **One real context per package**, set up in `tests/context.ts`. It builds a real `BasicContext` / server / client context (whatever the package documents in its `SKILL.md`), provisions real sibling-package services and resources, and exports a single helper specs import.
32
57
  3. **Cross-package imports are real.** No mocks for sibling packages. Auth/authz is the only mockable boundary, only in category B, only via `@owlmeans/test-auth`.
33
58
  4. **Don't test context wiring** in every package. Trust `@owlmeans/context`'s own tests.
34
59
  5. **Don't test utils. Don't test types.** Max 3-4 tests per method/function.
35
60
  6. **Cover the package's `SKILL.md` and `README.md` cases first.** Those are the documented consumption surfaces — tests validate them as executable specs before reaching for internal coverage.
36
- 7. **Env-gated provisioning** for category C: if a required env var is empty, neither the dependent service is provisioned in the test context nor are the specs that need it executed. Specs self-skip with a printed reason — never fail.
61
+ 7. **Env-gated provisioning** whenever a spec needs something the machine may not have: if a required env var is empty, neither the dependent service is provisioned in the test context nor are the specs that need it executed. Specs self-skip with a printed reason — never fail. Gates come from one of two places: `@owlmeans/test-integration` ships a ready-made one for each service it covers (PostgreSQL, MongoDB, Redis, S3, Kubernetes, SMTP), and a package whose provider is outside that set declares its own with `makeGates` from `@owlmeans/test` — `llm` does exactly that for its inference providers, and Mailgun's HTTP API needs the same. Both produce identical skip behaviour.
37
62
  8. **No new mocks.** If a test seems to need a mock that isn't auth/authz, the test belongs in category C (and the package belongs in category C) — write an integration test, not a fake.
38
63
 
39
64
  ## Running tests
40
65
 
41
66
  - All packages from root: `bun run test`
42
67
  - One package: `cd <your-package> && bun test ./tests` — same line for every category
43
- - Integration tests: `MONGO_URL=... bun run test` empty values skip cleanly
68
+ - Gated tests: `MONGO_URL=... bun run test`, or put the values in the repo-root `.env` that
69
+ `loadEnv` picks up. Empty values skip cleanly; a missing `.env` is not an error
44
70
  - One-time UI setup: `bunx playwright install chromium` (downloads the browser the `playwright` library drives in category D)
45
71
 
46
72
  ## What to test (always)
@@ -6,7 +6,54 @@ description: Category-A unit tests for OwlMeans Common packages — no mocks, re
6
6
 
7
7
  # Unit Tests — Category A (no mocks)
8
8
 
9
- Apply this skill when adding tests to packages in category A (see `testing-overview`). The list includes core abstractions (`context`, `config`, `error`, `entrypoint`, `route`, `resource`, …) and platform-agnostic services (`api`, `state`, `flow`, `i18n`, `client-flow`, `client-socket`, `server-route`, `server-context`, `web-router`, `web-db`, …).
9
+ **Install:** `"@owlmeans/test": "^0.1.18-rc.8"` in `devDependencies`
10
+
11
+ Apply this skill when adding tests to packages in category A (see `testing-overview`). The list includes core abstractions (`context`, `config`, `error`, `entrypoint`, `route`, `resource`, …) and platform-agnostic services (`api`, `state`, `flow`, `i18n`, `client-flow`, `client-socket`, `client-job`, `server-route`, `server-context`, `web-db`, …).
12
+
13
+ `@owlmeans/test` is also the base of the other three harness packages, so its exports below are
14
+ available in every category.
15
+
16
+ ## Helpers from `@owlmeans/test`
17
+
18
+ | Helper | Purpose |
19
+ |---|---|
20
+ | `loadEnv({ force?, file? })` | Read a `.env` into `process.env`, once per process. Never overwrites a variable already set to a non-empty value; a missing file is not an error. The root is found by walking up for a `bun.lock`, or for a `package.json` sitting beside a directory named `packages` — a workspace shaped any other way must pass `file` explicitly. |
21
+ | `hasEnv(key)` | `loadEnv()`, then true when the variable is set and non-empty. |
22
+ | `requireEnv(keys)` | `EnvGate` — `{ ok: true }` when every key is populated, otherwise `{ skip: true, reason }` naming the missing ones. |
23
+ | `makeGates(spec)` | Frozen `{ name: EnvGate }` built from `{ name: [envKey, …] }`. One call in `tests/context.ts` is the whole suite's availability map. |
24
+ | `isSkip(gate)` | Type guard narrowing an `EnvGate` to the skip branch, so `gate.reason` is readable. |
25
+ | `loadFixture<T>(relPath)` | Parse a JSON fixture from the package's own `tests/` directory — `bun test` sets `process.cwd()` to the package root, so the path is `tests/`-relative. |
26
+ | Types: `EnvGate`, `EnvOk`, `EnvSkip`, `GateSpec`, `Gates<S>` | The gate shapes, for typing a suite's exported map. |
27
+
28
+ ### Gating a spec
29
+
30
+ A category-A package needs nothing external, so most specs never gate. When one does — a live
31
+ provider, an optional local binary — declare every gate in `tests/context.ts` and let each spec
32
+ choose its own `test` at module scope:
33
+
34
+ ```ts
35
+ // tests/context.ts
36
+ import { makeGates } from '@owlmeans/test'
37
+
38
+ export const gates = makeGates({
39
+ openrouter: ['OPENROUTER_SECRET'],
40
+ anthropic: ['ANTHROPIC_SECRET'],
41
+ })
42
+ ```
43
+
44
+ ```ts
45
+ // tests/<area>.spec.ts
46
+ import { test } from 'bun:test'
47
+ import { isSkip } from '@owlmeans/test'
48
+ import { gates } from './context.js'
49
+
50
+ const gate = gates.openrouter
51
+ const it = isSkip(gate) ? test.skip : test
52
+ // `isSkip(gate) ? gate.reason : ''` names the missing variables in the skip title.
53
+ ```
54
+
55
+ Bun decides `test` vs `test.skip` **synchronously**, so the decision has to be a value already in
56
+ hand — never an `await` inside the suite. An empty variable is a printed skip, never a failure.
10
57
 
11
58
  ## Layout
12
59
 
@@ -16,7 +63,7 @@ Apply this skill when adding tests to packages in category A (see `testing-overv
16
63
  ├── tests/
17
64
  │ ├── context.ts # one real context, shared across specs
18
65
  │ ├── <area>.spec.ts # *.spec.ts only
19
- │ └── fixtures/ # JSON fixtures, optional
66
+ │ └── fixtures/ # JSON fixtures, read with loadFixture('fixtures/<name>.json')
20
67
  ```
21
68
 
22
69
  ## `tests/context.ts` — single source of truth
@@ -79,7 +126,10 @@ Per-package `package.json`:
79
126
  }
80
127
  ```
81
128
 
82
- `bun:test` is built-in no devDeps to add. The root `bun run test` script runs every package's `test` script via workspace filters.
129
+ `bun:test` is built-in, so `@types/bun` is the only devDep a plain category-A package needs; add
130
+ `@owlmeans/test` when a spec actually uses a helper above. Add `"./tests/**/*"` to the package's
131
+ `tsconfig.json` `exclude` so `tsc -b` never compiles specs into `build/`. The root `bun run test`
132
+ script runs every package's `test` script via workspace filters.
83
133
 
84
134
  ## When auth shows up
85
135
 
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,IAAI,CAAA;CACT;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;AAkCrC,eAAO,MAAM,OAAO,GAAI,OAAO;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,IAanE,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,KAAG,OAIpC,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,KAAG,OAK3C,CAAA"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,IAAI,CAAA;CACT;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;AAkCrC,eAAO,MAAM,OAAO,UAAW;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,IAanE,CAAA;AAED,eAAO,MAAM,MAAM,QAAS,MAAM,KAAG,OAIpC,CAAA;AAED,eAAO,MAAM,UAAU,SAAU,MAAM,EAAE,KAAG,OAK3C,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,KAAG,CAI1D,CAAA"}
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,GAAG,OAAO,WAAW,MAAM,KAAG,CAI1D,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AAE/C,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,QAAQ,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO;CAAE,CAAA;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,EAAE,MAAM,CAAC,KAAG,KAAK,CAAC,CAAC,CAM9D,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,MAAM,OAAO,KAAG,IAAI,IAAI;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAClC,CAAA"}
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AAE/C,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,QAAQ,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO;CAAE,CAAA;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,QAAQ,CAAC,KAAG,KAAK,CAAC,CAAC,CAM9D,CAAA;AAED,eAAO,MAAM,MAAM,SAAU,OAAO,KAAG,IAAI,IAAI;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAClC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/test",
3
- "version": "0.1.18-rc.7",
3
+ "version": "0.1.18-rc.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,7 +21,7 @@
21
21
  }
22
22
  },
23
23
  "dependencies": {
24
- "@owlmeans/context": "^0.1.18-rc.7"
24
+ "@owlmeans/context": "^0.1.18-rc.8"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@owlmeans/dep-config": "^0.1.18-rc.6",