@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 +1 -1
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/testing-overview/SKILL.md +34 -8
- package/agent-meta/skills/testing-unit/SKILL.md +53 -3
- package/build/env.d.ts.map +1 -1
- package/build/fixtures.d.ts.map +1 -1
- package/build/gates.d.ts.map +1 -1
- package/package.json +2 -2
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
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/test",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
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
|
|
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**
|
|
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
|
-
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
package/build/env.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/build/fixtures.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/build/gates.d.ts.map
CHANGED
|
@@ -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,
|
|
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.
|
|
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.
|
|
24
|
+
"@owlmeans/context": "^0.1.18-rc.8"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@owlmeans/dep-config": "^0.1.18-rc.6",
|