@entifix/testing-unit 0.4.2 → 0.5.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/README.md +1 -1
- package/dist/contracts/entity-repository.contract.d.ts.map +1 -1
- package/dist/contracts/entity-repository.contract.js +32 -32
- package/dist/contracts/entity-repository.contract.js.map +1 -1
- package/dist/doubles/configuration.d.ts +0 -2
- package/dist/doubles/configuration.d.ts.map +1 -1
- package/dist/doubles/configuration.js +0 -1
- package/dist/doubles/configuration.js.map +1 -1
- package/dist/effect/run.d.ts +5 -10
- package/dist/effect/run.d.ts.map +1 -1
- package/dist/effect/run.js +5 -9
- package/dist/effect/run.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ protocol does not.
|
|
|
24
24
|
## Entry points
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
-
import { makeInMemoryEntityRepository,
|
|
27
|
+
import { makeInMemoryEntityRepository, run } from '@entifix/testing-unit';
|
|
28
28
|
import { makeFakeMongoDb } from '@entifix/testing-unit/drivers';
|
|
29
29
|
import { entityRestHandlers, setupEntifixServer } from '@entifix/testing-unit/http';
|
|
30
30
|
import { describeEntityRepositoryContract } from '@entifix/testing-unit/contracts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-repository.contract.d.ts","sourceRoot":"","sources":["../../src/contracts/entity-repository.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAY,KAAK,MAAM,EAAU,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAM7E;;;;GAIG;AACH,qBACa,cAAe,YAAW,MAAM;;IAO3C,IACI,EAAE,IAAI,QAAQ,CAEjB;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,EAErB;IAED,IACI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEjC;IAED,IACI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEjC;IAED,IACI,IAAI,IAAI,SAAS,MAAM,EAAE,CAE5B;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAEhC;IAED,IACI,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAExD;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,EAE5D;CACF;AAED,eAAO,MAAM,kBAAkB,GAC7B,IAAI,QAAQ,EACZ,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,OAAM,SAAS,MAAM,EAAO,KAC3B,cAOF,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,cAAc,CACZ,IAAI,EAAE,cAAc,EAAE,GACrB,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACjD;AAED;;;;;GAKG;AACH,eAAO,MAAM,oCAAoC,GAC/C,MAAM,MAAM,EACZ,oBAAoB,+BAA+B,KAClD,
|
|
1
|
+
{"version":3,"file":"entity-repository.contract.d.ts","sourceRoot":"","sources":["../../src/contracts/entity-repository.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAY,KAAK,MAAM,EAAU,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAM7E;;;;GAIG;AACH,qBACa,cAAe,YAAW,MAAM;;IAO3C,IACI,EAAE,IAAI,QAAQ,CAEjB;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,EAErB;IAED,IACI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEjC;IAED,IACI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEjC;IAED,IACI,IAAI,IAAI,SAAS,MAAM,EAAE,CAE5B;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAEhC;IAED,IACI,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAExD;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,EAE5D;CACF;AAED,eAAO,MAAM,kBAAkB,GAC7B,IAAI,QAAQ,EACZ,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,OAAM,SAAS,MAAM,EAAO,KAC3B,cAOF,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,cAAc,CACZ,IAAI,EAAE,cAAc,EAAE,GACrB,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACjD;AAED;;;;;GAKG;AACH,eAAO,MAAM,oCAAoC,GAC/C,MAAM,MAAM,EACZ,oBAAoB,+BAA+B,KAClD,IAmOF,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,qCAAqC,GAChD,MAAM,MAAM,EACZ,oBAAoB,+BAA+B,KAClD,IA0FF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,GAC3C,MAAM,MAAM,EACZ,SAAS,+BAA+B,KACvC,IAGF,CAAC"}
|
|
@@ -5,7 +5,7 @@ var _dec, _initClass, _dec1, _dec2, _dec3, _dec4, _dec5, _initProto;
|
|
|
5
5
|
import { accessor, entity } from '@entifix/core';
|
|
6
6
|
import { Exit } from 'effect';
|
|
7
7
|
import { describe, expect, it } from 'vitest';
|
|
8
|
-
import {
|
|
8
|
+
import { run, runExit } from '../effect/run.js';
|
|
9
9
|
_dec = entity({
|
|
10
10
|
key: 'contract-widget'
|
|
11
11
|
}), _dec1 = accessor({
|
|
@@ -132,19 +132,19 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
132
132
|
};
|
|
133
133
|
it('keeps a record unchanged when a read instance is mutated without a save', async ()=>{
|
|
134
134
|
await withRepository(async (repository)=>{
|
|
135
|
-
const read = await
|
|
135
|
+
const read = await run(repository.get('w-1'));
|
|
136
136
|
read.name = 'Mutated';
|
|
137
|
-
const again = await
|
|
137
|
+
const again = await run(repository.get('w-1'));
|
|
138
138
|
expect(again.name).toBe('Alpha');
|
|
139
|
-
const page = await
|
|
139
|
+
const page = await run(repository.load({}));
|
|
140
140
|
page.items[0].name = 'Mutated';
|
|
141
|
-
const reloaded = await
|
|
141
|
+
const reloaded = await run(repository.load({}));
|
|
142
142
|
expect(reloaded.items[0].name).toBe('Alpha');
|
|
143
143
|
});
|
|
144
144
|
});
|
|
145
145
|
it('loads every stored entity with its total', async ()=>{
|
|
146
146
|
await withRepository(async (repository)=>{
|
|
147
|
-
const page = await
|
|
147
|
+
const page = await run(repository.load({}));
|
|
148
148
|
expect(page.total).toBe(3);
|
|
149
149
|
expect(page.items.map((item)=>item.name)).toEqual([
|
|
150
150
|
'Alpha',
|
|
@@ -159,14 +159,14 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
159
159
|
page: 1,
|
|
160
160
|
pageSize: 2
|
|
161
161
|
};
|
|
162
|
-
const page = await
|
|
162
|
+
const page = await run(repository.load(request));
|
|
163
163
|
expect(page.request).toEqual(request);
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
166
|
it('pages with a 1-based page number', async ()=>{
|
|
167
167
|
await withRepository(async (repository)=>{
|
|
168
168
|
var _page_items_;
|
|
169
|
-
const page = await
|
|
169
|
+
const page = await run(repository.load({
|
|
170
170
|
page: 2,
|
|
171
171
|
pageSize: 2
|
|
172
172
|
}));
|
|
@@ -179,7 +179,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
179
179
|
it('filters by an equality operator', async ()=>{
|
|
180
180
|
await withRepository(async (repository)=>{
|
|
181
181
|
var _page_items_;
|
|
182
|
-
const page = await
|
|
182
|
+
const page = await run(repository.load({
|
|
183
183
|
filtering: [
|
|
184
184
|
{
|
|
185
185
|
property: 'name',
|
|
@@ -195,7 +195,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
195
195
|
});
|
|
196
196
|
it('filters by a range operator', async ()=>{
|
|
197
197
|
await withRepository(async (repository)=>{
|
|
198
|
-
const page = await
|
|
198
|
+
const page = await run(repository.load({
|
|
199
199
|
filtering: [
|
|
200
200
|
{
|
|
201
201
|
property: 'size',
|
|
@@ -213,7 +213,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
213
213
|
});
|
|
214
214
|
it('matches `like` case-insensitively on a substring', async ()=>{
|
|
215
215
|
await withRepository(async (repository)=>{
|
|
216
|
-
const page = await
|
|
216
|
+
const page = await run(repository.load({
|
|
217
217
|
filtering: [
|
|
218
218
|
{
|
|
219
219
|
property: 'name',
|
|
@@ -229,7 +229,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
229
229
|
});
|
|
230
230
|
it('matches `in` and `nin` against any element of an array member', async ()=>{
|
|
231
231
|
await withRepository(async (repository)=>{
|
|
232
|
-
const tagged = await
|
|
232
|
+
const tagged = await run(repository.load({
|
|
233
233
|
filtering: [
|
|
234
234
|
{
|
|
235
235
|
property: 'tags',
|
|
@@ -245,7 +245,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
245
245
|
'w-1',
|
|
246
246
|
'w-3'
|
|
247
247
|
]);
|
|
248
|
-
const untagged = await
|
|
248
|
+
const untagged = await run(repository.load({
|
|
249
249
|
filtering: [
|
|
250
250
|
{
|
|
251
251
|
property: 'tags',
|
|
@@ -281,7 +281,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
281
281
|
};
|
|
282
282
|
await withRepository(async (repository)=>{
|
|
283
283
|
var _sorted_items_;
|
|
284
|
-
const found = await
|
|
284
|
+
const found = await run(repository.load({
|
|
285
285
|
filtering: [
|
|
286
286
|
{
|
|
287
287
|
property: 'title.es',
|
|
@@ -293,7 +293,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
293
293
|
expect(found.items.map((item)=>item.id)).toEqual([
|
|
294
294
|
'w-2'
|
|
295
295
|
]);
|
|
296
|
-
const sorted = await
|
|
296
|
+
const sorted = await run(repository.load({
|
|
297
297
|
sorting: [
|
|
298
298
|
{
|
|
299
299
|
0: {
|
|
@@ -329,7 +329,7 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
329
329
|
});
|
|
330
330
|
it('sorts descending when asked to', async ()=>{
|
|
331
331
|
await withRepository(async (repository)=>{
|
|
332
|
-
const page = await
|
|
332
|
+
const page = await run(repository.load({
|
|
333
333
|
sorting: [
|
|
334
334
|
{
|
|
335
335
|
0: {
|
|
@@ -348,20 +348,20 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
348
348
|
});
|
|
349
349
|
it('reads a single entity by id', async ()=>{
|
|
350
350
|
await withRepository(async (repository)=>{
|
|
351
|
-
const found = await
|
|
351
|
+
const found = await run(repository.get('w-2'));
|
|
352
352
|
expect(found.id).toBe('w-2');
|
|
353
353
|
expect(found.name).toBe('Beta');
|
|
354
354
|
});
|
|
355
355
|
});
|
|
356
356
|
it('fails rather than resolving undefined for an unknown id', async ()=>{
|
|
357
357
|
await withRepository(async (repository)=>{
|
|
358
|
-
const exit = await
|
|
358
|
+
const exit = await runExit(repository.get('missing'));
|
|
359
359
|
expect(Exit.isFailure(exit)).toBe(true);
|
|
360
360
|
});
|
|
361
361
|
});
|
|
362
362
|
it('loads an empty page from an empty store', async ()=>{
|
|
363
363
|
await withRepository(async (repository)=>{
|
|
364
|
-
const page = await
|
|
364
|
+
const page = await run(repository.load({}));
|
|
365
365
|
expect(page.items).toEqual([]);
|
|
366
366
|
expect(page.total).toBe(0);
|
|
367
367
|
}, []);
|
|
@@ -381,34 +381,34 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
381
381
|
it('keeps a saved record unchanged when the saved instance is mutated afterwards', async ()=>{
|
|
382
382
|
await withRepository(async (repository)=>{
|
|
383
383
|
const widget = makeContractWidget('w-9', 'Delta', 40);
|
|
384
|
-
const returned = await
|
|
384
|
+
const returned = await run(repository.save(widget));
|
|
385
385
|
widget.name = 'Mutated';
|
|
386
386
|
returned.name = 'Mutated too';
|
|
387
|
-
const stored = await
|
|
387
|
+
const stored = await run(repository.get('w-9'));
|
|
388
388
|
expect(stored.name).toBe('Delta');
|
|
389
389
|
});
|
|
390
390
|
});
|
|
391
391
|
it('mints an id when saving an entity that has none', async ()=>{
|
|
392
392
|
await withRepository(async (repository)=>{
|
|
393
|
-
const created = await
|
|
393
|
+
const created = await run(repository.save(makeContractWidget(undefined, 'Delta', 40)));
|
|
394
394
|
expect(created.id).toBeDefined();
|
|
395
|
-
const page = await
|
|
395
|
+
const page = await run(repository.load({}));
|
|
396
396
|
expect(page.total).toBe(4);
|
|
397
397
|
});
|
|
398
398
|
});
|
|
399
399
|
it('overwrites in place when saving an entity that already has an id', async ()=>{
|
|
400
400
|
await withRepository(async (repository)=>{
|
|
401
|
-
await
|
|
402
|
-
const found = await
|
|
401
|
+
await run(repository.save(makeContractWidget('w-2', 'Renamed', 25)));
|
|
402
|
+
const found = await run(repository.get('w-2'));
|
|
403
403
|
expect(found.name).toBe('Renamed');
|
|
404
|
-
const page = await
|
|
404
|
+
const page = await run(repository.load({}));
|
|
405
405
|
expect(page.total).toBe(3);
|
|
406
406
|
});
|
|
407
407
|
});
|
|
408
408
|
it('deletes by id', async ()=>{
|
|
409
409
|
await withRepository(async (repository)=>{
|
|
410
|
-
await
|
|
411
|
-
const page = await
|
|
410
|
+
await run(repository.delete('w-1'));
|
|
411
|
+
const page = await run(repository.load({}));
|
|
412
412
|
expect(page.items.map((item)=>item.id)).toEqual([
|
|
413
413
|
'w-2',
|
|
414
414
|
'w-3'
|
|
@@ -417,8 +417,8 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
417
417
|
});
|
|
418
418
|
it('deletes by entity', async ()=>{
|
|
419
419
|
await withRepository(async (repository)=>{
|
|
420
|
-
await
|
|
421
|
-
const page = await
|
|
420
|
+
await run(repository.delete(makeContractWidget('w-3', 'Gamma', 30)));
|
|
421
|
+
const page = await run(repository.load({}));
|
|
422
422
|
expect(page.items.map((item)=>item.id)).toEqual([
|
|
423
423
|
'w-1',
|
|
424
424
|
'w-2'
|
|
@@ -427,8 +427,8 @@ export const makeContractWidget = (id, name, size, tags = [])=>{
|
|
|
427
427
|
});
|
|
428
428
|
it('treats deleting an unknown id as a no-op', async ()=>{
|
|
429
429
|
await withRepository(async (repository)=>{
|
|
430
|
-
await
|
|
431
|
-
const page = await
|
|
430
|
+
await run(repository.delete('missing'));
|
|
431
|
+
const page = await run(repository.load({}));
|
|
432
432
|
expect(page.total).toBe(3);
|
|
433
433
|
});
|
|
434
434
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/contracts/entity-repository.contract.ts"],"sourcesContent":["import type { EntityRepository } from '@entifix/business';\nimport { accessor, type Entity, entity, type EntityId } from '@entifix/core';\nimport { Exit } from 'effect';\nimport { describe, expect, it } from 'vitest';\n\nimport { runRepository, runRepositoryExit } from '../effect/run.js';\n\n/**\n * The entity every implementation of the contract is exercised against. It is\n * declared here rather than by each caller so the suite controls exactly which\n * fields exist.\n */\n@entity({ key: 'contract-widget' })\nexport class ContractWidget implements Entity {\n #id?: EntityId;\n #name?: string;\n #size?: number;\n #tags: readonly string[] = [];\n #title?: Readonly<Record<string, string>>;\n\n @accessor({ type: 'id' })\n get id(): EntityId {\n return this.#id;\n }\n set id(value: EntityId) {\n this.#id = value;\n }\n\n @accessor({ type: 'string' })\n get name(): string | undefined {\n return this.#name;\n }\n set name(value: string | undefined) {\n this.#name = value;\n }\n\n @accessor({ type: 'number' })\n get size(): number | undefined {\n return this.#size;\n }\n set size(value: number | undefined) {\n this.#size = value;\n }\n\n @accessor({ type: 'scalarCollection' })\n get tags(): readonly string[] {\n return this.#tags;\n }\n set tags(value: readonly string[]) {\n this.#tags = value;\n }\n\n @accessor({ type: 'localized' })\n get title(): Readonly<Record<string, string>> | undefined {\n return this.#title;\n }\n set title(value: Readonly<Record<string, string>> | undefined) {\n this.#title = value;\n }\n}\n\nexport const makeContractWidget = (\n id: EntityId,\n name: string,\n size: number,\n tags: readonly string[] = [],\n): ContractWidget => {\n const widget = new ContractWidget();\n widget.id = id;\n widget.name = name;\n widget.size = size;\n widget.tags = tags;\n return widget;\n};\n\nexport interface EntityRepositoryContractOptions {\n /**\n * Builds a repository over `seed`. Called fresh for each test, so no state\n * leaks between them.\n */\n makeRepository(\n seed: ContractWidget[],\n ): EntityRepository | Promise<EntityRepository>;\n}\n\n/**\n * The reads every {@link EntityRepository} must answer alike: `load` with its\n * paging, filtering and sorting, and `get`. A read-only adapter — one over\n * static files, say — runs this half alone, since it refuses `save` and\n * `delete` by design.\n */\nexport const describeEntityRepositoryReadContract = (\n name: string,\n { makeRepository }: EntityRepositoryContractOptions,\n): void => {\n describe(`EntityRepository read contract: ${name}`, () => {\n const seeded = () => [\n makeContractWidget('w-1', 'Alpha', 10),\n makeContractWidget('w-2', 'Beta', 20),\n makeContractWidget('w-3', 'Gamma', 30),\n ];\n\n const withRepository = async (\n use: (repository: EntityRepository) => Promise<void>,\n seed: ContractWidget[] = seeded(),\n ) => {\n await use(await makeRepository(seed));\n };\n\n it('keeps a record unchanged when a read instance is mutated without a save', async () => {\n await withRepository(async repository => {\n const read = await runRepository(repository.get<ContractWidget>('w-1'));\n read.name = 'Mutated';\n\n const again = await runRepository(\n repository.get<ContractWidget>('w-1'),\n );\n expect(again.name).toBe('Alpha');\n\n const page = await runRepository(repository.load<ContractWidget>({}));\n page.items[0]!.name = 'Mutated';\n const reloaded = await runRepository(\n repository.load<ContractWidget>({}),\n );\n expect(reloaded.items[0]!.name).toBe('Alpha');\n });\n });\n\n it('loads every stored entity with its total', async () => {\n await withRepository(async repository => {\n const page = await runRepository(repository.load<ContractWidget>({}));\n\n expect(page.total).toBe(3);\n expect(page.items.map(item => item.name)).toEqual([\n 'Alpha',\n 'Beta',\n 'Gamma',\n ]);\n });\n });\n\n it('echoes the request it served on the page', async () => {\n await withRepository(async repository => {\n const request = { page: 1, pageSize: 2 };\n const page = await runRepository(\n repository.load<ContractWidget>(request),\n );\n\n expect(page.request).toEqual(request);\n });\n });\n\n it('pages with a 1-based page number', async () => {\n await withRepository(async repository => {\n const page = await runRepository(\n repository.load<ContractWidget>({ page: 2, pageSize: 2 }),\n );\n\n expect(page.items).toHaveLength(1);\n expect(page.items[0]?.name).toBe('Gamma');\n // The total counts matches, not the page.\n expect(page.total).toBe(3);\n });\n });\n\n it('filters by an equality operator', async () => {\n await withRepository(async repository => {\n const page = await runRepository(\n repository.load<ContractWidget>({\n filtering: [{ property: 'name', operator: 'eq', value: 'Beta' }],\n }),\n );\n\n expect(page.items).toHaveLength(1);\n expect(page.items[0]?.id).toBe('w-2');\n expect(page.total).toBe(1);\n });\n });\n\n it('filters by a range operator', async () => {\n await withRepository(async repository => {\n const page = await runRepository(\n repository.load<ContractWidget>({\n filtering: [\n { property: 'size', operator: 'between', start: 15, end: 35 },\n ],\n }),\n );\n\n expect(page.items.map(item => item.id)).toEqual(['w-2', 'w-3']);\n });\n });\n\n it('matches `like` case-insensitively on a substring', async () => {\n await withRepository(async repository => {\n const page = await runRepository(\n repository.load<ContractWidget>({\n filtering: [{ property: 'name', operator: 'like', value: 'et' }],\n }),\n );\n\n expect(page.items.map(item => item.id)).toEqual(['w-2']);\n });\n });\n\n it('matches `in` and `nin` against any element of an array member', async () => {\n await withRepository(\n async repository => {\n const tagged = await runRepository(\n repository.load<ContractWidget>({\n filtering: [\n { property: 'tags', operator: 'in', values: ['blue', 'red'] },\n ],\n }),\n );\n expect(tagged.items.map(item => item.id)).toEqual(['w-1', 'w-3']);\n\n const untagged = await runRepository(\n repository.load<ContractWidget>({\n filtering: [\n { property: 'tags', operator: 'nin', values: ['blue', 'red'] },\n ],\n }),\n );\n expect(untagged.items.map(item => item.id)).toEqual(['w-2']);\n },\n [\n makeContractWidget('w-1', 'Alpha', 10, ['blue', 'green']),\n makeContractWidget('w-2', 'Beta', 20, ['green']),\n makeContractWidget('w-3', 'Gamma', 30, ['red']),\n ],\n );\n });\n\n it('filters and sorts a localized member one locale at a time', async () => {\n const titled = (\n id: string,\n name: string,\n title: Record<string, string>,\n ) => {\n const widget = makeContractWidget(id, name, 0);\n widget.title = title;\n return widget;\n };\n await withRepository(\n async repository => {\n const found = await runRepository(\n repository.load<ContractWidget>({\n filtering: [\n {\n property: 'title.es' as keyof ContractWidget,\n operator: 'like',\n value: 'SOPA',\n },\n ],\n }),\n );\n expect(found.items.map(item => item.id)).toEqual(['w-2']);\n\n const sorted = await runRepository(\n repository.load<ContractWidget>({\n sorting: [\n {\n 0: {\n property: 'title.en' as keyof ContractWidget,\n type: 'asc',\n },\n },\n ],\n }),\n );\n expect(sorted.items.map(item => item.id)).toEqual([\n 'w-3',\n 'w-1',\n 'w-2',\n ]);\n expect(sorted.items[0]?.title).toEqual({\n en: 'Apple',\n es: 'Manzana',\n });\n },\n [\n titled('w-1', 'Alpha', { en: 'Bread', es: 'Pan' }),\n titled('w-2', 'Beta', { en: 'Soup', es: 'Sopa' }),\n titled('w-3', 'Gamma', { en: 'Apple', es: 'Manzana' }),\n ],\n );\n });\n\n it('sorts descending when asked to', async () => {\n await withRepository(async repository => {\n const page = await runRepository(\n repository.load<ContractWidget>({\n sorting: [{ 0: { property: 'size', type: 'desc' } }],\n }),\n );\n\n expect(page.items.map(item => item.size)).toEqual([30, 20, 10]);\n });\n });\n\n it('reads a single entity by id', async () => {\n await withRepository(async repository => {\n const found = await runRepository(\n repository.get<ContractWidget>('w-2'),\n );\n\n expect(found.id).toBe('w-2');\n expect(found.name).toBe('Beta');\n });\n });\n\n it('fails rather than resolving undefined for an unknown id', async () => {\n await withRepository(async repository => {\n const exit = await runRepositoryExit(\n repository.get<ContractWidget>('missing'),\n );\n\n expect(Exit.isFailure(exit)).toBe(true);\n });\n });\n\n it('loads an empty page from an empty store', async () => {\n await withRepository(async repository => {\n const page = await runRepository(repository.load<ContractWidget>({}));\n\n expect(page.items).toEqual([]);\n expect(page.total).toBe(0);\n }, []);\n });\n });\n};\n\n/** The writes every {@link EntityRepository} that accepts them must honour. */\nexport const describeEntityRepositoryWriteContract = (\n name: string,\n { makeRepository }: EntityRepositoryContractOptions,\n): void => {\n describe(`EntityRepository write contract: ${name}`, () => {\n const seeded = () => [\n makeContractWidget('w-1', 'Alpha', 10),\n makeContractWidget('w-2', 'Beta', 20),\n makeContractWidget('w-3', 'Gamma', 30),\n ];\n\n const withRepository = async (\n use: (repository: EntityRepository) => Promise<void>,\n seed: ContractWidget[] = seeded(),\n ) => {\n await use(await makeRepository(seed));\n };\n\n it('keeps a saved record unchanged when the saved instance is mutated afterwards', async () => {\n await withRepository(async repository => {\n const widget = makeContractWidget('w-9', 'Delta', 40);\n const returned = await runRepository(repository.save(widget));\n\n widget.name = 'Mutated';\n returned.name = 'Mutated too';\n\n const stored = await runRepository(\n repository.get<ContractWidget>('w-9'),\n );\n expect(stored.name).toBe('Delta');\n });\n });\n\n it('mints an id when saving an entity that has none', async () => {\n await withRepository(async repository => {\n const created = await runRepository(\n repository.save<ContractWidget>(\n makeContractWidget(undefined, 'Delta', 40),\n ),\n );\n\n expect(created.id).toBeDefined();\n\n const page = await runRepository(repository.load<ContractWidget>({}));\n expect(page.total).toBe(4);\n });\n });\n\n it('overwrites in place when saving an entity that already has an id', async () => {\n await withRepository(async repository => {\n await runRepository(\n repository.save<ContractWidget>(\n makeContractWidget('w-2', 'Renamed', 25),\n ),\n );\n\n const found = await runRepository(\n repository.get<ContractWidget>('w-2'),\n );\n expect(found.name).toBe('Renamed');\n\n const page = await runRepository(repository.load<ContractWidget>({}));\n expect(page.total).toBe(3);\n });\n });\n\n it('deletes by id', async () => {\n await withRepository(async repository => {\n await runRepository(repository.delete<ContractWidget>('w-1'));\n\n const page = await runRepository(repository.load<ContractWidget>({}));\n expect(page.items.map(item => item.id)).toEqual(['w-2', 'w-3']);\n });\n });\n\n it('deletes by entity', async () => {\n await withRepository(async repository => {\n await runRepository(\n repository.delete<ContractWidget>(\n makeContractWidget('w-3', 'Gamma', 30),\n ),\n );\n\n const page = await runRepository(repository.load<ContractWidget>({}));\n expect(page.items.map(item => item.id)).toEqual(['w-1', 'w-2']);\n });\n });\n\n it('treats deleting an unknown id as a no-op', async () => {\n await withRepository(async repository => {\n await runRepository(repository.delete<ContractWidget>('missing'));\n\n const page = await runRepository(repository.load<ContractWidget>({}));\n expect(page.total).toBe(3);\n });\n });\n });\n};\n\n/**\n * The behaviour every {@link EntityRepository} must exhibit, regardless of what\n * backs it: both halves.\n *\n * Run it against the in-memory fake *and* against each real adapter. That is\n * what stops a fake from drifting into a more forgiving version of the thing it\n * stands in for — the failure mode where a suite is green and production is\n * not. `entifix-ts-testing-integration` will run this same suite against real\n * infrastructure later.\n */\nexport const describeEntityRepositoryContract = (\n name: string,\n options: EntityRepositoryContractOptions,\n): void => {\n describeEntityRepositoryReadContract(name, options);\n describeEntityRepositoryWriteContract(name, options);\n};\n"],"names":["accessor","entity","Exit","describe","expect","it","runRepository","runRepositoryExit","key","type","ContractWidget","id","value","name","size","tags","title","makeContractWidget","widget","describeEntityRepositoryReadContract","makeRepository","seeded","withRepository","use","seed","repository","read","get","again","toBe","page","load","items","reloaded","total","map","item","toEqual","request","pageSize","toHaveLength","filtering","property","operator","start","end","tagged","values","untagged","titled","sorted","found","sorting","en","es","exit","isFailure","describeEntityRepositoryWriteContract","returned","save","stored","created","undefined","toBeDefined","delete","describeEntityRepositoryContract","options"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/contracts/entity-repository.contract.ts"],"sourcesContent":["import type { EntityRepository } from '@entifix/business';\nimport { accessor, type Entity, entity, type EntityId } from '@entifix/core';\nimport { Exit } from 'effect';\nimport { describe, expect, it } from 'vitest';\n\nimport { run, runExit } from '../effect/run.js';\n\n/**\n * The entity every implementation of the contract is exercised against. It is\n * declared here rather than by each caller so the suite controls exactly which\n * fields exist.\n */\n@entity({ key: 'contract-widget' })\nexport class ContractWidget implements Entity {\n #id?: EntityId;\n #name?: string;\n #size?: number;\n #tags: readonly string[] = [];\n #title?: Readonly<Record<string, string>>;\n\n @accessor({ type: 'id' })\n get id(): EntityId {\n return this.#id;\n }\n set id(value: EntityId) {\n this.#id = value;\n }\n\n @accessor({ type: 'string' })\n get name(): string | undefined {\n return this.#name;\n }\n set name(value: string | undefined) {\n this.#name = value;\n }\n\n @accessor({ type: 'number' })\n get size(): number | undefined {\n return this.#size;\n }\n set size(value: number | undefined) {\n this.#size = value;\n }\n\n @accessor({ type: 'scalarCollection' })\n get tags(): readonly string[] {\n return this.#tags;\n }\n set tags(value: readonly string[]) {\n this.#tags = value;\n }\n\n @accessor({ type: 'localized' })\n get title(): Readonly<Record<string, string>> | undefined {\n return this.#title;\n }\n set title(value: Readonly<Record<string, string>> | undefined) {\n this.#title = value;\n }\n}\n\nexport const makeContractWidget = (\n id: EntityId,\n name: string,\n size: number,\n tags: readonly string[] = [],\n): ContractWidget => {\n const widget = new ContractWidget();\n widget.id = id;\n widget.name = name;\n widget.size = size;\n widget.tags = tags;\n return widget;\n};\n\nexport interface EntityRepositoryContractOptions {\n /**\n * Builds a repository over `seed`. Called fresh for each test, so no state\n * leaks between them.\n */\n makeRepository(\n seed: ContractWidget[],\n ): EntityRepository | Promise<EntityRepository>;\n}\n\n/**\n * The reads every {@link EntityRepository} must answer alike: `load` with its\n * paging, filtering and sorting, and `get`. A read-only adapter — one over\n * static files, say — runs this half alone, since it refuses `save` and\n * `delete` by design.\n */\nexport const describeEntityRepositoryReadContract = (\n name: string,\n { makeRepository }: EntityRepositoryContractOptions,\n): void => {\n describe(`EntityRepository read contract: ${name}`, () => {\n const seeded = () => [\n makeContractWidget('w-1', 'Alpha', 10),\n makeContractWidget('w-2', 'Beta', 20),\n makeContractWidget('w-3', 'Gamma', 30),\n ];\n\n const withRepository = async (\n use: (repository: EntityRepository) => Promise<void>,\n seed: ContractWidget[] = seeded(),\n ) => {\n await use(await makeRepository(seed));\n };\n\n it('keeps a record unchanged when a read instance is mutated without a save', async () => {\n await withRepository(async repository => {\n const read = await run(repository.get<ContractWidget>('w-1'));\n read.name = 'Mutated';\n\n const again = await run(repository.get<ContractWidget>('w-1'));\n expect(again.name).toBe('Alpha');\n\n const page = await run(repository.load<ContractWidget>({}));\n page.items[0]!.name = 'Mutated';\n const reloaded = await run(repository.load<ContractWidget>({}));\n expect(reloaded.items[0]!.name).toBe('Alpha');\n });\n });\n\n it('loads every stored entity with its total', async () => {\n await withRepository(async repository => {\n const page = await run(repository.load<ContractWidget>({}));\n\n expect(page.total).toBe(3);\n expect(page.items.map(item => item.name)).toEqual([\n 'Alpha',\n 'Beta',\n 'Gamma',\n ]);\n });\n });\n\n it('echoes the request it served on the page', async () => {\n await withRepository(async repository => {\n const request = { page: 1, pageSize: 2 };\n const page = await run(repository.load<ContractWidget>(request));\n\n expect(page.request).toEqual(request);\n });\n });\n\n it('pages with a 1-based page number', async () => {\n await withRepository(async repository => {\n const page = await run(\n repository.load<ContractWidget>({ page: 2, pageSize: 2 }),\n );\n\n expect(page.items).toHaveLength(1);\n expect(page.items[0]?.name).toBe('Gamma');\n // The total counts matches, not the page.\n expect(page.total).toBe(3);\n });\n });\n\n it('filters by an equality operator', async () => {\n await withRepository(async repository => {\n const page = await run(\n repository.load<ContractWidget>({\n filtering: [{ property: 'name', operator: 'eq', value: 'Beta' }],\n }),\n );\n\n expect(page.items).toHaveLength(1);\n expect(page.items[0]?.id).toBe('w-2');\n expect(page.total).toBe(1);\n });\n });\n\n it('filters by a range operator', async () => {\n await withRepository(async repository => {\n const page = await run(\n repository.load<ContractWidget>({\n filtering: [\n { property: 'size', operator: 'between', start: 15, end: 35 },\n ],\n }),\n );\n\n expect(page.items.map(item => item.id)).toEqual(['w-2', 'w-3']);\n });\n });\n\n it('matches `like` case-insensitively on a substring', async () => {\n await withRepository(async repository => {\n const page = await run(\n repository.load<ContractWidget>({\n filtering: [{ property: 'name', operator: 'like', value: 'et' }],\n }),\n );\n\n expect(page.items.map(item => item.id)).toEqual(['w-2']);\n });\n });\n\n it('matches `in` and `nin` against any element of an array member', async () => {\n await withRepository(\n async repository => {\n const tagged = await run(\n repository.load<ContractWidget>({\n filtering: [\n { property: 'tags', operator: 'in', values: ['blue', 'red'] },\n ],\n }),\n );\n expect(tagged.items.map(item => item.id)).toEqual(['w-1', 'w-3']);\n\n const untagged = await run(\n repository.load<ContractWidget>({\n filtering: [\n { property: 'tags', operator: 'nin', values: ['blue', 'red'] },\n ],\n }),\n );\n expect(untagged.items.map(item => item.id)).toEqual(['w-2']);\n },\n [\n makeContractWidget('w-1', 'Alpha', 10, ['blue', 'green']),\n makeContractWidget('w-2', 'Beta', 20, ['green']),\n makeContractWidget('w-3', 'Gamma', 30, ['red']),\n ],\n );\n });\n\n it('filters and sorts a localized member one locale at a time', async () => {\n const titled = (\n id: string,\n name: string,\n title: Record<string, string>,\n ) => {\n const widget = makeContractWidget(id, name, 0);\n widget.title = title;\n return widget;\n };\n await withRepository(\n async repository => {\n const found = await run(\n repository.load<ContractWidget>({\n filtering: [\n {\n property: 'title.es' as keyof ContractWidget,\n operator: 'like',\n value: 'SOPA',\n },\n ],\n }),\n );\n expect(found.items.map(item => item.id)).toEqual(['w-2']);\n\n const sorted = await run(\n repository.load<ContractWidget>({\n sorting: [\n {\n 0: {\n property: 'title.en' as keyof ContractWidget,\n type: 'asc',\n },\n },\n ],\n }),\n );\n expect(sorted.items.map(item => item.id)).toEqual([\n 'w-3',\n 'w-1',\n 'w-2',\n ]);\n expect(sorted.items[0]?.title).toEqual({\n en: 'Apple',\n es: 'Manzana',\n });\n },\n [\n titled('w-1', 'Alpha', { en: 'Bread', es: 'Pan' }),\n titled('w-2', 'Beta', { en: 'Soup', es: 'Sopa' }),\n titled('w-3', 'Gamma', { en: 'Apple', es: 'Manzana' }),\n ],\n );\n });\n\n it('sorts descending when asked to', async () => {\n await withRepository(async repository => {\n const page = await run(\n repository.load<ContractWidget>({\n sorting: [{ 0: { property: 'size', type: 'desc' } }],\n }),\n );\n\n expect(page.items.map(item => item.size)).toEqual([30, 20, 10]);\n });\n });\n\n it('reads a single entity by id', async () => {\n await withRepository(async repository => {\n const found = await run(repository.get<ContractWidget>('w-2'));\n\n expect(found.id).toBe('w-2');\n expect(found.name).toBe('Beta');\n });\n });\n\n it('fails rather than resolving undefined for an unknown id', async () => {\n await withRepository(async repository => {\n const exit = await runExit(repository.get<ContractWidget>('missing'));\n\n expect(Exit.isFailure(exit)).toBe(true);\n });\n });\n\n it('loads an empty page from an empty store', async () => {\n await withRepository(async repository => {\n const page = await run(repository.load<ContractWidget>({}));\n\n expect(page.items).toEqual([]);\n expect(page.total).toBe(0);\n }, []);\n });\n });\n};\n\n/** The writes every {@link EntityRepository} that accepts them must honour. */\nexport const describeEntityRepositoryWriteContract = (\n name: string,\n { makeRepository }: EntityRepositoryContractOptions,\n): void => {\n describe(`EntityRepository write contract: ${name}`, () => {\n const seeded = () => [\n makeContractWidget('w-1', 'Alpha', 10),\n makeContractWidget('w-2', 'Beta', 20),\n makeContractWidget('w-3', 'Gamma', 30),\n ];\n\n const withRepository = async (\n use: (repository: EntityRepository) => Promise<void>,\n seed: ContractWidget[] = seeded(),\n ) => {\n await use(await makeRepository(seed));\n };\n\n it('keeps a saved record unchanged when the saved instance is mutated afterwards', async () => {\n await withRepository(async repository => {\n const widget = makeContractWidget('w-9', 'Delta', 40);\n const returned = await run(repository.save(widget));\n\n widget.name = 'Mutated';\n returned.name = 'Mutated too';\n\n const stored = await run(repository.get<ContractWidget>('w-9'));\n expect(stored.name).toBe('Delta');\n });\n });\n\n it('mints an id when saving an entity that has none', async () => {\n await withRepository(async repository => {\n const created = await run(\n repository.save<ContractWidget>(\n makeContractWidget(undefined, 'Delta', 40),\n ),\n );\n\n expect(created.id).toBeDefined();\n\n const page = await run(repository.load<ContractWidget>({}));\n expect(page.total).toBe(4);\n });\n });\n\n it('overwrites in place when saving an entity that already has an id', async () => {\n await withRepository(async repository => {\n await run(\n repository.save<ContractWidget>(\n makeContractWidget('w-2', 'Renamed', 25),\n ),\n );\n\n const found = await run(repository.get<ContractWidget>('w-2'));\n expect(found.name).toBe('Renamed');\n\n const page = await run(repository.load<ContractWidget>({}));\n expect(page.total).toBe(3);\n });\n });\n\n it('deletes by id', async () => {\n await withRepository(async repository => {\n await run(repository.delete<ContractWidget>('w-1'));\n\n const page = await run(repository.load<ContractWidget>({}));\n expect(page.items.map(item => item.id)).toEqual(['w-2', 'w-3']);\n });\n });\n\n it('deletes by entity', async () => {\n await withRepository(async repository => {\n await run(\n repository.delete<ContractWidget>(\n makeContractWidget('w-3', 'Gamma', 30),\n ),\n );\n\n const page = await run(repository.load<ContractWidget>({}));\n expect(page.items.map(item => item.id)).toEqual(['w-1', 'w-2']);\n });\n });\n\n it('treats deleting an unknown id as a no-op', async () => {\n await withRepository(async repository => {\n await run(repository.delete<ContractWidget>('missing'));\n\n const page = await run(repository.load<ContractWidget>({}));\n expect(page.total).toBe(3);\n });\n });\n });\n};\n\n/**\n * The behaviour every {@link EntityRepository} must exhibit, regardless of what\n * backs it: both halves.\n *\n * Run it against the in-memory fake *and* against each real adapter. That is\n * what stops a fake from drifting into a more forgiving version of the thing it\n * stands in for — the failure mode where a suite is green and production is\n * not. `entifix-ts-testing-integration` will run this same suite against real\n * infrastructure later.\n */\nexport const describeEntityRepositoryContract = (\n name: string,\n options: EntityRepositoryContractOptions,\n): void => {\n describeEntityRepositoryReadContract(name, options);\n describeEntityRepositoryWriteContract(name, options);\n};\n"],"names":["accessor","entity","Exit","describe","expect","it","run","runExit","key","type","ContractWidget","id","value","name","size","tags","title","makeContractWidget","widget","describeEntityRepositoryReadContract","makeRepository","seeded","withRepository","use","seed","repository","read","get","again","toBe","page","load","items","reloaded","total","map","item","toEqual","request","pageSize","toHaveLength","filtering","property","operator","start","end","tagged","values","untagged","titled","sorted","found","sorting","en","es","exit","isFailure","describeEntityRepositoryWriteContract","returned","save","stored","created","undefined","toBeDefined","delete","describeEntityRepositoryContract","options"],"mappings":"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"}
|
|
@@ -26,8 +26,6 @@ export declare const makeStubUriConfigurationClient: (entities: Record<string, s
|
|
|
26
26
|
keyTemplate?: string | undefined;
|
|
27
27
|
group?: string | undefined;
|
|
28
28
|
}) => ConfigurationClient;
|
|
29
|
-
/** Provides {@link ConfigurationRepositoryTag} from a plain configuration. */
|
|
30
|
-
export declare const stubConfigurationLayer: (plain?: ConfigurationPlain) => Layer.Layer<ConfigurationRepositoryTag, never, never>;
|
|
31
29
|
/** Provides {@link ConfigurationRepositoryTag} from an entity-to-URI mapping. */
|
|
32
30
|
export declare const stubUriConfigurationLayer: (entities: Record<string, string>, options?: {
|
|
33
31
|
keyTemplate?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/doubles/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,QAAO,kBAAuB,KAC7B,mBAA6D,CAAC;AAEjE;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GACzC,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC;;;CAA4D,KAC3D,mBAMC,CAAC;AAEL,
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/doubles/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,QAAO,kBAAuB,KAC7B,mBAA6D,CAAC;AAEjE;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GACzC,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC;;;CAA4D,KAC3D,mBAMC,CAAC;AAEL,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,GACpC,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,UAAU;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,0DAKjD,CAAC"}
|
|
@@ -25,7 +25,6 @@ import { Layer } from 'effect';
|
|
|
25
25
|
value: uri
|
|
26
26
|
}))
|
|
27
27
|
});
|
|
28
|
-
/** Provides {@link ConfigurationRepositoryTag} from a plain configuration. */ export const stubConfigurationLayer = (plain = {})=>Layer.succeed(ConfigurationRepositoryTag, makeStubConfigurationClient(plain));
|
|
29
28
|
/** Provides {@link ConfigurationRepositoryTag} from an entity-to-URI mapping. */ export const stubUriConfigurationLayer = (entities, options)=>Layer.succeed(ConfigurationRepositoryTag, makeStubUriConfigurationClient(entities, options));
|
|
30
29
|
|
|
31
30
|
//# sourceMappingURL=configuration.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/doubles/configuration.ts"],"sourcesContent":["import { ConfigurationRepositoryTag } from '@entifix/business';\nimport {\n type ConfigurationClient,\n ConfigurationClientInMemory,\n type ConfigurationPlain,\n} from '@entifix/core';\nimport { Layer } from 'effect';\n\n/**\n * The group the REST adapters read their entity URIs from when\n * `BuildEntityRestOptions.uriConfig.group` is left unset.\n */\nexport const DEFAULT_URI_GROUP = 'restUri';\n\n/**\n * A {@link ConfigurationClient} over a plain `{ group: [{ key, value }] }`\n * literal. This is a stub, not a fake: it answers reads and nothing else.\n *\n * Backed by core's own `ConfigurationClientInMemory`, so the parsing rules under\n * test are the production ones rather than a second implementation that could\n * drift.\n */\nexport const makeStubConfigurationClient = (\n plain: ConfigurationPlain = {},\n): ConfigurationClient => new ConfigurationClientInMemory(plain);\n\n/**\n * The common case for REST adapter specs: every entity resolves under one base\n * URL, keyed the way `buildEntityBaseUrl` looks them up.\n *\n * `entities` maps an entity `key` to its endpoint; passing `{ product: '…' }`\n * satisfies a `uriConfig.key` of `'service.[entity]'`.\n */\nexport const makeStubUriConfigurationClient = (\n entities: Record<string, string>,\n { keyTemplate = '[entity]', group = DEFAULT_URI_GROUP } = {},\n): ConfigurationClient =>\n makeStubConfigurationClient({\n [group]: Object.entries(entities).map(([entity, uri]) => ({\n key: keyTemplate.replace('[entity]', entity),\n value: uri,\n })),\n });\n\n/** Provides {@link ConfigurationRepositoryTag} from
|
|
1
|
+
{"version":3,"sources":["../../src/doubles/configuration.ts"],"sourcesContent":["import { ConfigurationRepositoryTag } from '@entifix/business';\nimport {\n type ConfigurationClient,\n ConfigurationClientInMemory,\n type ConfigurationPlain,\n} from '@entifix/core';\nimport { Layer } from 'effect';\n\n/**\n * The group the REST adapters read their entity URIs from when\n * `BuildEntityRestOptions.uriConfig.group` is left unset.\n */\nexport const DEFAULT_URI_GROUP = 'restUri';\n\n/**\n * A {@link ConfigurationClient} over a plain `{ group: [{ key, value }] }`\n * literal. This is a stub, not a fake: it answers reads and nothing else.\n *\n * Backed by core's own `ConfigurationClientInMemory`, so the parsing rules under\n * test are the production ones rather than a second implementation that could\n * drift.\n */\nexport const makeStubConfigurationClient = (\n plain: ConfigurationPlain = {},\n): ConfigurationClient => new ConfigurationClientInMemory(plain);\n\n/**\n * The common case for REST adapter specs: every entity resolves under one base\n * URL, keyed the way `buildEntityBaseUrl` looks them up.\n *\n * `entities` maps an entity `key` to its endpoint; passing `{ product: '…' }`\n * satisfies a `uriConfig.key` of `'service.[entity]'`.\n */\nexport const makeStubUriConfigurationClient = (\n entities: Record<string, string>,\n { keyTemplate = '[entity]', group = DEFAULT_URI_GROUP } = {},\n): ConfigurationClient =>\n makeStubConfigurationClient({\n [group]: Object.entries(entities).map(([entity, uri]) => ({\n key: keyTemplate.replace('[entity]', entity),\n value: uri,\n })),\n });\n\n/** Provides {@link ConfigurationRepositoryTag} from an entity-to-URI mapping. */\nexport const stubUriConfigurationLayer = (\n entities: Record<string, string>,\n options?: { keyTemplate?: string; group?: string },\n) =>\n Layer.succeed(\n ConfigurationRepositoryTag,\n makeStubUriConfigurationClient(entities, options),\n );\n"],"names":["ConfigurationRepositoryTag","ConfigurationClientInMemory","Layer","DEFAULT_URI_GROUP","makeStubConfigurationClient","plain","makeStubUriConfigurationClient","entities","keyTemplate","group","Object","entries","map","entity","uri","key","replace","value","stubUriConfigurationLayer","options","succeed"],"mappings":"AAAA,SAASA,0BAA0B,QAAQ,oBAAoB;AAC/D,SAEEC,2BAA2B,QAEtB,gBAAgB;AACvB,SAASC,KAAK,QAAQ,SAAS;AAE/B;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,UAAU;AAE3C;;;;;;;CAOC,GACD,OAAO,MAAMC,8BAA8B,CACzCC,QAA4B,CAAC,CAAC,GACN,IAAIJ,4BAA4BI,OAAO;AAEjE;;;;;;CAMC,GACD,OAAO,MAAMC,iCAAiC,CAC5CC,UACA,EAAEC,cAAc,UAAU,EAAEC,QAAQN,iBAAiB,EAAE,GAAG,CAAC,CAAC,GAE5DC,4BAA4B;QAC1B,CAACK,MAAM,EAAEC,OAAOC,OAAO,CAACJ,UAAUK,GAAG,CAAC,CAAC,CAACC,QAAQC,IAAI,GAAM,CAAA;gBACxDC,KAAKP,YAAYQ,OAAO,CAAC,YAAYH;gBACrCI,OAAOH;YACT,CAAA;IACF,GAAG;AAEL,+EAA+E,GAC/E,OAAO,MAAMI,4BAA4B,CACvCX,UACAY,UAEAjB,MAAMkB,OAAO,CACXpB,4BACAM,+BAA+BC,UAAUY,UACzC"}
|
package/dist/effect/run.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ConfigurationRepositoryTag } from '@entifix/business';
|
|
2
1
|
import { Effect, Exit, type Layer } from 'effect';
|
|
3
2
|
/**
|
|
4
3
|
* Runs an Effect that still requires `TContext`, providing it from `layer`.
|
|
@@ -17,18 +16,14 @@ export declare const runUC: <TValue, TError, TContext>(effect: Effect.Effect<TVa
|
|
|
17
16
|
*/
|
|
18
17
|
export declare const expectFailure: <TValue, TError, TContext>(effect: Effect.Effect<TValue, TError, TContext>, layer: Layer.Layer<TContext>) => Promise<TError>;
|
|
19
18
|
/**
|
|
20
|
-
* Runs
|
|
19
|
+
* Runs an Effect that requires nothing, returning its value.
|
|
21
20
|
*
|
|
22
|
-
* Every
|
|
23
|
-
*
|
|
24
|
-
* through it — so even a repository that needs no configuration has to be given
|
|
25
|
-
* one. `configuration` supplies it for the adapters that do.
|
|
21
|
+
* Every repository method is one of these: an adapter binds what it needs when
|
|
22
|
+
* it is built, so a spec runs a repository call with no layer at all.
|
|
26
23
|
*/
|
|
27
|
-
export declare const runRepository: <TValue, TError>(effect: Effect.Effect<TValue, TError, ConfigurationRepositoryTag>, configuration?: Layer.Layer<ConfigurationRepositoryTag>) => Promise<TValue>;
|
|
28
|
-
/** {@link runRepository}, for an Effect expected to fail. */
|
|
29
|
-
export declare const runRepositoryExit: <TValue, TError>(effect: Effect.Effect<TValue, TError, ConfigurationRepositoryTag>, configuration?: Layer.Layer<ConfigurationRepositoryTag>) => Promise<Exit.Exit<TValue, TError>>;
|
|
30
|
-
/** Runs an Effect that requires nothing, returning its value. */
|
|
31
24
|
export declare const run: <TValue, TError>(effect: Effect.Effect<TValue, TError>) => Promise<TValue>;
|
|
25
|
+
/** {@link run}, keeping the exit so a spec can assert on a failure. */
|
|
26
|
+
export declare const runExit: <TValue, TError>(effect: Effect.Effect<TValue, TError>) => Promise<Exit.Exit<TValue, TError>>;
|
|
32
27
|
/** Runs an Effect that requires nothing and returns its typed error. */
|
|
33
28
|
export declare const runFailure: <TValue, TError>(effect: Effect.Effect<TValue, TError>) => Promise<TError>;
|
|
34
29
|
//# sourceMappingURL=run.d.ts.map
|
package/dist/effect/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/effect/run.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/effect/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,IAAI,EAAE,KAAK,KAAK,EAAU,MAAM,QAAQ,CAAC;AAEjE;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,EAAE,MAAM,EAAE,QAAQ,EAC5C,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAC/C,OAAO,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,KAC3B,OAAO,CAAC,MAAM,CAAqD,CAAC;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAU,MAAM,EAAE,MAAM,EAAE,QAAQ,EAC1D,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAC/C,OAAO,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,KAC3B,OAAO,CAAC,MAAM,CAkBhB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,GAAG,GAAI,MAAM,EAAE,MAAM,EAChC,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,OAAO,CAAC,MAAM,CAA8B,CAAC;AAEhD,uEAAuE;AACvE,eAAO,MAAM,OAAO,GAAI,MAAM,EAAE,MAAM,EACpC,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAkC,CAAC;AAEvE,wEAAwE;AACxE,eAAO,MAAM,UAAU,GAAU,MAAM,EAAE,MAAM,EAC7C,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,OAAO,CAAC,MAAM,CAiBhB,CAAC"}
|
package/dist/effect/run.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Cause, Effect, Exit, Option } from 'effect';
|
|
2
|
-
import { stubConfigurationLayer } from '../doubles/configuration.js';
|
|
3
2
|
/**
|
|
4
3
|
* Runs an Effect that still requires `TContext`, providing it from `layer`.
|
|
5
4
|
*
|
|
@@ -25,15 +24,12 @@ import { stubConfigurationLayer } from '../doubles/configuration.js';
|
|
|
25
24
|
throw new Error(`Expected a typed failure, but the effect died with: ${String(exit.cause)}`);
|
|
26
25
|
};
|
|
27
26
|
/**
|
|
28
|
-
* Runs
|
|
27
|
+
* Runs an Effect that requires nothing, returning its value.
|
|
29
28
|
*
|
|
30
|
-
* Every
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*/ export const runRepository = (effect, configuration = stubConfigurationLayer())=>Effect.runPromise(Effect.provide(effect, configuration));
|
|
35
|
-
/** {@link runRepository}, for an Effect expected to fail. */ export const runRepositoryExit = (effect, configuration = stubConfigurationLayer())=>Effect.runPromiseExit(Effect.provide(effect, configuration));
|
|
36
|
-
/** Runs an Effect that requires nothing, returning its value. */ export const run = (effect)=>Effect.runPromise(effect);
|
|
29
|
+
* Every repository method is one of these: an adapter binds what it needs when
|
|
30
|
+
* it is built, so a spec runs a repository call with no layer at all.
|
|
31
|
+
*/ export const run = (effect)=>Effect.runPromise(effect);
|
|
32
|
+
/** {@link run}, keeping the exit so a spec can assert on a failure. */ export const runExit = (effect)=>Effect.runPromiseExit(effect);
|
|
37
33
|
/** Runs an Effect that requires nothing and returns its typed error. */ export const runFailure = async (effect)=>{
|
|
38
34
|
const exit = await Effect.runPromiseExit(effect);
|
|
39
35
|
if (Exit.isSuccess(exit)) {
|
package/dist/effect/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/effect/run.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../src/effect/run.ts"],"sourcesContent":["import { Cause, Effect, Exit, type Layer, Option } from 'effect';\n\n/**\n * Runs an Effect that still requires `TContext`, providing it from `layer`.\n *\n * Use-cases are `Effect.gen` blocks yielding Context tags; every spec would\n * otherwise repeat the same `Effect.runPromise(uc.pipe(Effect.provide(…)))`\n * incantation.\n */\nexport const runUC = <TValue, TError, TContext>(\n effect: Effect.Effect<TValue, TError, TContext>,\n layer: Layer.Layer<TContext>,\n): Promise<TValue> => Effect.runPromise(Effect.provide(effect, layer));\n\n/**\n * Runs an Effect expected to fail and returns its typed error.\n *\n * Asserting on the error value beats asserting that a promise rejected: it is\n * the difference between \"something went wrong\" and \"it failed with the\n * `EntifixLockError` that the facade turns into a 409\".\n */\nexport const expectFailure = async <TValue, TError, TContext>(\n effect: Effect.Effect<TValue, TError, TContext>,\n layer: Layer.Layer<TContext>,\n): Promise<TError> => {\n const exit = await Effect.runPromiseExit(Effect.provide(effect, layer));\n\n if (Exit.isSuccess(exit)) {\n throw new Error(\n `Expected the effect to fail, but it succeeded with: ${JSON.stringify(\n exit.value,\n )}`,\n );\n }\n\n const failure = Cause.findErrorOption(exit.cause);\n if (Option.isSome(failure)) {\n return failure.value;\n }\n throw new Error(\n `Expected a typed failure, but the effect died with: ${String(exit.cause)}`,\n );\n};\n\n/**\n * Runs an Effect that requires nothing, returning its value.\n *\n * Every repository method is one of these: an adapter binds what it needs when\n * it is built, so a spec runs a repository call with no layer at all.\n */\nexport const run = <TValue, TError>(\n effect: Effect.Effect<TValue, TError>,\n): Promise<TValue> => Effect.runPromise(effect);\n\n/** {@link run}, keeping the exit so a spec can assert on a failure. */\nexport const runExit = <TValue, TError>(\n effect: Effect.Effect<TValue, TError>,\n): Promise<Exit.Exit<TValue, TError>> => Effect.runPromiseExit(effect);\n\n/** Runs an Effect that requires nothing and returns its typed error. */\nexport const runFailure = async <TValue, TError>(\n effect: Effect.Effect<TValue, TError>,\n): Promise<TError> => {\n const exit = await Effect.runPromiseExit(effect);\n\n if (Exit.isSuccess(exit)) {\n throw new Error(\n `Expected the effect to fail, but it succeeded with: ${JSON.stringify(\n exit.value,\n )}`,\n );\n }\n const failure = Cause.findErrorOption(exit.cause);\n if (Option.isSome(failure)) {\n return failure.value;\n }\n throw new Error(\n `Expected a typed failure, but the effect died with: ${String(exit.cause)}`,\n );\n};\n"],"names":["Cause","Effect","Exit","Option","runUC","effect","layer","runPromise","provide","expectFailure","exit","runPromiseExit","isSuccess","Error","JSON","stringify","value","failure","findErrorOption","cause","isSome","String","run","runExit","runFailure"],"mappings":"AAAA,SAASA,KAAK,EAAEC,MAAM,EAAEC,IAAI,EAAcC,MAAM,QAAQ,SAAS;AAEjE;;;;;;CAMC,GACD,OAAO,MAAMC,QAAQ,CACnBC,QACAC,QACoBL,OAAOM,UAAU,CAACN,OAAOO,OAAO,CAACH,QAAQC,QAAQ;AAEvE;;;;;;CAMC,GACD,OAAO,MAAMG,gBAAgB,OAC3BJ,QACAC;IAEA,MAAMI,OAAO,MAAMT,OAAOU,cAAc,CAACV,OAAOO,OAAO,CAACH,QAAQC;IAEhE,IAAIJ,KAAKU,SAAS,CAACF,OAAO;QACxB,MAAM,IAAIG,MACR,CAAC,oDAAoD,EAAEC,KAAKC,SAAS,CACnEL,KAAKM,KAAK,GACT;IAEP;IAEA,MAAMC,UAAUjB,MAAMkB,eAAe,CAACR,KAAKS,KAAK;IAChD,IAAIhB,OAAOiB,MAAM,CAACH,UAAU;QAC1B,OAAOA,QAAQD,KAAK;IACtB;IACA,MAAM,IAAIH,MACR,CAAC,oDAAoD,EAAEQ,OAAOX,KAAKS,KAAK,GAAG;AAE/E,EAAE;AAEF;;;;;CAKC,GACD,OAAO,MAAMG,MAAM,CACjBjB,SACoBJ,OAAOM,UAAU,CAACF,QAAQ;AAEhD,qEAAqE,GACrE,OAAO,MAAMkB,UAAU,CACrBlB,SACuCJ,OAAOU,cAAc,CAACN,QAAQ;AAEvE,sEAAsE,GACtE,OAAO,MAAMmB,aAAa,OACxBnB;IAEA,MAAMK,OAAO,MAAMT,OAAOU,cAAc,CAACN;IAEzC,IAAIH,KAAKU,SAAS,CAACF,OAAO;QACxB,MAAM,IAAIG,MACR,CAAC,oDAAoD,EAAEC,KAAKC,SAAS,CACnEL,KAAKM,KAAK,GACT;IAEP;IACA,MAAMC,UAAUjB,MAAMkB,eAAe,CAACR,KAAKS,KAAK;IAChD,IAAIhB,OAAOiB,MAAM,CAACH,UAAU;QAC1B,OAAOA,QAAQD,KAAK;IACtB;IACA,MAAM,IAAIH,MACR,CAAC,oDAAoD,EAAEQ,OAAOX,KAAKS,KAAK,GAAG;AAE/E,EAAE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entifix/testing-unit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Test doubles for entifix: in-memory ports, driver fakes for Mongo, Redis, RabbitMQ and SQL, MSW handlers and port contract suites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@swc/helpers": "0.5.23",
|
|
75
|
-
"@entifix/business": "0.
|
|
76
|
-
"@entifix/
|
|
77
|
-
"@entifix/
|
|
75
|
+
"@entifix/business": "0.5.0",
|
|
76
|
+
"@entifix/transactions": "0.5.0",
|
|
77
|
+
"@entifix/core": "0.5.0"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
80
|
"@testing-library/react": "*",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"msw": ">=3",
|
|
83
83
|
"react": "*",
|
|
84
84
|
"vitest": "*",
|
|
85
|
-
"@entifix/react-controls": "0.
|
|
85
|
+
"@entifix/react-controls": "0.5.0"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
88
|
"@entifix/react-controls": {
|