@owlmeans/test 0.1.18-rc.35 → 0.1.18-rc.36

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
@@ -1,14 +1,130 @@
1
1
  # @owlmeans/test
2
2
 
3
- Foundation helpers shared by the OwlMeans test packages and individual package test suites.
3
+ Foundation helpers shared by the OwlMeans test packages and individual package test suites: the
4
+ repo-root `.env` loader, environment gates that let a spec self-skip with a printed reason, and a
5
+ JSON fixture loader. Every package's `tests/` uses it directly or through one of the harness
6
+ packages built on it. It holds no auth mocks, no service gates and no browser driver — those are
7
+ `@owlmeans/test-auth` (category B), `@owlmeans/test-integration` (category C) and
8
+ `@owlmeans/test-ui` (category D). Tests run on `bun:test`; this package adds no runner. The
9
+ package's skills are `testing-overview` (which category a package is in) and `testing-unit`.
4
10
 
5
- - `envHelper.loadEnv(opts?)` — locate `<monorepo-root>/.env`, parse it, merge into `process.env` (idempotent; no-op when Bun already loaded the file via `--env-file`).
6
- - `envHelper.requireEnv(keys)` — returns `{ ok: true }` or `{ skip: true, reason }` so callers can `test.skip(...)` when an integration variable is missing.
7
- - `envHelper.hasEnv(key)` — boolean shorthand for conditional service provisioning in `tests/context.ts`.
8
- - `makeGates(spec)` — builds a frozen record of `{ <name>: { skip, reason } }` so a per-package `tests/context.ts` exposes one source of truth and individual specs can self-skip.
9
- - `loadFixture<T>(relPath)` — JSON fixture loader rooted at the consuming package's `tests/fixtures/`.
11
+ ## Installation
10
12
 
11
- Tests must be located at `packages/<pkg>/tests/`, named `*.spec.ts`. See the `testing-overview` skill for the full strategy.
13
+ ```bash
14
+ bun add -d @owlmeans/test@^0.1.18-rc.36
15
+ ```
16
+
17
+ ## Concepts
18
+
19
+ - **Env file** — `<monorepo-root>/.env`, found by walking up from `process.cwd()` to a directory
20
+ holding `bun.lock`, or a `package.json` beside a `packages/` directory. Merged into `process.env`
21
+ once per process; a variable already set to a non-empty value wins. A missing file is not an error.
22
+ - **Gate** — `EnvGate`: `{ ok: true }` when every required variable is set, otherwise
23
+ `{ skip: true, reason }` naming the missing ones.
24
+ - **Gate map** — `makeGates({ name: [envKey, …] })`, a frozen record built once in a suite's
25
+ `tests/context.ts`; specs read it to choose `test` or `test.skip`.
26
+ - **Fixture** — a JSON file under the consuming package's `tests/`, read relative to it.
27
+
28
+ ## Usage
29
+
30
+ ### Declare the suite's gates
31
+
32
+ ```typescript
33
+ // tests/context.ts
34
+ import { makeGates } from '@owlmeans/test'
35
+
36
+ export const gates = makeGates({
37
+ openrouter: ['OPENROUTER_SECRET'],
38
+ anthropic: ['ANTHROPIC_SECRET'],
39
+ })
40
+ ```
41
+
42
+ ### Self-skip a spec
43
+
44
+ Bun picks `test` or `test.skip` synchronously, so decide from the gate at module scope:
45
+
46
+ ```typescript
47
+ // tests/inference.spec.ts
48
+ import { describe, expect, test } from 'bun:test'
49
+ import { isSkip } from '@owlmeans/test'
50
+ import { gates } from './context.js'
51
+
52
+ const gate = gates.openrouter
53
+ const it = isSkip(gate) ? test.skip : test
54
+
55
+ describe('llm — openrouter', () => {
56
+ it(`answers a prompt${isSkip(gate) ? ` (${gate.reason})` : ''}`, async () => {
57
+ /* exercise the real provider */
58
+ })
59
+ })
60
+ ```
61
+
62
+ ### Read the environment directly
63
+
64
+ ```typescript
65
+ import { envHelper } from '@owlmeans/test'
66
+
67
+ envHelper.loadEnv() // idempotent; `{ force: true }` re-reads
68
+ envHelper.loadEnv({ file: '/path/to/other/.env' }) // a workspace shaped differently
69
+
70
+ if (envHelper.hasEnv('MONGO_URL')) {
71
+ /* provision the optional service in tests/context.ts */
72
+ }
73
+
74
+ const gate = envHelper.requireEnv(['SMTP_HOST', 'SMTP_PORT'])
75
+ ```
76
+
77
+ ### Load a fixture
78
+
79
+ ```typescript
80
+ import { loadFixture } from '@owlmeans/test'
81
+
82
+ const profile = loadFixture<ProfileRecord>('fixtures/profile.json') // <package>/tests/fixtures/profile.json
83
+ ```
84
+
85
+ ## API
86
+
87
+ | Symbol | Kind | Purpose |
88
+ |---|---|---|
89
+ | `envHelper` (`createEnvHelper()`) | helper | The env loader and gates below |
90
+ | `envHelper.loadEnv(opts?)` | member | Locate `<monorepo-root>/.env`, parse it, merge into `process.env` (idempotent; no-op for variables Bun already loaded via `--env-file`) |
91
+ | `envHelper.hasEnv(key)` | member | `loadEnv()`, then whether the variable is set and non-empty |
92
+ | `envHelper.requireEnv(keys)` | member | `{ ok: true }` or `{ skip: true, reason }` so callers can `test.skip(...)` when an integration variable is missing |
93
+ | `makeGates(spec)` | function | Frozen record of `{ <name>: EnvGate }` — one source of truth in `tests/context.ts` |
94
+ | `isSkip(gate)` | function | Type guard narrowing an `EnvGate` to `{ skip: true, reason }` |
95
+ | `loadFixture<T>(relPath)` | function | JSON fixture loader rooted at the consuming package's `tests/` |
96
+
97
+ | Type | Purpose |
98
+ |---|---|
99
+ | `EnvGate`, `EnvOk`, `EnvSkip` | Gate outcome union and its branches |
100
+ | `GateSpec`, `Gates<S>` | `{ name: string[] }` input and the frozen gate map it produces |
101
+ | `LoadEnvOptions` | `{ force?, file? }` |
102
+ | `EnvHelper` | The helper's interface |
103
+
104
+ ## Test layout
105
+
106
+ Tests are located at `packages/<pkg>/tests/`, named `*.spec.ts`, and run with `bun test ./tests`
107
+ from the package root. List `"./tests/**/*"` in the package `tsconfig.json` `exclude` so `tsc -b`
108
+ never compiles specs. One real context per package lives in `tests/context.ts`; specs import its
109
+ helper instead of building contexts themselves.
110
+
111
+ ## Common pitfalls
112
+
113
+ - Awaiting anything before choosing `test` / `test.skip` — the choice must be a value already in hand.
114
+ - A missing variable must skip, never fail; do not throw from a gate.
115
+ - `loadFixture` resolves against `process.cwd()`, which `bun test` sets to the package root; running
116
+ specs from another directory breaks the path.
117
+ - A workspace without `bun.lock` and without a `packages/` directory beside its `package.json` is not
118
+ found by `loadEnv` — pass `file` explicitly.
119
+ - Do not add mocks here; auth mocks belong in `@owlmeans/test-auth`, and anything else is an
120
+ integration test.
121
+
122
+ ## Related packages
123
+
124
+ - [`@owlmeans/test-auth`](../test-auth) — auth/authz mocks for category-B unit tests
125
+ - [`@owlmeans/test-integration`](../test-integration) — service gates, namespaces and cleanup for category-C tests
126
+ - [`@owlmeans/test-ui`](../test-ui) — Playwright-driven component acceptance for category D
127
+ - [`@owlmeans/context`](../context) — the real context a suite's `tests/context.ts` builds
12
128
 
13
129
  <!-- owlmeans:agent-guidance:start -->
14
130
  ## Agent guidance
@@ -18,7 +134,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
18
134
  your project's skill store (`.agents/skills/`):
19
135
 
20
136
  ```sh
21
- npx @owlmeans/agent-skills@^0.1.18-rc.49
137
+ npx @owlmeans/agent-skills@^0.1.18-rc.52
22
138
  ```
23
139
 
24
140
  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.35",
5
- "generatedAt": "2026-10-05T21:03:06.380Z",
4
+ "version": "0.1.18-rc.36",
5
+ "generatedAt": "2026-10-08T21:27:54.292Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -31,9 +31,9 @@ New auth mocks belong in `@owlmeans/test-auth`, never in a package's own `tests/
31
31
 
32
32
  ## Package → category map
33
33
 
34
- All 101 packages, one category each.
34
+ All 103 packages, one category each.
35
35
 
36
- - **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`
36
+ - **A** (52): `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`, `common-inquiry`, `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-inquiry`, `web-router-react-router`
37
37
  - **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`
38
38
  - **C** (15): `kluster`, `llm` (live inference providers), `mailer-smtp`, `mongo`, `mongo-resource`, `planning-postgres`, `postgres`, `postgres-resource`, `redis`, `redis-queue`, `redis-resource`, `server-api`, `server-app`, `server-mailer-mailgun`, `storage-resource`
39
39
  - **D** (11): `client`, `client-i18n`, `client-panel`, `client-wl`, `mui-panel`, `web-client`, `web-consent`, `web-flow`, `web-panel`, `web-router`, `web-wl`
@@ -52,8 +52,8 @@ while one whose code issues real commands against the service is C even when it
52
52
  `web-router` is D, not A: it drives the History API, so its routing behaviour is only observable in
53
53
  a browser and its specs mount a real harness in chromium. A client-side package stays in A when it
54
54
  ships no component at all — `client-config`, `client-context`, `client-entrypoint`, `client-flow`,
55
- `client-job`, `client-resource`, `client-route`, `client-socket`, `web-db`, `web-gtm` and
56
- `web-router-react-router` are services, models and adapters, so `bun test` alone covers them.
55
+ `client-job`, `client-resource`, `client-route`, `client-socket`, `web-db`, `web-gtm`, `web-inquiry`
56
+ and `web-router-react-router` are services, models and adapters, so `bun test` alone covers them.
57
57
 
58
58
  A new package takes the category its own behaviour implies: an external service it cannot fake ⇒ C,
59
59
  a rendered React surface ⇒ D, an authenticated identity it must stand in for ⇒ B, otherwise A.
@@ -6,7 +6,7 @@ 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
- **Install:** `"@owlmeans/test": "^0.1.18-rc.35"` in `devDependencies`
9
+ **Install:** `"@owlmeans/test": "^0.1.18-rc.36"` in `devDependencies`
10
10
 
11
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
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/test",
3
- "version": "0.1.18-rc.35",
3
+ "version": "0.1.18-rc.36",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {