@metamesh/testkit 0.0.0-stage → 0.14.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +234 -0
  2. package/LICENSE +200 -0
  3. package/README.md +118 -2
  4. package/dist/assertions.d.ts +20 -0
  5. package/dist/assertions.d.ts.map +1 -0
  6. package/dist/assertions.js +103 -0
  7. package/dist/assertions.js.map +1 -0
  8. package/dist/docs/guides/package.md +119 -0
  9. package/dist/docs/manifest.json +259 -0
  10. package/dist/docs/reference/font-fixtures/LoadedFontFixtures.md +13 -0
  11. package/dist/docs/reference/font-fixtures/loadFontFixtureServices.md +10 -0
  12. package/dist/docs/reference/font-fixtures/loadFontFixtures.md +10 -0
  13. package/dist/docs/reference/fonts/FontTestFixture.md +17 -0
  14. package/dist/docs/reference/fonts/prepareFontTestServices.md +10 -0
  15. package/dist/docs/reference/index/GeneratorTestExecution.md +19 -0
  16. package/dist/docs/reference/index/GeneratorTestRunner.md +19 -0
  17. package/dist/docs/reference/index/GeneratorTestRunnerOptions.md +14 -0
  18. package/dist/docs/reference/index/MeshMeasurementExpectation.md +19 -0
  19. package/dist/docs/reference/index/MeshMeasurementTolerances.md +17 -0
  20. package/dist/docs/reference/index/createGeneratorTestRunner.md +12 -0
  21. package/dist/docs/reference/index/expectMeshMeasurements.md +14 -0
  22. package/dist/docs/reference/index/expectSerializableDefinition.md +10 -0
  23. package/dist/docs/reference/index/expectValidDefaults.md +10 -0
  24. package/dist/docs/reference/index/expectValidParts.md +19 -0
  25. package/dist/docs/reference/index/expectValidPresets.md +10 -0
  26. package/dist/docs/reference/index/getTestEngine.md +13 -0
  27. package/dist/engine.d.ts +7 -0
  28. package/dist/engine.d.ts.map +1 -0
  29. package/dist/engine.js +11 -0
  30. package/dist/engine.js.map +1 -0
  31. package/dist/font-fixtures.d.ts +11 -0
  32. package/dist/font-fixtures.d.ts.map +1 -0
  33. package/dist/font-fixtures.js +75 -0
  34. package/dist/font-fixtures.js.map +1 -0
  35. package/dist/fonts.d.ts +13 -0
  36. package/dist/fonts.d.ts.map +1 -0
  37. package/dist/fonts.js +34 -0
  38. package/dist/fonts.js.map +1 -0
  39. package/dist/index.d.ts +7 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +5 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/measurements.d.ts +28 -0
  44. package/dist/measurements.d.ts.map +1 -0
  45. package/dist/measurements.js +66 -0
  46. package/dist/measurements.js.map +1 -0
  47. package/dist/runner.d.ts +36 -0
  48. package/dist/runner.d.ts.map +1 -0
  49. package/dist/runner.js +118 -0
  50. package/dist/runner.js.map +1 -0
  51. package/package.json +55 -3
@@ -0,0 +1,103 @@
1
+ import { parseGeneratorDefinition, validateGeneratorPresets, } from "@metamesh/generator-sdk/packages";
2
+ import { createParamCodec, getDefaultParameters } from "@metamesh/generator-sdk/parameters";
3
+ import { parseGeneratedParts } from "@metamesh/generator-sdk/runtime";
4
+ import { generate } from "@metamesh/modeling";
5
+ import { getTestEngine } from "./engine.js";
6
+ function stringify(value, label) {
7
+ try {
8
+ const serialized = JSON.stringify(value);
9
+ if (serialized === undefined)
10
+ throw new TypeError(`${label} is not JSON-serializable.`);
11
+ return serialized;
12
+ }
13
+ catch (cause) {
14
+ throw new TypeError(`${label} is not JSON-serializable.`, { cause });
15
+ }
16
+ }
17
+ function sortJson(value) {
18
+ if (Array.isArray(value))
19
+ return value.map(sortJson);
20
+ if (typeof value !== "object" || value === null)
21
+ return value;
22
+ return Object.fromEntries(Object.entries(value)
23
+ .sort(([left], [right]) => left.localeCompare(right))
24
+ .map(([key, entry]) => [key, sortJson(entry)]));
25
+ }
26
+ function stableStringify(value, label) {
27
+ return stringify(sortJson(value), label);
28
+ }
29
+ /** Throws unless a definition already has the canonical package-serializable representation. */
30
+ export function expectSerializableDefinition(definition) {
31
+ parseGeneratorDefinition(definition);
32
+ const decoded = JSON.parse(stringify(definition, "Generator definition"));
33
+ const parsed = parseGeneratorDefinition(decoded);
34
+ if (stableStringify(parsed, "Parsed Generator definition") !==
35
+ stableStringify(decoded, "Generator definition")) {
36
+ throw new TypeError("Generator definition must already use its canonical serialized form.");
37
+ }
38
+ }
39
+ /** Throws unless defaults strictly decode and equal the defaults derived from the definition. */
40
+ export function expectValidDefaults(definition, defaults) {
41
+ const parsed = createParamCodec(definition).parse(defaults);
42
+ if (stableStringify(parsed, "Generator defaults") !==
43
+ stableStringify(getDefaultParameters(definition), "Derived Generator defaults")) {
44
+ throw new TypeError("Generator defaults do not match their definition.");
45
+ }
46
+ }
47
+ /** Checks preset metadata. Validate domain assertions through the generation test runner. */
48
+ export function expectValidPresets(definition, presets) {
49
+ validateGeneratorPresets(definition, presets);
50
+ }
51
+ export function expectValidParts(value, engine) {
52
+ if (engine === undefined)
53
+ return getTestEngine().then((host) => expectValidParts(value, host));
54
+ const wireValue = Array.isArray(value)
55
+ ? value.map((part, index) => {
56
+ if (typeof part !== "object" || part === null || !Object.hasOwn(part, "measurements"))
57
+ return part;
58
+ const prototype = Object.getPrototypeOf(part);
59
+ if (prototype !== null && prototype !== Object.prototype)
60
+ return part;
61
+ const descriptors = Object.getOwnPropertyDescriptors(part);
62
+ if (Object.keys(descriptors).some((key) => ![
63
+ "id",
64
+ "positions",
65
+ "indices",
66
+ "color",
67
+ "measurements",
68
+ "geometry",
69
+ "position",
70
+ "rotation",
71
+ ].includes(key)))
72
+ return part;
73
+ // Captured engine snapshots expose owned buffer copies through accessors.
74
+ descriptors["positions"] = { value: part.positions, enumerable: true };
75
+ descriptors["indices"] = { value: part.indices, enumerable: true };
76
+ delete descriptors["position"];
77
+ delete descriptors["rotation"];
78
+ delete descriptors["measurements"];
79
+ delete descriptors["geometry"];
80
+ return Object.defineProperties({
81
+ geometryId: `assertion-${index}`,
82
+ geometryRevision: 0,
83
+ position: [0, 0, 0],
84
+ rotation: [0, 0, 0],
85
+ }, descriptors);
86
+ })
87
+ : value;
88
+ const parts = parseGeneratedParts(wireValue, engine.limits);
89
+ const session = engine.createSession(generate(({ part }) => parts.map((mesh) => part(mesh.id, {
90
+ geometry: mesh,
91
+ position: mesh.position,
92
+ rotation: mesh.rotation,
93
+ ...(mesh.color === undefined ? {} : { color: mesh.color }),
94
+ }))));
95
+ try {
96
+ session.run({}).dispose();
97
+ }
98
+ finally {
99
+ session.dispose();
100
+ }
101
+ return parts;
102
+ }
103
+ //# sourceMappingURL=assertions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ # `@metamesh/testkit`
2
+
3
+ Test MetaMesh models and Generators with the same generation engine used by their hosts. Check definitions,
4
+ presets, mesh validity, and physical measurements with any test runner.
5
+
6
+ > **Preview:** `0.x` minor releases may break compatibility. Patches remain compatible within the
7
+ > same minor line.
8
+
9
+ ## Installation
10
+
11
+ Install the same exact SDK, Modeling, and Testkit release. Testkit is a development dependency:
12
+
13
+ ```bash
14
+ pnpm add @metamesh/generator-sdk @metamesh/modeling
15
+ pnpm add -D @metamesh/testkit
16
+ ```
17
+
18
+ ## Execute models in tests
19
+
20
+ Given a Generator module exporting its `definition` and default runtime, such as the
21
+ [SDK example](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/README.md#define-controls):
22
+
23
+ ```ts
24
+ import { getDefaultParameters } from "@metamesh/generator-sdk/parameters";
25
+ import { createGeneratorTestRunner, expectValidParts } from "@metamesh/testkit";
26
+
27
+ import runtime, { definition } from "./generator.js";
28
+
29
+ const runner = createGeneratorTestRunner(definition, runtime);
30
+ try {
31
+ const result = await runner.execute(getDefaultParameters(definition));
32
+ if (result.type !== "generated") throw new Error("Expected valid Generator output.");
33
+ try {
34
+ await expectValidParts(result.model.parts);
35
+ } finally {
36
+ result.model.dispose();
37
+ }
38
+ } finally {
39
+ runner.dispose();
40
+ }
41
+ ```
42
+
43
+ `execute` normalizes values, prepares services, and runs a persistent session. Schema and assertion
44
+ errors return `invalid`; unexpected execution or malformed-output errors reject. Dispose returned
45
+ snapshots separately from the runner; captured snapshots remain valid after runner disposal.
46
+
47
+ Use `runner.validate(parameters, { normalize: false })` to probe deliberately invalid typed values
48
+ without finalizing parts. It checks declarations and reached assertions; observing geometry can
49
+ still initialize the engine. Creating a runner alone loads no engine or assets. Pass `options.engine`
50
+ to reuse a test-owned engine and `options.limits` to lower host ceilings.
51
+
52
+ ## Assertions
53
+
54
+ Helpers throw on failure without depending on your test framework:
55
+
56
+ | Helper | Checks |
57
+ | ------------------------------ | ------------------------------------------------------------------ |
58
+ | `expectSerializableDefinition` | Strict parsing and a canonical JSON round trip |
59
+ | `expectValidDefaults` | Strict parameter decoding and agreement with definition defaults |
60
+ | `expectValidPresets` | Serializable preset metadata; use the runner for domain assertions |
61
+ | `expectValidParts` | Strict part metadata, closed topology, and native reconstruction |
62
+ | `expectMeshMeasurements` | Bounds, area, signed volume, and connected-component count |
63
+
64
+ `expectValidParts(parts, engine)` is synchronous and uses that engine's limits. Without an engine,
65
+ it returns a promise using the shared test engine and default limits. It also accepts finalized
66
+ parts, stripping measurement facts before strict validation; other unknown fields reject.
67
+
68
+ Compare physical measurements with explicit tolerances. For a 30 × 20 × 4 mm box mesh:
69
+
70
+ ```ts
71
+ import { expectMeshMeasurements } from "@metamesh/testkit";
72
+
73
+ expectMeshMeasurements(
74
+ mesh,
75
+ { signedVolume: 30 * 20 * 4, connectedComponentCount: 1 },
76
+ { signedVolume: 0.05 }
77
+ );
78
+ ```
79
+
80
+ Bounds use mm, area mm², and volume mm³. Tolerances are absolute and default to zero; component
81
+ counts are exact. Measurements check closed oriented topology without a native engine. Matching
82
+ measurements alone do not establish geometric equivalence or exclude self-intersections; use
83
+ `expectValidParts` for native reconstruction.
84
+
85
+ ## Font fixtures
86
+
87
+ Supply `prepareServices` in runner options for models with fonts. It receives the definition and
88
+ current parameters, and can return services synchronously or asynchronously. Missing fixtures or
89
+ service failures reject before generation.
90
+
91
+ `prepareFontTestServices` from `/fonts` checks immutable font revisions and prepares services from
92
+ checked-in bytes. Install Modeling's optional `fontkit` peer, plus `woff2-encoder` for WOFF2 fixtures.
93
+ Tests need no font downloads or browser cache; retain the fixtures' licenses.
94
+
95
+ For local JSON fixture descriptors, `loadFontFixtureServices` from
96
+ `@metamesh/testkit/font-fixtures` returns the runner's `prepareServices` adapter. Descriptor paths
97
+ resolve beside the JSON file, and every file's SHA-256 must match its declared selection revision.
98
+ `loadFontFixtures` additionally returns the verified selections. These loaders are Node-only;
99
+ use `/fonts` when the host already supplies the bytes.
100
+
101
+ ## Documentation
102
+
103
+ Resolve `@metamesh/testkit/docs/manifest.json` for this installed version's Markdown
104
+ reference and testing guidance. Testkit has no compiler, bundler, renderer, or browser dependencies.
105
+ It shares the Modeling engine and Generator SDK contracts rather than duplicating runtime rules.
106
+
107
+ For source checking, captured runs, documentation lookup, and images, install
108
+ [`@metamesh/cli`](https://github.com/JasonKoolman/metamesh/blob/main/packages/cli/README.md).
109
+ Its executable is `metamesh`; reusable Node authoring functions live in `@metamesh/cli/authoring`.
110
+
111
+ - [Generator SDK](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/README.md) — definitions, parameter values, and module authoring.
112
+ - [Modeling](https://github.com/JasonKoolman/metamesh/blob/main/packages/modeling/README.md) — geometry, sessions, and snapshots.
113
+ - [API source](https://github.com/JasonKoolman/metamesh/blob/main/packages/testkit/src/index.ts) — runner and assertion exports; [font helpers](https://github.com/JasonKoolman/metamesh/blob/main/packages/testkit/src/fonts.ts) live in `/fonts`.
114
+
115
+ For package development, see [Contributing](https://github.com/JasonKoolman/metamesh/blob/main/CONTRIBUTING.md).
116
+
117
+ ## License
118
+
119
+ [Apache-2.0](https://github.com/JasonKoolman/metamesh/blob/main/packages/testkit/LICENSE).
@@ -0,0 +1,259 @@
1
+ {
2
+ "formatVersion": 1,
3
+ "packageName": "@metamesh/testkit",
4
+ "packageVersion": "0.14.0",
5
+ "documents": [
6
+ {
7
+ "id": "testkit/guide/testing",
8
+ "title": "Test models and Generators",
9
+ "summary": "Runner-neutral MetaMesh model and Generator testing helpers, assertions, and local font fixtures.",
10
+ "path": "guides/package.md",
11
+ "importPath": "@metamesh/testkit",
12
+ "availability": "Local Node testing",
13
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
14
+ "keywords": [
15
+ "guide",
16
+ "installation",
17
+ "testkit"
18
+ ]
19
+ },
20
+ {
21
+ "id": "testkit/reference/font-fixtures/LoadedFontFixtures",
22
+ "title": "LoadedFontFixtures",
23
+ "summary": "Byte-verified local font selections and a reusable Generator service-preparation adapter.",
24
+ "path": "reference/font-fixtures/LoadedFontFixtures.md",
25
+ "importPath": "@metamesh/testkit/font-fixtures",
26
+ "availability": "Local Node testing",
27
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
28
+ "keywords": [
29
+ "reference",
30
+ "font-fixtures",
31
+ "LoadedFontFixtures"
32
+ ]
33
+ },
34
+ {
35
+ "id": "testkit/reference/font-fixtures/loadFontFixtures",
36
+ "title": "loadFontFixtures",
37
+ "summary": "Node-only local descriptor loader. Paths resolve beside the descriptor; bytes never come from a catalog.",
38
+ "path": "reference/font-fixtures/loadFontFixtures.md",
39
+ "importPath": "@metamesh/testkit/font-fixtures",
40
+ "availability": "Local Node testing",
41
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
42
+ "keywords": [
43
+ "reference",
44
+ "font-fixtures",
45
+ "loadFontFixtures"
46
+ ]
47
+ },
48
+ {
49
+ "id": "testkit/reference/font-fixtures/loadFontFixtureServices",
50
+ "title": "loadFontFixtureServices",
51
+ "summary": "Fixture provider matching createGeneratorTestRunner's asynchronous service boundary.",
52
+ "path": "reference/font-fixtures/loadFontFixtureServices.md",
53
+ "importPath": "@metamesh/testkit/font-fixtures",
54
+ "availability": "Local Node testing",
55
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
56
+ "keywords": [
57
+ "reference",
58
+ "font-fixtures",
59
+ "loadFontFixtureServices"
60
+ ]
61
+ },
62
+ {
63
+ "id": "testkit/reference/fonts/FontTestFixture",
64
+ "title": "FontTestFixture",
65
+ "summary": "A checked-in immutable font fixture; no catalog or network is used by the test harness.",
66
+ "path": "reference/fonts/FontTestFixture.md",
67
+ "importPath": "@metamesh/testkit/fonts",
68
+ "availability": "Local Node testing",
69
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
70
+ "keywords": [
71
+ "reference",
72
+ "fonts",
73
+ "FontTestFixture"
74
+ ]
75
+ },
76
+ {
77
+ "id": "testkit/reference/fonts/prepareFontTestServices",
78
+ "title": "prepareFontTestServices",
79
+ "summary": "Prepares the same synchronous text service used by Studio, from reproducible fixture bytes.",
80
+ "path": "reference/fonts/prepareFontTestServices.md",
81
+ "importPath": "@metamesh/testkit/fonts",
82
+ "availability": "Local Node testing",
83
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
84
+ "keywords": [
85
+ "reference",
86
+ "fonts",
87
+ "prepareFontTestServices"
88
+ ]
89
+ },
90
+ {
91
+ "id": "testkit/reference/index/createGeneratorTestRunner",
92
+ "title": "createGeneratorTestRunner",
93
+ "summary": "Creates a lazy runner for one generation program without loading assets or an engine.",
94
+ "path": "reference/index/createGeneratorTestRunner.md",
95
+ "importPath": "@metamesh/testkit",
96
+ "availability": "Local Node testing",
97
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
98
+ "keywords": [
99
+ "reference",
100
+ "index",
101
+ "createGeneratorTestRunner"
102
+ ]
103
+ },
104
+ {
105
+ "id": "testkit/reference/index/expectMeshMeasurements",
106
+ "title": "expectMeshMeasurements",
107
+ "summary": "Analyzes current buffers, requires closed oriented edge topology, and checks selected physical facts with explicit absolute tolerances. Returns fresh measurements without initializing an engine. These facts do not prove geometric equivalence or absence of self-intersections.",
108
+ "path": "reference/index/expectMeshMeasurements.md",
109
+ "importPath": "@metamesh/testkit",
110
+ "availability": "Local Node testing",
111
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
112
+ "keywords": [
113
+ "reference",
114
+ "index",
115
+ "expectMeshMeasurements"
116
+ ]
117
+ },
118
+ {
119
+ "id": "testkit/reference/index/expectSerializableDefinition",
120
+ "title": "expectSerializableDefinition",
121
+ "summary": "Throws unless a definition already has the canonical package-serializable representation.",
122
+ "path": "reference/index/expectSerializableDefinition.md",
123
+ "importPath": "@metamesh/testkit",
124
+ "availability": "Local Node testing",
125
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
126
+ "keywords": [
127
+ "reference",
128
+ "index",
129
+ "expectSerializableDefinition"
130
+ ]
131
+ },
132
+ {
133
+ "id": "testkit/reference/index/expectValidDefaults",
134
+ "title": "expectValidDefaults",
135
+ "summary": "Throws unless defaults strictly decode and equal the defaults derived from the definition.",
136
+ "path": "reference/index/expectValidDefaults.md",
137
+ "importPath": "@metamesh/testkit",
138
+ "availability": "Local Node testing",
139
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
140
+ "keywords": [
141
+ "reference",
142
+ "index",
143
+ "expectValidDefaults"
144
+ ]
145
+ },
146
+ {
147
+ "id": "testkit/reference/index/expectValidParts",
148
+ "title": "expectValidParts",
149
+ "summary": "Checks finalized engine parts after omitting measurement facts from their strict wire DTOs. Runs mesh reconstruction/finalization synchronously using the supplied engine. Strictly checks unknown wire data and reconstructs its meshes using the supplied engine. Initializes the shared test engine lazily, then applies the same wire and mesh checks.",
150
+ "path": "reference/index/expectValidParts.md",
151
+ "importPath": "@metamesh/testkit",
152
+ "availability": "Local Node testing",
153
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
154
+ "keywords": [
155
+ "reference",
156
+ "index",
157
+ "expectValidParts"
158
+ ]
159
+ },
160
+ {
161
+ "id": "testkit/reference/index/expectValidPresets",
162
+ "title": "expectValidPresets",
163
+ "summary": "Checks preset metadata. Validate domain assertions through the generation test runner.",
164
+ "path": "reference/index/expectValidPresets.md",
165
+ "importPath": "@metamesh/testkit",
166
+ "availability": "Local Node testing",
167
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
168
+ "keywords": [
169
+ "reference",
170
+ "index",
171
+ "expectValidPresets"
172
+ ]
173
+ },
174
+ {
175
+ "id": "testkit/reference/index/GeneratorTestExecution",
176
+ "title": "GeneratorTestExecution",
177
+ "summary": "Parameter or assertion failure, or a generated snapshot owned separately from its runner.",
178
+ "path": "reference/index/GeneratorTestExecution.md",
179
+ "importPath": "@metamesh/testkit",
180
+ "availability": "Local Node testing",
181
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
182
+ "keywords": [
183
+ "reference",
184
+ "index",
185
+ "GeneratorTestExecution"
186
+ ]
187
+ },
188
+ {
189
+ "id": "testkit/reference/index/GeneratorTestRunner",
190
+ "title": "GeneratorTestRunner",
191
+ "summary": "A lazy reusable Generator test session; dispose the runner and every returned snapshot separately.",
192
+ "path": "reference/index/GeneratorTestRunner.md",
193
+ "importPath": "@metamesh/testkit",
194
+ "availability": "Local Node testing",
195
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
196
+ "keywords": [
197
+ "reference",
198
+ "index",
199
+ "GeneratorTestRunner"
200
+ ]
201
+ },
202
+ {
203
+ "id": "testkit/reference/index/GeneratorTestRunnerOptions",
204
+ "title": "GeneratorTestRunnerOptions",
205
+ "summary": "Fixed host resources and optional asynchronous asset preparation.",
206
+ "path": "reference/index/GeneratorTestRunnerOptions.md",
207
+ "importPath": "@metamesh/testkit",
208
+ "availability": "Local Node testing",
209
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
210
+ "keywords": [
211
+ "reference",
212
+ "index",
213
+ "GeneratorTestRunnerOptions"
214
+ ]
215
+ },
216
+ {
217
+ "id": "testkit/reference/index/getTestEngine",
218
+ "title": "getTestEngine",
219
+ "summary": "Returns the shared test engine, initializing it only on first request. Runner construction does not call this function.",
220
+ "path": "reference/index/getTestEngine.md",
221
+ "importPath": "@metamesh/testkit",
222
+ "availability": "Local Node testing",
223
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
224
+ "keywords": [
225
+ "reference",
226
+ "index",
227
+ "getTestEngine"
228
+ ]
229
+ },
230
+ {
231
+ "id": "testkit/reference/index/MeshMeasurementExpectation",
232
+ "title": "MeshMeasurementExpectation",
233
+ "summary": "Physical expectations for a closed mesh; omitted facts are not asserted.",
234
+ "path": "reference/index/MeshMeasurementExpectation.md",
235
+ "importPath": "@metamesh/testkit",
236
+ "availability": "Local Node testing",
237
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
238
+ "keywords": [
239
+ "reference",
240
+ "index",
241
+ "MeshMeasurementExpectation"
242
+ ]
243
+ },
244
+ {
245
+ "id": "testkit/reference/index/MeshMeasurementTolerances",
246
+ "title": "MeshMeasurementTolerances",
247
+ "summary": "Absolute nonnegative comparison allowances; each omitted tolerance defaults to zero.",
248
+ "path": "reference/index/MeshMeasurementTolerances.md",
249
+ "importPath": "@metamesh/testkit",
250
+ "availability": "Local Node testing",
251
+ "canonical": "https://metamesh.studio/docs/develop/agent-authoring#test-models",
252
+ "keywords": [
253
+ "reference",
254
+ "index",
255
+ "MeshMeasurementTolerances"
256
+ ]
257
+ }
258
+ ]
259
+ }
@@ -0,0 +1,13 @@
1
+ # LoadedFontFixtures
2
+
3
+ Byte-verified local font selections and a reusable Generator service-preparation adapter.
4
+
5
+ Import from `@metamesh/testkit/font-fixtures` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Byte-verified local font selections and a reusable Generator service-preparation adapter. */
9
+ export interface LoadedFontFixtures {
10
+ readonly selections: readonly FontSelection[];
11
+ readonly prepareServices: (definition: GeneratorDefinition, parameters: Readonly<Record<string, unknown>>) => Promise<GeneratorServices>;
12
+ }
13
+ ```
@@ -0,0 +1,10 @@
1
+ # loadFontFixtureServices
2
+
3
+ Fixture provider matching createGeneratorTestRunner's asynchronous service boundary.
4
+
5
+ Import from `@metamesh/testkit/font-fixtures` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Fixture provider matching createGeneratorTestRunner's asynchronous service boundary. */
9
+ export declare function loadFontFixtureServices(filename: string): Promise<LoadedFontFixtures["prepareServices"]>;
10
+ ```
@@ -0,0 +1,10 @@
1
+ # loadFontFixtures
2
+
3
+ Node-only local descriptor loader. Paths resolve beside the descriptor; bytes never come from a catalog.
4
+
5
+ Import from `@metamesh/testkit/font-fixtures` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Node-only local descriptor loader. Paths resolve beside the descriptor; bytes never come from a catalog. */
9
+ export declare function loadFontFixtures(filename: string): Promise<LoadedFontFixtures>;
10
+ ```
@@ -0,0 +1,17 @@
1
+ # FontTestFixture
2
+
3
+ A checked-in immutable font fixture; no catalog or network is used by the test harness.
4
+
5
+ Import from `@metamesh/testkit/fonts` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** A checked-in immutable font fixture; no catalog or network is used by the test harness. */
9
+ export interface FontTestFixture {
10
+ /** Font face, weight and SHA-256 revision to resolve from these bytes. */
11
+ readonly selection: FontSelection;
12
+ /** Complete checked-in font file; its digest must match selection.revision. */
13
+ readonly bytes: Uint8Array<ArrayBuffer>;
14
+ /** Weight represented by a static face; omit for variable fonts. */
15
+ readonly staticWeight?: number;
16
+ }
17
+ ```
@@ -0,0 +1,10 @@
1
+ # prepareFontTestServices
2
+
3
+ Prepares the same synchronous text service used by Studio, from reproducible fixture bytes.
4
+
5
+ Import from `@metamesh/testkit/fonts` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Prepares the same synchronous text service used by Studio, from reproducible fixture bytes. */
9
+ export declare function prepareFontTestServices(fixtures: readonly FontTestFixture[]): Promise<GeneratorServices>;
10
+ ```
@@ -0,0 +1,19 @@
1
+ # GeneratorTestExecution
2
+
3
+ Parameter or assertion failure, or a generated snapshot owned separately from its runner.
4
+
5
+ Import from `@metamesh/testkit` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Parameter or assertion failure, or a generated snapshot owned separately from its runner. */
9
+ export type GeneratorTestExecution<D extends GeneratorDefinition> = {
10
+ readonly type: "invalid";
11
+ readonly parameters: GeneratorParams<D>;
12
+ readonly validation: ParameterValidationResult;
13
+ } | {
14
+ readonly type: "generated";
15
+ readonly parameters: GeneratorParams<D>;
16
+ readonly validation: ParameterValidationResult;
17
+ readonly model: ModelSnapshot;
18
+ };
19
+ ```
@@ -0,0 +1,19 @@
1
+ # GeneratorTestRunner
2
+
3
+ A lazy reusable Generator test session; dispose the runner and every returned snapshot separately.
4
+
5
+ Import from `@metamesh/testkit` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** A lazy reusable Generator test session; dispose the runner and every returned snapshot separately. */
9
+ export interface GeneratorTestRunner<D extends GeneratorDefinition> {
10
+ /** Checks declarations and assertions only. Set `normalize: false` to probe deliberately invalid typed values. */
11
+ readonly validate: (parameters: GeneratorParams<D>, options?: {
12
+ readonly normalize?: boolean;
13
+ }) => Promise<ParameterValidationResult>;
14
+ /** Normalizes once, prepares assets, and executes the same persistent session used by production hosts. */
15
+ readonly execute: (parameters: Readonly<Record<string, unknown>>) => Promise<GeneratorTestExecution<D>>;
16
+ /** Releases runner caches; separately owned snapshots remain valid. */
17
+ readonly dispose: () => void;
18
+ }
19
+ ```
@@ -0,0 +1,14 @@
1
+ # GeneratorTestRunnerOptions
2
+
3
+ Fixed host resources and optional asynchronous asset preparation.
4
+
5
+ Import from `@metamesh/testkit` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Fixed host resources and optional asynchronous asset preparation. */
9
+ export interface GeneratorTestRunnerOptions<D extends GeneratorDefinition> {
10
+ readonly prepareServices?: (definition: D, parameters: GeneratorParams<D>) => GeneratorServices | Promise<GeneratorServices>;
11
+ readonly engine?: ModelEngine;
12
+ readonly limits?: Partial<ModelingLimits>;
13
+ }
14
+ ```
@@ -0,0 +1,19 @@
1
+ # MeshMeasurementExpectation
2
+
3
+ Physical expectations for a closed mesh; omitted facts are not asserted.
4
+
5
+ Import from `@metamesh/testkit` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Physical expectations for a closed mesh; omitted facts are not asserted. */
9
+ export interface MeshMeasurementExpectation {
10
+ /** Axis-aligned XYZ bounds in millimetres. */
11
+ readonly bounds?: Bounds3;
12
+ /** Oriented enclosed volume in cubic millimetres; outward winding is positive. */
13
+ readonly signedVolume?: number;
14
+ /** Sum of triangle areas in square millimetres. */
15
+ readonly surfaceArea?: number;
16
+ /** Exact number of components connected by indexed mesh edges. */
17
+ readonly connectedComponentCount?: number;
18
+ }
19
+ ```
@@ -0,0 +1,17 @@
1
+ # MeshMeasurementTolerances
2
+
3
+ Absolute nonnegative comparison allowances; each omitted tolerance defaults to zero.
4
+
5
+ Import from `@metamesh/testkit` (@metamesh/testkit@0.14.0).
6
+
7
+ ```ts
8
+ /** Absolute nonnegative comparison allowances; each omitted tolerance defaults to zero. */
9
+ export interface MeshMeasurementTolerances {
10
+ /** Allowance for each minimum/maximum XYZ coordinate in millimetres. */
11
+ readonly bounds?: number;
12
+ /** Allowance for signed volume in cubic millimetres. */
13
+ readonly signedVolume?: number;
14
+ /** Allowance for surface area in square millimetres. */
15
+ readonly surfaceArea?: number;
16
+ }
17
+ ```