@fougere/testing 0.3.0-alpha.0
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/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/all.d.ts +33 -0
- package/dist/all.d.ts.map +1 -0
- package/dist/all.js +47 -0
- package/dist/all.js.map +1 -0
- package/dist/app.d.ts +68 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +65 -0
- package/dist/app.js.map +1 -0
- package/dist/comparison.d.ts +43 -0
- package/dist/comparison.d.ts.map +1 -0
- package/dist/comparison.js +182 -0
- package/dist/comparison.js.map +1 -0
- package/dist/derive.d.ts +12 -0
- package/dist/derive.d.ts.map +1 -0
- package/dist/derive.js +13 -0
- package/dist/derive.js.map +1 -0
- package/dist/doors.d.ts +37 -0
- package/dist/doors.d.ts.map +1 -0
- package/dist/doors.js +76 -0
- package/dist/doors.js.map +1 -0
- package/dist/gql.d.ts +67 -0
- package/dist/gql.d.ts.map +1 -0
- package/dist/gql.js +113 -0
- package/dist/gql.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/load.d.ts +34 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +92 -0
- package/dist/load.js.map +1 -0
- package/dist/remotes.d.ts +49 -0
- package/dist/remotes.d.ts.map +1 -0
- package/dist/remotes.js +121 -0
- package/dist/remotes.js.map +1 -0
- package/dist/sample.d.ts +22 -0
- package/dist/sample.d.ts.map +1 -0
- package/dist/sample.js +61 -0
- package/dist/sample.js.map +1 -0
- package/dist/scope.d.ts +49 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +62 -0
- package/dist/scope.js.map +1 -0
- package/dist/stub.d.ts +44 -0
- package/dist/stub.d.ts.map +1 -0
- package/dist/stub.js +71 -0
- package/dist/stub.js.map +1 -0
- package/dist/sync.d.ts +46 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +79 -0
- package/dist/sync.js.map +1 -0
- package/dist/vitest.d.ts +22 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +49 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fougere contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @fougere/testing
|
|
2
|
+
|
|
3
|
+
Tests derived from the declaration, and a gradient the test file never states.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add -D @fougere/testing
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// vitest.config.ts — the whole file
|
|
11
|
+
import { fougereTest } from '@fougere/testing/vitest';
|
|
12
|
+
export default fougereTest();
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// tests/contracts.test.ts — the whole file, and it produces one test per branch
|
|
17
|
+
import { testApp, checkContract, checkOutput, checkDoors } from '@fougere/testing';
|
|
18
|
+
import Product from '../fronds/catalog/entities/Product.js';
|
|
19
|
+
|
|
20
|
+
const app = await testApp();
|
|
21
|
+
|
|
22
|
+
checkContract(app, Product); // every refusal the judge can state
|
|
23
|
+
checkOutput(app, Product); // no writeOnly field leaves
|
|
24
|
+
checkDoors(app, Product); // local · REST · GraphQL · RPC answer the same
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`checkDoorContract(app, Product, cases)` runs handler-specific invocation cases through
|
|
28
|
+
the same four-door harness. It is useful when the handler must observe a distinction such
|
|
29
|
+
as omitted (`undefined`) versus explicitly supplied `null`.
|
|
30
|
+
|
|
31
|
+
`testApp()` takes no argument: a file under `fronds/catalog/tests/` says its subject is
|
|
32
|
+
`catalog`, the way `entities/` already says what a directory holds.
|
|
33
|
+
|
|
34
|
+
Full documentation: **[Testing](https://fougere.dev/docs/infra/testing)** ·
|
|
35
|
+
a worked example: `demos/test-gradient`.
|
|
36
|
+
|
|
37
|
+
## What it does not do
|
|
38
|
+
|
|
39
|
+
A test derived from `Post` can never say that `Post` is wrong — it proves the realizations
|
|
40
|
+
agree. What must break when the declaration moves is `fougere migrate`. And business
|
|
41
|
+
intent is always written by hand.
|
|
42
|
+
|
|
43
|
+
MIT
|
package/dist/all.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { App } from '@fougere/core';
|
|
2
|
+
import type { SchemaView } from '@fougere/schema';
|
|
3
|
+
import { type CheckOptions } from './doors.js';
|
|
4
|
+
import { type DoorOptions } from './comparison.js';
|
|
5
|
+
export interface CheckAllOptions extends DoorOptions, CheckOptions {
|
|
6
|
+
/** Entities to leave out, by name — one whose rows a test cannot seed, typically. */
|
|
7
|
+
except?: string[];
|
|
8
|
+
/** Skip the four-door comparison. The contract and the leak are still checked. */
|
|
9
|
+
doors?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Every entity the app SERVES, with its handler.
|
|
13
|
+
*
|
|
14
|
+
* Not every entity it scanned: one with no handler has no façade, so there is no door to
|
|
15
|
+
* ask — the distinction the NOT_FOUND message used to miss. A relation is not followed
|
|
16
|
+
* either; the entity it points at is checked on its own turn if it has a door.
|
|
17
|
+
*/
|
|
18
|
+
export declare function servedEntities(app: App): {
|
|
19
|
+
name: string;
|
|
20
|
+
entity: SchemaView;
|
|
21
|
+
}[];
|
|
22
|
+
/**
|
|
23
|
+
* The whole suite, for every entity, from a file that names none of them.
|
|
24
|
+
*
|
|
25
|
+
* The list comes from the scan rather than from imports, and that is the point: a list of
|
|
26
|
+
* imports is a second copy of what the project already declares, and it goes stale the day
|
|
27
|
+
* an entity is added — silently, because a missing test cannot fail.
|
|
28
|
+
*
|
|
29
|
+
* An entity whose frond is declared in `remotes:` is not here at all: it is not this app's
|
|
30
|
+
* to serve, and `testApp` does not follow the topology in the first place.
|
|
31
|
+
*/
|
|
32
|
+
export declare function checkAll(app: App, options?: CheckAllOptions): void;
|
|
33
|
+
//# sourceMappingURL=all.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D,MAAM,WAAW,eAAgB,SAAQ,WAAW,EAAE,YAAY;IAChE,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kFAAkF;IAClF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,EAAE,CAS/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,eAAoB,GAAG,IAAI,CAetE"}
|
package/dist/all.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { checkContract, checkOutput } from './doors.js';
|
|
3
|
+
import { checkDoors } from './comparison.js';
|
|
4
|
+
/**
|
|
5
|
+
* Every entity the app SERVES, with its handler.
|
|
6
|
+
*
|
|
7
|
+
* Not every entity it scanned: one with no handler has no façade, so there is no door to
|
|
8
|
+
* ask — the distinction the NOT_FOUND message used to miss. A relation is not followed
|
|
9
|
+
* either; the entity it points at is checked on its own turn if it has a door.
|
|
10
|
+
*/
|
|
11
|
+
export function servedEntities(app) {
|
|
12
|
+
const served = [];
|
|
13
|
+
for (const frond of app.fronds) {
|
|
14
|
+
const addresses = new Set(frond.handlers.filter((handler) => !handler.surface).map((handler) => handler.address));
|
|
15
|
+
for (const entity of frond.entities) {
|
|
16
|
+
if (addresses.has(entity.name))
|
|
17
|
+
served.push({ name: entity.name, entity: entity.entityClass });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return served;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The whole suite, for every entity, from a file that names none of them.
|
|
24
|
+
*
|
|
25
|
+
* The list comes from the scan rather than from imports, and that is the point: a list of
|
|
26
|
+
* imports is a second copy of what the project already declares, and it goes stale the day
|
|
27
|
+
* an entity is added — silently, because a missing test cannot fail.
|
|
28
|
+
*
|
|
29
|
+
* An entity whose frond is declared in `remotes:` is not here at all: it is not this app's
|
|
30
|
+
* to serve, and `testApp` does not follow the topology in the first place.
|
|
31
|
+
*/
|
|
32
|
+
export function checkAll(app, options = {}) {
|
|
33
|
+
const except = new Set(options.except ?? []);
|
|
34
|
+
const served = servedEntities(app).filter((one) => !except.has(one.name));
|
|
35
|
+
describe('what this app serves', () => {
|
|
36
|
+
it('is not empty — an empty suite proves nothing and still passes', () => {
|
|
37
|
+
expect(served.length).toBeGreaterThan(0);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
for (const { entity } of served) {
|
|
41
|
+
checkContract(app, entity, options);
|
|
42
|
+
checkOutput(app, entity, options);
|
|
43
|
+
if (options.doors !== false)
|
|
44
|
+
checkDoors(app, entity, options);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=all.js.map
|
package/dist/all.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all.js","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAqB,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAoB,MAAM,iBAAiB,CAAC;AAS/D;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,GAAQ;IACrC,MAAM,MAAM,GAA2C,EAAE,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QAClH,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAQ,EAAE,OAAO,GAAoB,EAAE;IAC9D,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAE1E,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;YACvE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QAChC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK;YAAE,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;AACH,CAAC"}
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type App } from '@fougere/core';
|
|
2
|
+
import { type Port, type Stub } from './stub.js';
|
|
3
|
+
export interface TestAppOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The project to scan. Read from the test file's own position when absent — a file
|
|
6
|
+
* under `fronds/blog/tests/` states that its subject is `blog` and that the project
|
|
7
|
+
* starts above `fronds/`.
|
|
8
|
+
*/
|
|
9
|
+
root?: string;
|
|
10
|
+
/** Boot only these fronds, by name. Deduced from the position when absent. */
|
|
11
|
+
fronds?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* The running test file, for hosts other than vitest. Vitest is read automatically
|
|
14
|
+
* through `expect.getState()`; anything else hands its own path in.
|
|
15
|
+
*/
|
|
16
|
+
testPath?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Where the rows go. `':memory:'` by default — a real SQLite engine, so the DDL, the
|
|
19
|
+
* `CHECK` constraints, `unique` and real transactions are all exercised, and nothing is
|
|
20
|
+
* written to disk.
|
|
21
|
+
*
|
|
22
|
+
* Not an in-memory ORM: `createMemoryOrm` realizes the axes but is not the engine
|
|
23
|
+
* production runs, and a tool whose thesis is *the realizations must agree* cannot pick
|
|
24
|
+
* the one that does not ship.
|
|
25
|
+
*/
|
|
26
|
+
db?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Follow `remotes:` from the config. False by default — a test that meant to exercise
|
|
29
|
+
* one frond should not silently reach for a process that is not running.
|
|
30
|
+
*/
|
|
31
|
+
topology?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Ports answered by a double instead of their realization. A double carries every
|
|
34
|
+
* method the port declares and returns nothing until the test says what it returns.
|
|
35
|
+
*/
|
|
36
|
+
stub?: Port[];
|
|
37
|
+
}
|
|
38
|
+
/** An app, plus the doubles standing in front of its ports and the facts it announced. */
|
|
39
|
+
export interface TestApp extends App {
|
|
40
|
+
/** The double installed for a port, to state a return or read what was called. */
|
|
41
|
+
stub<T>(port: abstract new (...args: never[]) => T): Stub<T>;
|
|
42
|
+
/**
|
|
43
|
+
* What was announced under a fact's name, in order.
|
|
44
|
+
*
|
|
45
|
+
* Read from the carrier's own seat: `Emissions.announce` hands EVERY announced fact to
|
|
46
|
+
* `onEmit`, whether or not anything in this process listens. So watching costs nothing
|
|
47
|
+
* and adds no second dispatcher — the one thing `Emissions` refuses to become.
|
|
48
|
+
*
|
|
49
|
+
* Its dual needs no help at all: `app.deliver(fact, payload)` is already the carrier's
|
|
50
|
+
* door, and it rejects when a subscriber refuses.
|
|
51
|
+
*/
|
|
52
|
+
announced(fact: {
|
|
53
|
+
name: string;
|
|
54
|
+
} | string): unknown[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* An app to ask questions of, wired the conventional way.
|
|
58
|
+
*
|
|
59
|
+
* Wraps `boot()`, which already loads the config, creates the container, sets the storage
|
|
60
|
+
* up and seeds — its own comment names tests among the surfaces it serves. What this adds
|
|
61
|
+
* is the two decisions a test makes differently: the rows live in memory, and the
|
|
62
|
+
* topology is not followed.
|
|
63
|
+
*
|
|
64
|
+
* Storage still goes through `resolveStorage`, the single place that knows a storage
|
|
65
|
+
* package; calling `setupSqlite` here would be a second one.
|
|
66
|
+
*/
|
|
67
|
+
export declare function testApp(options?: TestAppOptions): Promise<TestApp>;
|
|
68
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,EAAgB,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAI/D,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;CACf;AAED,0FAA0F;AAC1F,MAAM,WAAW,OAAQ,SAAQ,GAAG;IAClC,kFAAkF;IAClF,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D;;;;;;;;;OASG;IACH,SAAS,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC;CACvD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyC5E"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { boot } from '@fougere/core/node';
|
|
2
|
+
import { createContainer } from '@fougere/container';
|
|
3
|
+
import { resolveStorage } from '@fougere/defaults';
|
|
4
|
+
import { installStubs } from './stub.js';
|
|
5
|
+
import { scopeOfRun } from './scope.js';
|
|
6
|
+
import { registrationKeyOf } from '@fougere/schema';
|
|
7
|
+
/**
|
|
8
|
+
* An app to ask questions of, wired the conventional way.
|
|
9
|
+
*
|
|
10
|
+
* Wraps `boot()`, which already loads the config, creates the container, sets the storage
|
|
11
|
+
* up and seeds — its own comment names tests among the surfaces it serves. What this adds
|
|
12
|
+
* is the two decisions a test makes differently: the rows live in memory, and the
|
|
13
|
+
* topology is not followed.
|
|
14
|
+
*
|
|
15
|
+
* Storage still goes through `resolveStorage`, the single place that knows a storage
|
|
16
|
+
* package; calling `setupSqlite` here would be a second one.
|
|
17
|
+
*/
|
|
18
|
+
export async function testApp(options = {}) {
|
|
19
|
+
const db = { dialect: 'sqlite', path: options.db ?? ':memory:' };
|
|
20
|
+
// The position is consulted only for what the caller did not state: an explicit `root`
|
|
21
|
+
// or `fronds` wins, the way a config key wins over a convention everywhere else.
|
|
22
|
+
const scope = (options.root && options.fronds) ? undefined
|
|
23
|
+
: await scopeOfRun(options.testPath ?? currentTestPath());
|
|
24
|
+
// One entry per announcement, in order. `Emissions.announce` calls the carrier for
|
|
25
|
+
// every fact, so this sees them all — including those nobody in this process listens to.
|
|
26
|
+
const heard = [];
|
|
27
|
+
const app = await boot({
|
|
28
|
+
onEmit: (fact, payload) => { heard.push({ fact, payload }); },
|
|
29
|
+
root: options.root ?? scope?.root,
|
|
30
|
+
createContainer,
|
|
31
|
+
fronds: options.fronds ?? (scope?.frond ? [scope.frond] : undefined),
|
|
32
|
+
// `boot` merges this over the file, so a project declaring a real database still
|
|
33
|
+
// gets the in-memory one here.
|
|
34
|
+
config: options.topology ? undefined : { remotes: {} },
|
|
35
|
+
db: () => resolveStorage(db),
|
|
36
|
+
});
|
|
37
|
+
const doubles = installStubs(app, options.stub ?? []);
|
|
38
|
+
return Object.assign(app, {
|
|
39
|
+
announced(fact) {
|
|
40
|
+
// A fact travels under its REGISTRATION key — `postPublished`, not `PostPublished`
|
|
41
|
+
// — the same lowering every entity gets. Both spellings are accepted here and
|
|
42
|
+
// lowered before comparing, so a caller may hand in the class or the name.
|
|
43
|
+
const wanted = registrationKeyOf(typeof fact === 'string' ? fact : fact.name);
|
|
44
|
+
return heard.filter((one) => registrationKeyOf(one.fact) === wanted).map((one) => one.payload);
|
|
45
|
+
},
|
|
46
|
+
stub(port) {
|
|
47
|
+
const found = doubles.get(port);
|
|
48
|
+
if (!found) {
|
|
49
|
+
throw new Error(`[stub] ${port.name} was not stubbed — name it: testApp({ stub: [${port.name}] }).`);
|
|
50
|
+
}
|
|
51
|
+
return found;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vitest's own answer to "which file is running", or nothing.
|
|
57
|
+
*
|
|
58
|
+
* Imported dynamically: a package that imports vitest at the top level stops being
|
|
59
|
+
* loadable outside a test run, and `testApp` is meant to work under any host.
|
|
60
|
+
*/
|
|
61
|
+
function currentTestPath() {
|
|
62
|
+
const globals = globalThis;
|
|
63
|
+
return globals.__vitest_worker__?.filepath;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type App } from '@fougere/core';
|
|
2
|
+
import { type SchemaView } from '@fougere/schema';
|
|
3
|
+
import { type SampleOptions } from './sample.js';
|
|
4
|
+
export interface DoorOptions extends SampleOptions {
|
|
5
|
+
given?: Record<string, unknown>;
|
|
6
|
+
/** The audience, when the app serves named surfaces. */
|
|
7
|
+
surface?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DoorInput {
|
|
10
|
+
id?: string;
|
|
11
|
+
body?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface DoorContractCase {
|
|
14
|
+
/** What this case proves — becomes the test name. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Any declared operation, CRUD or custom. */
|
|
17
|
+
operation: string;
|
|
18
|
+
input?: DoorInput;
|
|
19
|
+
/** The protocol envelopes are removed before this value is compared. */
|
|
20
|
+
expected: unknown;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* One entity, four doors, the same answers.
|
|
24
|
+
*
|
|
25
|
+
* The claim runs through the whole repo — a frond runs in-process or behind JSON-RPC with
|
|
26
|
+
* identical user code, and REST, GraphQL and RPC are three projections of one contract —
|
|
27
|
+
* and nothing compared REST to GraphQL until now. `transport-swap.test.ts` compares three
|
|
28
|
+
* TRANSPORTS, which is a different sentence.
|
|
29
|
+
*
|
|
30
|
+
* The five CRUD operations, reads and writes. A CUSTOM op is not compared: REST addresses
|
|
31
|
+
* it by a path the table states, GraphQL by a mutation whose input type is its own, and
|
|
32
|
+
* matching the two means guessing which is which — a guess this file exists to avoid.
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkDoors(app: App, entity: SchemaView, options?: DoorOptions): void;
|
|
35
|
+
/**
|
|
36
|
+
* Run one hand-written invocation contract through every door.
|
|
37
|
+
*
|
|
38
|
+
* `checkDoors` derives the generic CRUD gradient. This is its small explicit companion
|
|
39
|
+
* for semantics only the handler can observe — notably omitted versus null input. A new
|
|
40
|
+
* adapter joins the same harness instead of inventing its own interpretation.
|
|
41
|
+
*/
|
|
42
|
+
export declare function checkDoorContract(app: App, entity: SchemaView, cases: readonly DoorContractCase[], options?: Pick<DoorOptions, 'surface'>): void;
|
|
43
|
+
//# sourceMappingURL=comparison.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison.d.ts","sourceRoot":"","sources":["../src/comparison.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AAG5D,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AA+B9D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AASD,MAAM,WAAW,SAAS;IAAG,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,WAAgB,GAAG,IAAI,CA8ExF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,SAAS,gBAAgB,EAAE,EAClC,OAAO,GAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAM,GACzC,IAAI,CAeN"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createLocalRunner } from '@fougere/core';
|
|
3
|
+
import { EMPTY_INVOCATION } from '@fougere/core/contract';
|
|
4
|
+
import { serveRest, serveRpc, tableOf } from '@fougere/app';
|
|
5
|
+
import { registrationKeyOf } from '@fougere/schema';
|
|
6
|
+
import { listQuery, findQuery, mutationFor, at } from './gql.js';
|
|
7
|
+
import { sampleInput } from './sample.js';
|
|
8
|
+
/**
|
|
9
|
+
* The rows a door hands back, with its own envelope taken off.
|
|
10
|
+
*
|
|
11
|
+
* Each door wraps differently by construction — REST answers a page, GraphQL nests under
|
|
12
|
+
* its field, RPC returns the value — and comparing the wrappers would compare the
|
|
13
|
+
* protocols. What must agree is what is inside.
|
|
14
|
+
*/
|
|
15
|
+
function rowsOf(value) {
|
|
16
|
+
if (Array.isArray(value))
|
|
17
|
+
return [...value];
|
|
18
|
+
const page = value;
|
|
19
|
+
return page && typeof page === 'object' && 'items' in page ? page.items : value;
|
|
20
|
+
}
|
|
21
|
+
/** Through the wire and back, so a `Date` and its ISO string are not read as a divergence. */
|
|
22
|
+
const wire = (value) => JSON.parse(JSON.stringify(value ?? null));
|
|
23
|
+
/**
|
|
24
|
+
* What is the SAME row seen twice, and what is merely a second row.
|
|
25
|
+
*
|
|
26
|
+
* A write creates a different row at each door — different id, different `createdAt` —
|
|
27
|
+
* so comparing values would compare clocks. The generated fields are dropped and what the
|
|
28
|
+
* caller sent is what remains, which is the part the doors must agree on.
|
|
29
|
+
*/
|
|
30
|
+
function written(value, sent) {
|
|
31
|
+
const row = value;
|
|
32
|
+
if (!row || typeof row !== 'object')
|
|
33
|
+
return wire(row);
|
|
34
|
+
return wire(Object.fromEntries(Object.keys(sent).map((key) => [key, row[key]])));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* One entity, four doors, the same answers.
|
|
38
|
+
*
|
|
39
|
+
* The claim runs through the whole repo — a frond runs in-process or behind JSON-RPC with
|
|
40
|
+
* identical user code, and REST, GraphQL and RPC are three projections of one contract —
|
|
41
|
+
* and nothing compared REST to GraphQL until now. `transport-swap.test.ts` compares three
|
|
42
|
+
* TRANSPORTS, which is a different sentence.
|
|
43
|
+
*
|
|
44
|
+
* The five CRUD operations, reads and writes. A CUSTOM op is not compared: REST addresses
|
|
45
|
+
* it by a path the table states, GraphQL by a mutation whose input type is its own, and
|
|
46
|
+
* matching the two means guessing which is which — a guess this file exists to avoid.
|
|
47
|
+
*/
|
|
48
|
+
export function checkDoors(app, entity, options = {}) {
|
|
49
|
+
const name = registrationKeyOf(entity.name ?? '');
|
|
50
|
+
const doors = doorsOf(app, entity, name, options.surface);
|
|
51
|
+
const bodyOf = () => sampleInput(entity, options.given ?? {}, options);
|
|
52
|
+
describe(`${entity.name} — the doors agree`, () => {
|
|
53
|
+
it('on create, over what the caller sent', async () => {
|
|
54
|
+
const sent = bodyOf();
|
|
55
|
+
const answers = await Promise.all(['local', 'rpc', 'rest', 'graphql'].map((door) => doors[door]('create', { body: sent })));
|
|
56
|
+
const [local, ...others] = answers.map((answer) => written(answer, sent));
|
|
57
|
+
for (const [index, other] of others.entries()) {
|
|
58
|
+
expect(other, `${['rpc', 'rest', 'graphql'][index]} ≠ local`).toEqual(local);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
it('on list', async () => {
|
|
62
|
+
await doors.local('create', { body: bodyOf() });
|
|
63
|
+
const local = wire(rowsOf(await doors.local('list')));
|
|
64
|
+
expect(Array.isArray(local) && local.length > 0, 'nothing to compare').toBe(true);
|
|
65
|
+
expect(wire(rowsOf(await doors.rpc('list'))), 'rpc ≠ local').toEqual(local);
|
|
66
|
+
expect(wire(rowsOf(await doors.rest('list'))), 'rest ≠ local').toEqual(local);
|
|
67
|
+
expect(wire(rowsOf(await doors.graphql('list'))), 'graphql ≠ local').toEqual(local);
|
|
68
|
+
});
|
|
69
|
+
it('on findById', async () => {
|
|
70
|
+
const row = await doors.local('create', { body: bodyOf() });
|
|
71
|
+
const local = wire(await doors.local('findById', { id: row.id }));
|
|
72
|
+
expect(wire(await doors.rpc('findById', { id: row.id })), 'rpc ≠ local').toEqual(local);
|
|
73
|
+
expect(wire(await doors.rest('findById', { id: row.id })), 'rest ≠ local').toEqual(local);
|
|
74
|
+
expect(wire(await doors.graphql('findById', { id: row.id })), 'graphql ≠ local').toEqual(local);
|
|
75
|
+
});
|
|
76
|
+
it('on update, over what the caller sent', async () => {
|
|
77
|
+
const rows = await Promise.all([1, 2, 3, 4].map(() => doors.local('create', { body: bodyOf() })));
|
|
78
|
+
const patch = bodyOf();
|
|
79
|
+
const answers = await Promise.all(['local', 'rpc', 'rest', 'graphql']
|
|
80
|
+
.map((door, index) => doors[door]('update', { id: rows[index].id, body: patch })));
|
|
81
|
+
const [local, ...others] = answers.map((answer) => written(answer, patch));
|
|
82
|
+
for (const [index, other] of others.entries()) {
|
|
83
|
+
expect(other, `${['rpc', 'rest', 'graphql'][index]} ≠ local`).toEqual(local);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
it('on delete', async () => {
|
|
87
|
+
const rows = await Promise.all([1, 2, 3, 4].map(() => doors.local('create', { body: bodyOf() })));
|
|
88
|
+
const answers = await Promise.all(['local', 'rpc', 'rest', 'graphql'].map((door, index) => doors[door]('delete', { id: rows[index].id })));
|
|
89
|
+
// REST answers a deletion with no content at all, which is the protocol saying yes.
|
|
90
|
+
const said = answers.map((answer) => (answer === undefined || answer === null ? true : wire(answer)));
|
|
91
|
+
expect(new Set(said).size, `the doors disagree: ${JSON.stringify(said)}`).toBe(1);
|
|
92
|
+
});
|
|
93
|
+
it('on a refusal', async () => {
|
|
94
|
+
// A refusal is where the doors diverge most, and where each is most tempted to
|
|
95
|
+
// answer in its own words. What must match is that it WAS refused.
|
|
96
|
+
const bad = { ...bodyOf(), __unknown__: 'x' };
|
|
97
|
+
const refusals = await Promise.all(['local', 'rpc', 'rest', 'graphql'].map((door) => refused(() => doors[door]('create', { body: bad }))));
|
|
98
|
+
expect(refusals[0], 'local accepted a body outside the contract').toBe(true);
|
|
99
|
+
expect(refusals, `the doors disagree: ${JSON.stringify(refusals)}`).toEqual([true, true, true, true]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Run one hand-written invocation contract through every door.
|
|
105
|
+
*
|
|
106
|
+
* `checkDoors` derives the generic CRUD gradient. This is its small explicit companion
|
|
107
|
+
* for semantics only the handler can observe — notably omitted versus null input. A new
|
|
108
|
+
* adapter joins the same harness instead of inventing its own interpretation.
|
|
109
|
+
*/
|
|
110
|
+
export function checkDoorContract(app, entity, cases, options = {}) {
|
|
111
|
+
const name = registrationKeyOf(entity.name ?? '');
|
|
112
|
+
const doors = doorsOf(app, entity, name, options.surface);
|
|
113
|
+
const names = ['local', 'rpc', 'rest', 'graphql'];
|
|
114
|
+
describe(`${entity.name} — its invocation contract crosses every door`, () => {
|
|
115
|
+
for (const one of cases) {
|
|
116
|
+
it(one.name, async () => {
|
|
117
|
+
const answers = await Promise.all(names.map((door) => doors[door](one.operation, one.input)));
|
|
118
|
+
for (const [index, answer] of answers.entries()) {
|
|
119
|
+
expect(wire(answer), `${names[index]} disagrees with the canonical invocation`).toEqual(wire(one.expected));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async function refused(call) {
|
|
126
|
+
try {
|
|
127
|
+
await call();
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** The four doors, each reduced to `(op, input) => answer` so the tests above read alike. */
|
|
135
|
+
function doorsOf(app, entity, name, surface) {
|
|
136
|
+
const run = createLocalRunner(app, surface);
|
|
137
|
+
const state = {};
|
|
138
|
+
const invocation = (input = {}) => ({
|
|
139
|
+
...EMPTY_INVOCATION,
|
|
140
|
+
...(input.id !== undefined ? { params: { id: input.id } } : {}),
|
|
141
|
+
...(input.body !== undefined ? { body: input.body } : {}),
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
local: (op, input) => run({ entity: name, op }, invocation(input)),
|
|
145
|
+
rpc: async (op, input) => {
|
|
146
|
+
const answer = await serveRpc(app, {
|
|
147
|
+
path: '',
|
|
148
|
+
body: { jsonrpc: '2.0', id: 1, method: `${name}.${op}`, params: invocation(input) },
|
|
149
|
+
state,
|
|
150
|
+
});
|
|
151
|
+
if (answer.error)
|
|
152
|
+
throw new Error(answer.error.message);
|
|
153
|
+
return answer.result;
|
|
154
|
+
},
|
|
155
|
+
rest: async (op, input) => {
|
|
156
|
+
// The route the REST door itself would match, read from its own table — rebuilding
|
|
157
|
+
// the path here would be a second opinion on where an entity lives.
|
|
158
|
+
const route = tableOf(app).find((one) => one.entityName === name && one.operationName === op);
|
|
159
|
+
if (!route)
|
|
160
|
+
throw new Error(`[checkDoors] REST serves no ${name}.${op}`);
|
|
161
|
+
const path = route.segments.map((segment) => (segment.startsWith(':') ? input?.id ?? '' : segment)).join('/');
|
|
162
|
+
const answer = await serveRest(app, { method: route.method, path, query: {}, body: input?.body, state });
|
|
163
|
+
if (answer.kind !== 'ok')
|
|
164
|
+
throw new Error(`[checkDoors] REST answered ${answer.kind} on ${name}.${op}`);
|
|
165
|
+
return answer.body;
|
|
166
|
+
},
|
|
167
|
+
graphql: async (op, input) => {
|
|
168
|
+
const { executeOn, schemaOf } = await import('@fougere/adapter-graphql');
|
|
169
|
+
const schema = schemaOf(app);
|
|
170
|
+
const built = op === 'list' ? listQuery(schema, entity)
|
|
171
|
+
: op === 'findById' ? findQuery(schema, entity, input?.id ?? '')
|
|
172
|
+
: mutationFor(schema, entity, op, { id: input?.id, body: input?.body });
|
|
173
|
+
if (!built)
|
|
174
|
+
throw new Error(`[checkDoors] GraphQL serves no ${entity.name} ${op}`);
|
|
175
|
+
const answer = await executeOn(app, { query: built.query, state });
|
|
176
|
+
if (answer.errors?.length)
|
|
177
|
+
throw new Error(`[checkDoors] GraphQL: ${answer.errors[0].message}`);
|
|
178
|
+
return at(answer.data, built.at);
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=comparison.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison.js","sourceRoot":"","sources":["../src/comparison.ts"],"names":[],"mappings":"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"}
|
package/dist/derive.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Case } from '@fougere/schema';
|
|
2
|
+
import type { SchemaView } from '@fougere/schema';
|
|
3
|
+
import { type SampleOptions } from './sample.js';
|
|
4
|
+
/**
|
|
5
|
+
* The cases, with the valid body generated rather than handed in.
|
|
6
|
+
*
|
|
7
|
+
* `casesFor` lives in `@fougere/schema` because deriving them reads the four axes and
|
|
8
|
+
* nothing else. This is the half that needs a generator, which is why it is here: a
|
|
9
|
+
* 426 KB faker has no business in the package a browser loads.
|
|
10
|
+
*/
|
|
11
|
+
export declare function derivedCases(entity: SchemaView, given?: Record<string, unknown>, options?: SampleOptions): Case[];
|
|
12
|
+
//# sourceMappingURL=derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,OAAO,GAAE,aAAkB,GAC1B,IAAI,EAAE,CAER"}
|
package/dist/derive.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { casesFor } from '@fougere/schema';
|
|
2
|
+
import { sampleInput } from './sample.js';
|
|
3
|
+
/**
|
|
4
|
+
* The cases, with the valid body generated rather than handed in.
|
|
5
|
+
*
|
|
6
|
+
* `casesFor` lives in `@fougere/schema` because deriving them reads the four axes and
|
|
7
|
+
* nothing else. This is the half that needs a generator, which is why it is here: a
|
|
8
|
+
* 426 KB faker has no business in the package a browser loads.
|
|
9
|
+
*/
|
|
10
|
+
export function derivedCases(entity, given = {}, options = {}) {
|
|
11
|
+
return casesFor(entity, sampleInput(entity, given, options));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAkB,EAClB,KAAK,GAA4B,EAAE,EACnC,OAAO,GAAkB,EAAE;IAE3B,OAAO,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D,CAAC"}
|
package/dist/doors.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type App } from '@fougere/core';
|
|
2
|
+
import { type SchemaView, type ValidationError } from '@fougere/schema';
|
|
3
|
+
import { type SampleOptions } from './sample.js';
|
|
4
|
+
/** The one shape both the local judge and a door already speak. */
|
|
5
|
+
export interface Verdict {
|
|
6
|
+
success: boolean;
|
|
7
|
+
errors?: ValidationError[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* What a door answers, in the shape a verdict is compared in.
|
|
11
|
+
*
|
|
12
|
+
* A refusal reaches a caller as a thrown `FougereError` carrying `details`, not as a
|
|
13
|
+
* returned value — so the translation happens once, here, and every reader below compares
|
|
14
|
+
* the same thing.
|
|
15
|
+
*/
|
|
16
|
+
export declare function verdictOf(call: () => Promise<unknown>): Promise<Verdict>;
|
|
17
|
+
export interface CheckOptions extends SampleOptions {
|
|
18
|
+
/** Values the generator cannot invent — the id of a row a `ref()` points at. */
|
|
19
|
+
given?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The declared contract, posed to the façade that will receive it.
|
|
23
|
+
*
|
|
24
|
+
* Declares one `it` per case rather than looping inside a single one: a failure names the
|
|
25
|
+
* case, and a suite that lists what it checked is the point — the list comes from the
|
|
26
|
+
* entity, not from what the author remembered.
|
|
27
|
+
*/
|
|
28
|
+
export declare function checkContract(app: App, entity: SchemaView, options?: CheckOptions): void;
|
|
29
|
+
/**
|
|
30
|
+
* What may leave, checked against what the entity says may leave.
|
|
31
|
+
*
|
|
32
|
+
* Costs nothing to state: `outputFields` already answers it, and a field the boundary
|
|
33
|
+
* closes has no business in any response. A `password: text({ boundary: 'writeOnly' })`
|
|
34
|
+
* that reaches a caller is the one leak no reviewer catches by reading a handler.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkOutput(app: App, entity: SchemaView, options?: CheckOptions): void;
|
|
37
|
+
//# sourceMappingURL=doors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doors.d.ts","sourceRoot":"","sources":["../src/doors.ts"],"names":[],"mappings":"AACA,OAAO,EAAyC,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AAEhF,OAAO,EAAmC,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGzG,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1E,mEAAmE;AACnE,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAS9E;AAOD,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI,CAiB5F;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI,CA2B1F"}
|