@aventara/testing 0.1.0-pilot.3 → 0.1.0-pilot.4

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.
@@ -1,3 +1,3 @@
1
1
  import type { Adapter, AdapterModel } from "@aventara/core";
2
- /** Stable construction seam consumed by reusable adapter conformance suites. */
2
+ /** Builds the Adapter under test; reusable adapter conformance suites take one. */
3
3
  export type AdapterFactory<M extends AdapterModel = AdapterModel> = () => Adapter<M> | Promise<Adapter<M>>;
@@ -1,28 +1,42 @@
1
1
  import type { Adapter, AdapterModel, AdapterOperation } from "@aventara/core";
2
+ /** Answers one operation the fake Adapter is handed. */
2
3
  export type FakeAdapterExecute<M extends AdapterModel> = (operation: AdapterOperation<M>) => unknown | Promise<unknown>;
4
+ /** How a transaction ended: `"committed"` or `"rolled-back"`. */
3
5
  export type FakeTransactionOutcome = "committed" | "rolled-back";
4
6
  /** One settled transaction boundary: what ran inside it, and how it ended. */
5
7
  export type FakeTransactionTrace<M extends AdapterModel> = {
8
+ /** The operations run inside the transaction, in order. */
6
9
  readonly operations: readonly AdapterOperation<M>[];
10
+ /** How the transaction ended. */
7
11
  readonly outcome: FakeTransactionOutcome;
8
12
  };
9
13
  /**
10
- * Real rollback for caller-owned state: snapshot on begin, restore when the
11
- * work rejects. Without it the fake Adapter records boundaries but persists
12
- * whatever the work wrote, so a rollback assertion would prove nothing.
14
+ * Rollback for state the test owns: `snapshot` when a transaction begins,
15
+ * `restore` when its work rejects. Without it, the fake Adapter records
16
+ * transaction boundaries but keeps whatever the work wrote.
13
17
  */
14
18
  export type FakeAdapterRollback<S> = {
19
+ /** Captures the state before a transaction's work runs. */
15
20
  readonly snapshot: () => S;
21
+ /** Puts the captured state back after the work rejects. */
16
22
  readonly restore: (snapshot: S) => void;
17
23
  };
24
+ /** Options for {@link createFakeAdapter}. */
18
25
  export type FakeAdapterOptions<M extends AdapterModel, S = unknown> = {
26
+ /** The adapter model the fake Adapter reports. */
19
27
  readonly model: M;
28
+ /** Answers each operation; without it, every operation resolves `undefined`. */
20
29
  readonly execute?: FakeAdapterExecute<M>;
30
+ /** Makes transactions roll back the test's own state; see {@link FakeAdapterRollback}. */
21
31
  readonly rollback?: FakeAdapterRollback<S>;
22
32
  };
33
+ /**
34
+ * The Adapter {@link createFakeAdapter} returns, with a record of every
35
+ * operation and transaction it ran.
36
+ */
23
37
  export type FakeAdapter<M extends AdapterModel = AdapterModel> = Adapter<M> & {
24
38
  readonly operations: readonly AdapterOperation<M>[];
25
39
  readonly transactions: readonly FakeTransactionTrace<M>[];
26
40
  };
27
- /** Reusable provider-free Adapter implementation for framework/conformance tests. */
41
+ /** A provider-free, in-memory Adapter for framework and conformance tests. */
28
42
  export declare function createFakeAdapter<M extends AdapterModel, S = unknown>(options: FakeAdapterOptions<M, S>): FakeAdapter<M>;
@@ -3,14 +3,15 @@ import { Decimal } from "@aventara/core";
3
3
  /**
4
4
  * The corpus's adapter model:
5
5
  *
6
- * - `authors` — every standard operation (family 2); its `secret` field is hidden
7
- * at client scope;
8
- * - `books` — references `authors`, and deleting an author cascades into it
9
- * ({@link CORPUS_REFERENTIAL_ACTIONS}; a plan's `V1019`); the client scope adds
10
- * a computed `label` to it;
11
- * - `secrets` — its one operation restricted away at client scope;
12
- * - `hiddenRes` — hidden at client scope;
13
- * - `samples` — one field of each scalar whose wire form is not its runtime form.
6
+ * - `authors`: every standard operation; its `secret` field is hidden at
7
+ * client scope;
8
+ * - `books`: references `authors`, and deleting an author cascades into it
9
+ * ({@link CORPUS_REFERENTIAL_ACTIONS}; a plan's `V1019`); the client scope
10
+ * adds a computed `label` to it ({@link CORPUS_CLIENT_CONFIG});
11
+ * - `secrets`: its one operation restricted away at client scope;
12
+ * - `hiddenRes`: hidden at client scope;
13
+ * - `samples`: one field of each scalar whose wire form is not its runtime
14
+ * form (`bigint`, `datetime`, `decimal`, `bytes`).
14
15
  */
15
16
  export declare const CORPUS_MODEL: {
16
17
  readonly transactions: "interactive";
@@ -278,8 +279,7 @@ export declare const CORPUS_REFERENTIAL_ACTIONS: {
278
279
  }];
279
280
  };
280
281
  /**
281
- * The `where.id` a row sends to make the corpus Framework fail one way
282
- * (families 10 and 12, "a fake adapter returning the code, labelled so"):
282
+ * The `where.id` a row sends to make the corpus Framework fail one way:
283
283
  *
284
284
  * - `A4000`, `A4001`, `A4002`, `A3000` — the client guard throws
285
285
  * `FrameworkError("A4000")`, denies (`false`), throws
@@ -314,7 +314,7 @@ declare function corpusGuard(context: PipelineContext): boolean;
314
314
  declare function corpusPipe(args: unknown): unknown;
315
315
  /**
316
316
  * The corpus's client-scope configuration: what the ClientContract hides or
317
- * restricts, and `books.label` — a VIRTUAL field added at client scope and
317
+ * restricts, and `books.label`, a VIRTUAL field added at client scope and
318
318
  * computed on read from `id`.
319
319
  */
320
320
  export declare const CORPUS_CLIENT_CONFIG: {
@@ -372,24 +372,20 @@ export declare const CORPUS_CLIENT_CONFIG: {
372
372
  export declare const CORPUS_ENTRYPOINT = "/api";
373
373
  /**
374
374
  * The corpus ClientContract's hash: what a generated client for it sends as
375
- * `Aventara-Contract-Hash`. A literal, as a generated client holds it;
376
- * `corpus-contract.spec.ts` fails, naming this constant, when the compiled
377
- * contract moves.
375
+ * `Aventara-Contract-Hash`. A literal, as a generated client holds it.
378
376
  */
379
377
  export declare const CORPUS_CLIENT_CONTRACT_HASH = "sha256:07b1a75a6e8e88e30115ae81bcf7e3a12fa493bddce995a01867e2381bd158d0";
380
378
  /**
381
379
  * The corpus ClientContract as `GET /_contract` serves it: its JCS-canonical
382
380
  * bytes, `protocol.hash` included. A literal, as a host must serve it byte for
383
- * byte; `corpus-contract.spec.ts` fails, naming this constant, when the compiled
384
- * contract moves.
381
+ * byte.
385
382
  */
386
383
  export declare const CORPUS_CLIENT_CONTRACT_DOCUMENT = "{\"enums\":{},\"limits\":{\"maxBooleanNodes\":50,\"maxListLimit\":250,\"maxNestingDepth\":12,\"maxRequestBytes\":1048576,\"maxTransactionOperations\":20},\"protocol\":{\"hash\":\"sha256:07b1a75a6e8e88e30115ae81bcf7e3a12fa493bddce995a01867e2381bd158d0\",\"version\":1},\"resources\":{\"authors\":{\"fields\":{\"id\":{\"capabilities\":{\"filter\":[\"equals\"],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}},\"name\":{\"capabilities\":{\"create\":[],\"filter\":[\"equals\"],\"select\":true,\"update\":[]},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}}},\"identifiers\":[[\"id\"]],\"operations\":{\"create\":{\"count\":true,\"many\":true,\"one\":true},\"delete\":{\"count\":true,\"first\":true,\"many\":true,\"unique\":true},\"find\":{\"count\":true,\"first\":true,\"many\":true,\"unique\":true},\"update\":{\"count\":true,\"first\":true,\"many\":true,\"unique\":true},\"upsert\":{\"unique\":true}}},\"books\":{\"fields\":{\"authorId\":{\"capabilities\":{\"filter\":[\"equals\"],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}},\"id\":{\"capabilities\":{\"filter\":[\"equals\"],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}},\"label\":{\"capabilities\":{\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[\"COMPUTED_ON_READ\",\"VIRTUAL\"],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}}},\"identifiers\":[[\"id\"]],\"operations\":{\"find\":{\"many\":true}}},\"samples\":{\"fields\":{\"amount\":{\"capabilities\":{\"create\":[],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"decimal\"}},\"at\":{\"capabilities\":{\"create\":[],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"datetime\"}},\"blob\":{\"capabilities\":{\"create\":[],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"bytes\"}},\"count\":{\"capabilities\":{\"create\":[],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"bigint\"}},\"id\":{\"capabilities\":{\"filter\":[\"equals\"],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}}},\"identifiers\":[[\"id\"]],\"operations\":{\"create\":{\"one\":true},\"find\":{\"unique\":true}}},\"secrets\":{\"fields\":{\"id\":{\"capabilities\":{\"filter\":[\"equals\"],\"select\":true},\"kind\":\"scalar\",\"lifecycle\":[],\"list\":false,\"nullable\":false,\"type\":{\"scalar\":\"string\"}}},\"identifiers\":[[\"id\"]],\"operations\":{}}},\"scalars\":{\"bigint\":{\"builtin\":true},\"bytes\":{\"builtin\":true},\"datetime\":{\"builtin\":true},\"decimal\":{\"builtin\":true},\"string\":{\"builtin\":true}},\"transactions\":\"interactive\"}";
387
384
  /**
388
- * The hash of the corpus's SECOND ClientContract — the same model and
389
- * configuration with `transactions: "none"` (family 6's `/_transactions` absence).
390
- * A row targets it by sending this hash, exactly as a client generated against it
391
- * would; the runner gives the row the Framework whose ClientContract it names. A
392
- * literal, held to the compiled value by `corpus-contract.spec.ts`.
385
+ * The hash of the corpus's second ClientContract: the same model and
386
+ * configuration with `transactions: "none"` (the `/_transactions` absence
387
+ * rows). A row targets it by sending this hash, exactly as a client generated
388
+ * against it would.
393
389
  */
394
390
  export declare const CORPUS_NONE_CLIENT_CONTRACT_HASH = "sha256:d333e8396b9bf9127e0859ed69b9874f230058a597e2129e6b9e9b380daef618";
395
391
  /** The one record the corpus Adapter knows. */
@@ -1,10 +1,8 @@
1
1
  import type { AvProtocolDriver } from "./protocol-driver.js";
2
2
  /**
3
- * Driver 1: the protocol hosted in process, with no HTTP stack — a catch-all host
4
- * that hands `/_contract` to `protocol.encodeContract`, `/_transactions` to the
5
- * bound shortcut `protocol.handleTransaction`, every other request to
6
- * `protocol.handleOperation`, and writes back exactly what they answer. What it
7
- * proves is the framework's own answer, host-free; driver 2 must answer every row
8
- * identically through a real server.
3
+ * The protocol hosted in process, with no HTTP stack: `/_contract` goes to
4
+ * `protocol.encodeContract`, `/_transactions` to `protocol.handleTransaction`,
5
+ * every other request to `protocol.handleOperation`, and the answer is written
6
+ * back as it is.
9
7
  */
10
8
  export declare const inProcessDriver: AvProtocolDriver;
@@ -1,11 +1,10 @@
1
1
  import type { AvProtocolDriver } from "./protocol-driver.js";
2
2
  /**
3
- * The per-route driver: the protocol hosted in process as a host that mounts ONE
4
- * route per `protocol.surface()` entry — each operation on `handleOperation`,
3
+ * The protocol hosted in process as a host that mounts one route per
4
+ * `protocol.surface()` entry (each operation on `handleOperation`,
5
5
  * `/_transactions` on `handleTransaction`, `/_contract` on `encodeContract`,
6
- * matched by method and path — and `protocol.answerAbsent` as the fallback for
7
- * everything it did not mount. `undefined` from the fallback is a path outside the
8
- * protocol, which this host answers with its own bodiless 404. Every corpus row
9
- * must answer here exactly as through driver 1's catch-all.
6
+ * matched by method and path) and uses `protocol.answerAbsent` as the fallback
7
+ * for everything it did not mount. `undefined` from the fallback is a path
8
+ * outside the protocol, which this host answers with its own bodiless 404.
10
9
  */
11
10
  export declare const perRouteDriver: AvProtocolDriver;
@@ -1,29 +1,30 @@
1
1
  import type { AvProtocolDriver } from "./protocol-driver.js";
2
2
  /**
3
- * `runAvProtocolConformance(driver)` — the corpus, driven through one host.
3
+ * `runAvProtocolConformance(driver)` — drives the protocol fixture corpus
4
+ * through one host.
4
5
  *
5
- * Every row a host answers is driven — the rows whose `client.outcome` is
6
+ * Every row a host answers is driven; rows whose `client.outcome` is
6
7
  * `"transport-error"` describe what a client receives instead of a framework
7
8
  * answer, so they are skipped. Each driven row runs against a fresh corpus
8
- * Framework, through a fresh driver session, with one request — the corpus
9
- * Framework whose ClientContract hash the row's `Aventara-Contract-Hash` names, as
10
- * a generated client names it — and its answer is held to the row's `server`
11
- * block:
9
+ * Framework, through a fresh driver session, with one request (the corpus
10
+ * Framework whose ClientContract hash the row's `Aventara-Contract-Hash`
11
+ * names; the main corpus contract for any other hash), and its answer is
12
+ * held to the row's `server` block:
12
13
  *
13
14
  * - `status`, exactly;
14
- * - `code`: the body is a framework envelope (`AvProtocol.isOperationResponse`,
15
- * the one envelope test) carrying that code — or, for `null`, is none;
15
+ * - `code`: the body is a framework envelope carrying that code — or, for
16
+ * `null`, is none;
16
17
  * - `causeKeys`: the exact sorted key set of `cause`;
17
18
  * - `issues`: each issue's `code` and `path`, in order;
18
19
  * - `adapterCalls`: the operations the corpus Adapter was handed, exactly;
19
- * - `responseHeaders`: each named header present (names matched without regard to
20
- * case) with exactly that value — a host may add its own;
20
+ * - `responseHeaders`: each named header present (names matched without
21
+ * regard to case) with exactly that value — a host may add its own;
21
22
  * - `bodyBytes`: the body text, exactly.
22
23
  *
23
- * Resolves one result per driven row, in corpus order, each with the mismatches
24
- * found — none when the host conforms. A driver that throws is a mismatch of its
25
- * row, not a rejection of the run. Test-runner free: a caller asserts on the
26
- * results with whatever runner it uses.
24
+ * Resolves one result per driven row, in corpus order, each with the
25
+ * mismatches found — none when the host conforms. A driver that throws is a
26
+ * mismatch of its row, not a rejection of the run. Works with any test
27
+ * runner: assert on the results with whatever runner you use.
27
28
  */
28
29
  export declare function runAvProtocolConformance(driver: AvProtocolDriver): Promise<readonly {
29
30
  readonly label: string;
@@ -1,14 +1,11 @@
1
1
  import type { Framework } from "@aventara/core";
2
2
  import type { AvProtocolRequest, AvProtocolResponse } from "@aventara/core/protocol";
3
3
  /**
4
- * One host under test, as the protocol conformance run drives it
5
- * (`runAvProtocolConformance`): given the corpus's Framework, mount the protocol
6
- * the way that host does, then answer each corpus request as that host would
7
- * answer it over HTTP — status, headers and body text, as an `AvProtocolResponse`.
8
- *
9
- * Driver 1 (`in-process.driver.ts`) calls the bound protocol's shortcuts directly;
10
- * a real host sends the request over its own stack and reads back what arrived.
11
- * The run calls `close` once per row, after its one request.
4
+ * One host under test, as `runAvProtocolConformance` drives it: given the
5
+ * corpus's Framework, mount the protocol the way that host does, then answer
6
+ * each corpus request as that host would answer it over HTTP (status, headers
7
+ * and body text, as an `AvProtocolResponse`). The run calls `close` once per
8
+ * row, after its one request.
12
9
  */
13
10
  export type AvProtocolDriver = (framework: Framework) => Promise<{
14
11
  readonly send: (request: AvProtocolRequest) => Promise<AvProtocolResponse>;
@@ -1,25 +1,27 @@
1
1
  import type { OperationCode, ValidationCode } from "@aventara/core";
2
2
  import type { AvProtocolRequest } from "@aventara/core/protocol";
3
3
  /**
4
- * Types only; the rows live in `protocol-fixtures.ts`.
4
+ * One row of the protocol fixture corpus: the HTTP request a host receives
5
+ * (`request`), what the host must answer (`server`), and what a generated
6
+ * client must do with that answer (`client`).
5
7
  *
6
- * `request` is core's own request value, `AvProtocolRequest` from
7
- * `@aventara/core/protocol`, so a row is exactly what a host hands the bound
8
- * protocol: no second request shape exists for the corpus to drift from.
8
+ * `request` is an `AvProtocolRequest` from `@aventara/core/protocol`, exactly
9
+ * what a host hands the bound protocol.
9
10
  */
10
11
  export type AvProtocolFixture = {
11
12
  /**
12
- * A row that covers one of the specification's success rows begins with that row's
13
- * operation name (`find.many …`, `create.one …`) or with `transaction` for the
14
- * committed-plan row.
13
+ * The row's name. A row for a standard operation's success begins with that
14
+ * operation's name (`find.many …`, `create.one …`), or with `transaction` for
15
+ * the committed-plan row.
15
16
  */
16
17
  readonly label: string;
17
18
  /** Method, entrypoint-relative path, headers, and a raw or parsed body. */
18
19
  readonly request: AvProtocolRequest;
20
+ /** What the host must answer. */
19
21
  readonly server: {
20
22
  /**
21
- * The HTTP status. `0` only on a transport-error row (family 11) where no
22
- * HTTP response arrived at all — the Fetch API's network-error status.
23
+ * The HTTP status. `0` only on a transport-error row, where no HTTP
24
+ * response arrived at all (the Fetch API's network-error status).
23
25
  */
24
26
  readonly status: number;
25
27
  /** `null`: the host produces no framework envelope at all. */
@@ -36,11 +38,12 @@ export type AvProtocolFixture = {
36
38
  /** The exact body, where bytes matter. */
37
39
  readonly bodyBytes?: string;
38
40
  };
41
+ /** What a generated client must do with the answer. */
39
42
  readonly client: {
40
43
  /**
41
44
  * What a generated client does with the answer. `"transport-error"`
42
- * also marks a row no host produces (family 11): a host driver filters
43
- * on `client.outcome === "transport-error"` and does not drive those rows.
45
+ * also marks a row no host produces: a host driver filters on
46
+ * `client.outcome === "transport-error"` and does not drive those rows.
44
47
  */
45
48
  readonly outcome: "resolve" | "framework-error" | "transport-error";
46
49
  /** Present iff `outcome` is `"framework-error"`. */
@@ -1,2 +1,7 @@
1
1
  import type { AvProtocolFixture } from "./protocol-fixture.js";
2
+ /**
3
+ * The protocol fixture corpus: passive data rows, each an HTTP request, the
4
+ * answer a host must give and what a generated client must do with it. Drive
5
+ * them through a host with `runAvProtocolConformance`.
6
+ */
2
7
  export declare const AvProtocolFixtures: readonly AvProtocolFixture[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aventara/testing",
3
- "version": "0.1.0-pilot.3",
3
+ "version": "0.1.0-pilot.4",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "Conformance and extension testing utilities for Aventara.",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "LICENSE-ADDITIONAL-PERMISSION.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@aventara/core": "0.1.0-pilot.3"
23
+ "@aventara/core": "0.1.0-pilot.4"
24
24
  },
25
25
  "publishConfig": {
26
26
  "access": "public"