@ddd-ts-kit/testing 0.9.0 → 0.11.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 +38 -0
- package/dist/contracts/event-store/companion-stores-suite.d.ts +12 -0
- package/dist/contracts/event-store/companion-stores-suite.d.ts.map +1 -0
- package/dist/contracts/event-store/companion-stores-suite.js +60 -0
- package/dist/contracts/event-store/companion-stores-suite.js.map +1 -0
- package/dist/contracts/event-store/consignment-registered.d.ts +8 -0
- package/dist/contracts/event-store/consignment-registered.d.ts.map +1 -0
- package/dist/contracts/event-store/consignment-registered.js +10 -0
- package/dist/contracts/event-store/consignment-registered.js.map +1 -0
- package/dist/contracts/event-store/consignment-weighed.d.ts +9 -0
- package/dist/contracts/event-store/consignment-weighed.d.ts.map +1 -0
- package/dist/contracts/event-store/consignment-weighed.js +12 -0
- package/dist/contracts/event-store/consignment-weighed.js.map +1 -0
- package/dist/contracts/event-store/consignment.d.ts +16 -0
- package/dist/contracts/event-store/consignment.d.ts.map +1 -0
- package/dist/contracts/event-store/consignment.js +36 -0
- package/dist/contracts/event-store/consignment.js.map +1 -0
- package/dist/contracts/event-store/consignments.d.ts +10 -0
- package/dist/contracts/event-store/consignments.d.ts.map +1 -0
- package/dist/contracts/event-store/consignments.js +34 -0
- package/dist/contracts/event-store/consignments.js.map +1 -0
- package/dist/contracts/event-store/event-of.d.ts +4 -0
- package/dist/contracts/event-store/event-of.d.ts.map +1 -0
- package/dist/contracts/event-store/event-of.js +13 -0
- package/dist/contracts/event-store/event-of.js.map +1 -0
- package/dist/contracts/event-store/event-store-under-contract.d.ts +11 -0
- package/dist/contracts/event-store/event-store-under-contract.d.ts.map +1 -0
- package/dist/contracts/event-store/event-store-under-contract.js +2 -0
- package/dist/contracts/event-store/event-store-under-contract.js.map +1 -0
- package/dist/contracts/event-store/one-transaction-suite.d.ts +18 -0
- package/dist/contracts/event-store/one-transaction-suite.d.ts.map +1 -0
- package/dist/contracts/event-store/one-transaction-suite.js +92 -0
- package/dist/contracts/event-store/one-transaction-suite.js.map +1 -0
- package/dist/contracts/event-store/store-suite.d.ts +10 -0
- package/dist/contracts/event-store/store-suite.d.ts.map +1 -0
- package/dist/contracts/event-store/store-suite.js +44 -0
- package/dist/contracts/event-store/store-suite.js.map +1 -0
- package/dist/contracts/event-store/stream-suite.d.ts +11 -0
- package/dist/contracts/event-store/stream-suite.d.ts.map +1 -0
- package/dist/contracts/event-store/stream-suite.js +91 -0
- package/dist/contracts/event-store/stream-suite.js.map +1 -0
- package/dist/contracts/event-store-contract.d.ts +20 -0
- package/dist/contracts/event-store-contract.d.ts.map +1 -0
- package/dist/contracts/event-store-contract.js +21 -0
- package/dist/contracts/event-store-contract.js.map +1 -0
- package/dist/contracts/event-store-unit-of-work-contract.d.ts +15 -0
- package/dist/contracts/event-store-unit-of-work-contract.d.ts.map +1 -0
- package/dist/contracts/event-store-unit-of-work-contract.js +10 -0
- package/dist/contracts/event-store-unit-of-work-contract.js.map +1 -0
- package/dist/contracts/job-store/a-job.d.ts +24 -0
- package/dist/contracts/job-store/a-job.d.ts.map +1 -0
- package/dist/contracts/job-store/a-job.js +28 -0
- package/dist/contracts/job-store/a-job.js.map +1 -0
- package/dist/contracts/job-store/claim-suite.d.ts +15 -0
- package/dist/contracts/job-store/claim-suite.d.ts.map +1 -0
- package/dist/contracts/job-store/claim-suite.js +126 -0
- package/dist/contracts/job-store/claim-suite.js.map +1 -0
- package/dist/contracts/job-store/job-fixtures.d.ts +5 -0
- package/dist/contracts/job-store/job-fixtures.d.ts.map +1 -0
- package/dist/contracts/job-store/job-fixtures.js +5 -0
- package/dist/contracts/job-store/job-fixtures.js.map +1 -0
- package/dist/contracts/job-store/job-store-under-contract.d.ts +61 -0
- package/dist/contracts/job-store/job-store-under-contract.d.ts.map +1 -0
- package/dist/contracts/job-store/job-store-under-contract.js +2 -0
- package/dist/contracts/job-store/job-store-under-contract.js.map +1 -0
- package/dist/contracts/job-store/keep-suite.d.ts +11 -0
- package/dist/contracts/job-store/keep-suite.d.ts.map +1 -0
- package/dist/contracts/job-store/keep-suite.js +55 -0
- package/dist/contracts/job-store/keep-suite.js.map +1 -0
- package/dist/contracts/job-store/later.d.ts +3 -0
- package/dist/contracts/job-store/later.d.ts.map +1 -0
- package/dist/contracts/job-store/later.js +6 -0
- package/dist/contracts/job-store/later.js.map +1 -0
- package/dist/contracts/job-store/report-suite.d.ts +12 -0
- package/dist/contracts/job-store/report-suite.d.ts.map +1 -0
- package/dist/contracts/job-store/report-suite.js +82 -0
- package/dist/contracts/job-store/report-suite.js.map +1 -0
- package/dist/contracts/job-store/transaction-suite.d.ts +23 -0
- package/dist/contracts/job-store/transaction-suite.d.ts.map +1 -0
- package/dist/contracts/job-store/transaction-suite.js +68 -0
- package/dist/contracts/job-store/transaction-suite.js.map +1 -0
- package/dist/contracts/job-store-contract.d.ts +24 -0
- package/dist/contracts/job-store-contract.d.ts.map +1 -0
- package/dist/contracts/job-store-contract.js +22 -0
- package/dist/contracts/job-store-contract.js.map +1 -0
- package/dist/contracts/process-store-contract.d.ts +28 -0
- package/dist/contracts/process-store-contract.d.ts.map +1 -0
- package/dist/contracts/process-store-contract.js +81 -0
- package/dist/contracts/process-store-contract.js.map +1 -0
- package/dist/contracts.d.ts +6 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +4 -0
- package/dist/contracts.js.map +1 -1
- package/dist/event-sourcing/expect-snapshot-equivalence.d.ts +14 -0
- package/dist/event-sourcing/expect-snapshot-equivalence.d.ts.map +1 -0
- package/dist/event-sourcing/expect-snapshot-equivalence.js +48 -0
- package/dist/event-sourcing/expect-snapshot-equivalence.js.map +1 -0
- package/dist/event-sourcing/given-events.d.ts +12 -0
- package/dist/event-sourcing/given-events.d.ts.map +1 -0
- package/dist/event-sourcing/given-events.js +16 -0
- package/dist/event-sourcing/given-events.js.map +1 -0
- package/dist/event-sourcing/snapshot-equivalence.d.ts +12 -0
- package/dist/event-sourcing/snapshot-equivalence.d.ts.map +1 -0
- package/dist/event-sourcing/snapshot-equivalence.js +2 -0
- package/dist/event-sourcing/snapshot-equivalence.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/processes/command-matcher.d.ts +8 -0
- package/dist/processes/command-matcher.d.ts.map +1 -0
- package/dist/processes/command-matcher.js +2 -0
- package/dist/processes/command-matcher.js.map +1 -0
- package/dist/processes/given-process.d.ts +18 -0
- package/dist/processes/given-process.d.ts.map +1 -0
- package/dist/processes/given-process.js +17 -0
- package/dist/processes/given-process.js.map +1 -0
- package/dist/processes/process-outcome.d.ts +20 -0
- package/dist/processes/process-outcome.d.ts.map +1 -0
- package/dist/processes/process-outcome.js +67 -0
- package/dist/processes/process-outcome.js.map +1 -0
- package/dist/scenarios/event-mismatches.d.ts.map +1 -1
- package/dist/scenarios/event-mismatches.js +1 -18
- package/dist/scenarios/event-mismatches.js.map +1 -1
- package/dist/scenarios/subset-differences.d.ts +3 -0
- package/dist/scenarios/subset-differences.d.ts.map +1 -0
- package/dist/scenarios/subset-differences.js +21 -0
- package/dist/scenarios/subset-differences.js.map +1 -0
- package/package.json +6 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"later.js","sourceRoot":"","sources":["../../../src/contracts/job-store/later.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC,0CAA0C;AAC1C,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ContractSuite } from '../contract-suite.js';
|
|
2
|
+
import type { JobStoreUnderContract } from './job-store-under-contract.js';
|
|
3
|
+
/** What a worker reports of an attempt: progress and checkpoint, success, failure, release. Only the attempt that holds the job is heard. */
|
|
4
|
+
export declare class ReportSuite extends ContractSuite<JobStoreUnderContract> {
|
|
5
|
+
protected tests(): void;
|
|
6
|
+
private progressAndOutcomes;
|
|
7
|
+
private deadAndRevived;
|
|
8
|
+
private releasedAndStale;
|
|
9
|
+
private claimFirst;
|
|
10
|
+
private claim;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=report-suite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-suite.d.ts","sourceRoot":"","sources":["../../../src/contracts/job-store/report-suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAIrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAI3E,6IAA6I;AAC7I,qBAAa,WAAY,SAAQ,aAAa,CAAC,qBAAqB,CAAC;IACnE,SAAS,CAAC,KAAK,IAAI,IAAI,CAItB;IAED,OAAO,CAAC,mBAAmB;IA6C3B,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,gBAAgB;YAsBV,UAAU;IAKxB,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ContractSuite } from '../contract-suite.js';
|
|
2
|
+
import { aJob } from './a-job.js';
|
|
3
|
+
import { LEASE_MS, NOW, QUEUE } from './job-fixtures.js';
|
|
4
|
+
import { later } from './later.js';
|
|
5
|
+
const FIRST = { id: 'job-1', attempt: 1 };
|
|
6
|
+
/** What a worker reports of an attempt: progress and checkpoint, success, failure, release. Only the attempt that holds the job is heard. */
|
|
7
|
+
export class ReportSuite extends ContractSuite {
|
|
8
|
+
tests() {
|
|
9
|
+
this.progressAndOutcomes();
|
|
10
|
+
this.deadAndRevived();
|
|
11
|
+
this.releasedAndStale();
|
|
12
|
+
}
|
|
13
|
+
progressAndOutcomes() {
|
|
14
|
+
this.it('shows the progress and hands the checkpoint to the next attempt', async () => {
|
|
15
|
+
await this.claimFirst();
|
|
16
|
+
this.expect(await this.subject.update(FIRST, { progress: { done: 40, total: 120 } }, later(LEASE_MS))).toBe(true);
|
|
17
|
+
this.expect(await this.subject.update(FIRST, { checkpoint: { after: 'c-1' } }, later(LEASE_MS))).toBe(true);
|
|
18
|
+
this.expect((await this.subject.find('job-1'))?.progress).toEqual({ done: 40, total: 120 });
|
|
19
|
+
const [resumed] = await this.claim(later(LEASE_MS));
|
|
20
|
+
this.expect([resumed?.attempt, resumed?.checkpoint]).toEqual([2, { after: 'c-1' }]);
|
|
21
|
+
});
|
|
22
|
+
this.it('finishes a job that succeeded: never handed out again', async () => {
|
|
23
|
+
await this.claimFirst();
|
|
24
|
+
this.expect(await this.subject.complete(FIRST, later(5_000))).toBe(true);
|
|
25
|
+
const job = await this.subject.find('job-1');
|
|
26
|
+
this.expect([job?.status, job?.finishedAt]).toEqual(['succeeded', later(5_000)]);
|
|
27
|
+
this.expect(await this.claim(later(LEASE_MS * 10))).toEqual([]);
|
|
28
|
+
this.expect(await this.subject.stats(QUEUE)).toEqual({ waiting: 0, running: 0, dead: 0 });
|
|
29
|
+
});
|
|
30
|
+
this.it('retries a failed job at its instant, not before, with its error code', async () => {
|
|
31
|
+
await this.claimFirst();
|
|
32
|
+
this.expect(await this.subject.fail(FIRST, { errorCode: 'LEDGER_DOWN', retryAt: later(5_000), at: NOW })).toBe(true);
|
|
33
|
+
const waiting = await this.subject.find('job-1');
|
|
34
|
+
this.expect([waiting?.status, waiting?.lastErrorCode, waiting?.runAt]).toEqual([
|
|
35
|
+
'waiting',
|
|
36
|
+
'LEDGER_DOWN',
|
|
37
|
+
later(5_000),
|
|
38
|
+
]);
|
|
39
|
+
this.expect(await this.claim(later(4_999))).toEqual([]);
|
|
40
|
+
this.expect((await this.claim(later(5_000)))[0]?.attempt).toBe(2);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
deadAndRevived() {
|
|
44
|
+
this.it('sets a dead job aside, then revives it with a fresh attempt budget', async () => {
|
|
45
|
+
await this.claimFirst();
|
|
46
|
+
await this.subject.fail(FIRST, { errorCode: 'LEDGER_DOWN', retryAt: null, at: NOW });
|
|
47
|
+
this.expect((await this.subject.find('job-1'))?.status).toBe('dead');
|
|
48
|
+
this.expect(await this.subject.stats(QUEUE)).toEqual({ waiting: 0, running: 0, dead: 1 });
|
|
49
|
+
this.expect(await this.claim(later(LEASE_MS * 10))).toEqual([]);
|
|
50
|
+
this.expect(await this.subject.retryDead([], later(1_000))).toBe(0);
|
|
51
|
+
this.expect(await this.subject.retryDead(['job-1', 'unknown'], later(1_000))).toBe(1);
|
|
52
|
+
const [revived] = await this.claim(later(1_000));
|
|
53
|
+
this.expect(revived?.attempt).toBe(1);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
releasedAndStale() {
|
|
57
|
+
this.it('takes a released job back without counting the attempt', async () => {
|
|
58
|
+
await this.claimFirst();
|
|
59
|
+
this.expect(await this.subject.release(FIRST, NOW)).toBe(true);
|
|
60
|
+
this.expect((await this.subject.find('job-1'))?.status).toBe('waiting');
|
|
61
|
+
this.expect((await this.claim(NOW))[0]?.attempt).toBe(1);
|
|
62
|
+
});
|
|
63
|
+
this.it('ignores an attempt whose lease was taken over', async () => {
|
|
64
|
+
await this.claimFirst();
|
|
65
|
+
await this.claim(later(LEASE_MS));
|
|
66
|
+
this.expect(await this.subject.update(FIRST, { progress: { done: 1 } }, later(LEASE_MS * 2))).toBe(false);
|
|
67
|
+
this.expect(await this.subject.complete(FIRST, later(LEASE_MS))).toBe(false);
|
|
68
|
+
this.expect(await this.subject.fail(FIRST, { errorCode: 'X', retryAt: null, at: NOW })).toBe(false);
|
|
69
|
+
this.expect(await this.subject.release(FIRST, NOW)).toBe(false);
|
|
70
|
+
this.expect((await this.subject.find('job-1'))?.status).toBe('running');
|
|
71
|
+
this.expect(await this.subject.complete({ id: 'job-1', attempt: 2 }, later(LEASE_MS))).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async claimFirst() {
|
|
75
|
+
await this.subject.add(aJob('job-1'));
|
|
76
|
+
await this.claim(NOW);
|
|
77
|
+
}
|
|
78
|
+
claim(now) {
|
|
79
|
+
return this.subject.claim({ queue: QUEUE, limit: 10, now, leaseMs: LEASE_MS });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=report-suite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-suite.js","sourceRoot":"","sources":["../../../src/contracts/job-store/report-suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAE1C,6IAA6I;AAC7I,MAAM,OAAO,WAAY,SAAQ,aAAoC;IACzD,KAAK;QACb,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEO,mBAAmB;QACzB,IAAI,CAAC,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;YACpF,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAExB,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClH,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5G,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAE5F,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAEpD,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;YAC1E,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAExB,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEzE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE7C,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;YACzF,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAExB,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5G,IAAI,CACL,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEjD,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC7E,SAAS;gBACT,aAAa;gBACb,KAAK,CAAC,KAAK,CAAC;aACb,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACxD,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;YACvF,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAErF,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1F,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAEtF,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;YAEjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YAExB,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/D,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxE,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAElC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1G,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7E,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpG,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,GAAS;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ContractSuite } from '../contract-suite.js';
|
|
2
|
+
import type { JobStoreUnderContract } from './job-store-under-contract.js';
|
|
3
|
+
/** The unit of work the store is used in, as the kernel port states it. */
|
|
4
|
+
export interface UnitOfWorkOfTheStore {
|
|
5
|
+
run<T>(work: () => Promise<T>): Promise<T>;
|
|
6
|
+
}
|
|
7
|
+
interface StoreInUnitOfWork {
|
|
8
|
+
readonly store: JobStoreUnderContract;
|
|
9
|
+
readonly unitOfWork: UnitOfWorkOfTheStore;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A job added inside a unit of work exists if and only if the unit of work committed; a job
|
|
13
|
+
* cancelled or revived inside one is cancelled or revived if and only if it committed.
|
|
14
|
+
*/
|
|
15
|
+
export declare class TransactionSuite extends ContractSuite<StoreInUnitOfWork> {
|
|
16
|
+
protected tests(): void;
|
|
17
|
+
private adds;
|
|
18
|
+
private cancels;
|
|
19
|
+
private revives;
|
|
20
|
+
private claimed;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=transaction-suite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-suite.d.ts","sourceRoot":"","sources":["../../../src/contracts/job-store/transaction-suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAIrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE3E,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC5C;AAED,UAAU,iBAAiB;IACzB,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;CAC3C;AAID;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,aAAa,CAAC,iBAAiB,CAAC;IACpE,SAAS,CAAC,KAAK,IAAI,IAAI,CAItB;IAED,OAAO,CAAC,IAAI;IAqBZ,OAAO,CAAC,OAAO;IAyBf,OAAO,CAAC,OAAO;YAmBD,OAAO;CAKtB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ContractSuite } from '../contract-suite.js';
|
|
2
|
+
import { failureOf } from '../failure-of.js';
|
|
3
|
+
import { aJob } from './a-job.js';
|
|
4
|
+
import { LEASE_MS, NOW, QUEUE } from './job-fixtures.js';
|
|
5
|
+
class CommandRefused extends Error {
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A job added inside a unit of work exists if and only if the unit of work committed; a job
|
|
9
|
+
* cancelled or revived inside one is cancelled or revived if and only if it committed.
|
|
10
|
+
*/
|
|
11
|
+
export class TransactionSuite extends ContractSuite {
|
|
12
|
+
tests() {
|
|
13
|
+
this.adds();
|
|
14
|
+
this.cancels();
|
|
15
|
+
this.revives();
|
|
16
|
+
}
|
|
17
|
+
adds() {
|
|
18
|
+
this.it('leaves no job behind a unit of work that rolled back', async () => {
|
|
19
|
+
const failure = await failureOf(() => this.subject.unitOfWork.run(async () => {
|
|
20
|
+
await this.subject.store.add(aJob('job-1'));
|
|
21
|
+
throw new CommandRefused();
|
|
22
|
+
}));
|
|
23
|
+
this.expect(failure instanceof CommandRefused).toBe(true);
|
|
24
|
+
this.expect(await this.subject.store.find('job-1')).toBe(undefined);
|
|
25
|
+
this.expect(await this.claimed()).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
this.it('keeps the job of a unit of work that committed', async () => {
|
|
28
|
+
await this.subject.unitOfWork.run(() => this.subject.store.add(aJob('job-1')));
|
|
29
|
+
this.expect(await this.claimed()).toEqual(['job-1']);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
cancels() {
|
|
33
|
+
this.it('leaves the job waiting when the unit of work that cancelled it rolled back', async () => {
|
|
34
|
+
await this.subject.store.add(aJob('job-1'));
|
|
35
|
+
await failureOf(() => this.subject.unitOfWork.run(async () => {
|
|
36
|
+
this.expect(await this.subject.store.cancel('job-1', NOW)).toBe(true);
|
|
37
|
+
throw new CommandRefused();
|
|
38
|
+
}));
|
|
39
|
+
this.expect((await this.subject.store.find('job-1'))?.status).toBe('waiting');
|
|
40
|
+
this.expect(await this.claimed()).toEqual(['job-1']);
|
|
41
|
+
});
|
|
42
|
+
this.it('cancels the job once the unit of work committed', async () => {
|
|
43
|
+
await this.subject.store.add(aJob('job-1'));
|
|
44
|
+
const cancelled = await this.subject.unitOfWork.run(() => this.subject.store.cancel('job-1', NOW));
|
|
45
|
+
this.expect([cancelled, (await this.subject.store.find('job-1'))?.status]).toEqual([true, 'cancelled']);
|
|
46
|
+
this.expect(await this.claimed()).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
revives() {
|
|
50
|
+
this.it('leaves the job dead when the unit of work that revived it rolled back', async () => {
|
|
51
|
+
await this.subject.store.add(aJob('job-1'));
|
|
52
|
+
await this.claimed();
|
|
53
|
+
await this.subject.store.fail({ id: 'job-1', attempt: 1 }, { errorCode: 'X', retryAt: null, at: NOW });
|
|
54
|
+
await failureOf(() => this.subject.unitOfWork.run(async () => {
|
|
55
|
+
this.expect(await this.subject.store.retryDead(['job-1'], NOW)).toBe(1);
|
|
56
|
+
throw new CommandRefused();
|
|
57
|
+
}));
|
|
58
|
+
this.expect((await this.subject.store.find('job-1'))?.status).toBe('dead');
|
|
59
|
+
this.expect(await this.subject.unitOfWork.run(() => this.subject.store.retryDead(['job-1'], NOW))).toBe(1);
|
|
60
|
+
this.expect((await this.subject.store.find('job-1'))?.status).toBe('waiting');
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async claimed() {
|
|
64
|
+
const jobs = await this.subject.store.claim({ queue: QUEUE, limit: 10, now: NOW, leaseMs: LEASE_MS });
|
|
65
|
+
return jobs.map((job) => job.id);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=transaction-suite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-suite.js","sourceRoot":"","sources":["../../../src/contracts/job-store/transaction-suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAazD,MAAM,cAAe,SAAQ,KAAK;CAAG;AAErC;;;GAGG;AACH,MAAM,OAAO,gBAAiB,SAAQ,aAAgC;IAC1D,KAAK;QACb,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACzE,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,CACnC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;gBACrC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC5C,MAAM,IAAI,cAAc,EAAE,CAAC;YAC7B,CAAC,CAAC,CACH,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,OAAO,YAAY,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACpE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAE/E,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;YAC/F,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAE5C,MAAM,SAAS,CAAC,GAAG,EAAE,CACnB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;gBACrC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtE,MAAM,IAAI,cAAc,EAAE,CAAC;YAC7B,CAAC,CAAC,CACH,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9E,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YACpE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAE5C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAEnG,IAAI,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;YACxG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;YAC1F,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5C,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAEvG,MAAM,SAAS,CAAC,GAAG,EAAE,CACnB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;gBACrC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACxE,MAAM,IAAI,cAAc,EAAE,CAAC;YAC7B,CAAC,CAAC,CACH,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3E,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3G,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEtG,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Awaitable } from './awaitable.js';
|
|
2
|
+
import type { ContractRunner } from './contract-runner.js';
|
|
3
|
+
import type { JobStoreUnderContract } from './job-store/job-store-under-contract.js';
|
|
4
|
+
import { type UnitOfWorkOfTheStore } from './job-store/transaction-suite.js';
|
|
5
|
+
export interface JobStoreContractOptions {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
/** A fresh, empty store; called before each test. */
|
|
8
|
+
createStore(): Awaitable<JobStoreUnderContract>;
|
|
9
|
+
/**
|
|
10
|
+
* The unit of work the store is used in, over the same database and the same context as the
|
|
11
|
+
* store just created: the contract then proves that a job added inside it exists if and only
|
|
12
|
+
* if it committed.
|
|
13
|
+
*/
|
|
14
|
+
readonly unitOfWork?: () => UnitOfWorkOfTheStore;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* What every `JobStore` guarantees, whatever it runs on: due jobs handed out earliest first and
|
|
18
|
+
* each to one claim, a lease that brings back the job of a worker that died, reports heard only
|
|
19
|
+
* from the attempt that holds the job, retries, dead jobs and their revival, cancellation,
|
|
20
|
+
* idempotency keys, housekeeping, and the enqueue sharing the fate of the unit of work.
|
|
21
|
+
* The claims of the contract ask for a lease of `30000` milliseconds.
|
|
22
|
+
*/
|
|
23
|
+
export declare function jobStoreContract(runner: ContractRunner, options: JobStoreContractOptions): void;
|
|
24
|
+
//# sourceMappingURL=job-store-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-store-contract.d.ts","sourceRoot":"","sources":["../../src/contracts/job-store-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAGrF,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAE/F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,WAAW,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,oBAAoB,CAAC;CAClD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAc/F"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ClaimSuite } from './job-store/claim-suite.js';
|
|
2
|
+
import { KeepSuite } from './job-store/keep-suite.js';
|
|
3
|
+
import { ReportSuite } from './job-store/report-suite.js';
|
|
4
|
+
import { TransactionSuite } from './job-store/transaction-suite.js';
|
|
5
|
+
/**
|
|
6
|
+
* What every `JobStore` guarantees, whatever it runs on: due jobs handed out earliest first and
|
|
7
|
+
* each to one claim, a lease that brings back the job of a worker that died, reports heard only
|
|
8
|
+
* from the attempt that holds the job, retries, dead jobs and their revival, cancellation,
|
|
9
|
+
* idempotency keys, housekeeping, and the enqueue sharing the fate of the unit of work.
|
|
10
|
+
* The claims of the contract ask for a lease of `30000` milliseconds.
|
|
11
|
+
*/
|
|
12
|
+
export function jobStoreContract(runner, options) {
|
|
13
|
+
const create = () => options.createStore();
|
|
14
|
+
new ClaimSuite(runner, create).describe(`${options.name} (job store contract: claims)`);
|
|
15
|
+
new ReportSuite(runner, create).describe(`${options.name} (job store contract: reports of an attempt)`);
|
|
16
|
+
new KeepSuite(runner, create).describe(`${options.name} (job store contract: cancellation and housekeeping)`);
|
|
17
|
+
const { unitOfWork } = options;
|
|
18
|
+
if (unitOfWork !== undefined) {
|
|
19
|
+
new TransactionSuite(runner, async () => ({ store: await create(), unitOfWork: unitOfWork() })).describe(`${options.name} (job store contract: in a unit of work)`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=job-store-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-store-contract.js","sourceRoot":"","sources":["../../src/contracts/job-store-contract.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAA6B,MAAM,kCAAkC,CAAC;AAc/F;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAsB,EAAE,OAAgC;IACvF,MAAM,MAAM,GAAG,GAAqC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAE7E,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,+BAA+B,CAAC,CAAC;IACxF,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,8CAA8C,CAAC,CAAC;IACxG,IAAI,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,sDAAsD,CAAC,CAAC;IAE9G,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE/B,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,gBAAgB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,MAAM,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CACtG,GAAG,OAAO,CAAC,IAAI,0CAA0C,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { StoredProcess } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import type { Awaitable } from './awaitable.js';
|
|
3
|
+
import type { ContractRunner } from './contract-runner.js';
|
|
4
|
+
/** The process store port of the kernel, restated so that a store of any package fits. */
|
|
5
|
+
export interface ProcessStoreUnderContract {
|
|
6
|
+
load(name: string, id: string): Promise<StoredProcess | undefined>;
|
|
7
|
+
save(process: StoredProcess): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** The unit of work the store is used in, as the kernel port states it. */
|
|
10
|
+
interface UnitOfWorkOfTheStore {
|
|
11
|
+
run<T>(work: () => Promise<T>): Promise<T>;
|
|
12
|
+
}
|
|
13
|
+
export interface ProcessStoreContractOptions {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/** A fresh, empty store; called before each test. */
|
|
16
|
+
createStore(): Awaitable<ProcessStoreUnderContract>;
|
|
17
|
+
/** The unit of work over the same context as the store just created: the contract then proves that a rolled back save leaves nothing. */
|
|
18
|
+
readonly unitOfWork?: () => UnitOfWorkOfTheStore;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What every `ProcessStore` guarantees: an instance comes back as saved, JSON state, armed
|
|
22
|
+
* timeouts and end included; a save at a version that is not the next one is refused with the
|
|
23
|
+
* code `CONCURRENCY_CONFLICT`, also when two first saves race; and, in a unit of work, a save
|
|
24
|
+
* shares its fate.
|
|
25
|
+
*/
|
|
26
|
+
export declare function processStoreContract(runner: ContractRunner, options: ProcessStoreContractOptions): void;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=process-store-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-store-contract.d.ts","sourceRoot":"","sources":["../../src/contracts/process-store-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,0FAA0F;AAC1F,MAAM,WAAW,yBAAyB;IACxC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IACnE,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED,2EAA2E;AAC3E,UAAU,oBAAoB;IAC5B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,WAAW,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;IACpD,yIAAyI;IACzI,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,oBAAoB,CAAC;CAClD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAEvG"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ContractSuite } from './contract-suite.js';
|
|
2
|
+
import { failureOf } from './failure-of.js';
|
|
3
|
+
/**
|
|
4
|
+
* What every `ProcessStore` guarantees: an instance comes back as saved, JSON state, armed
|
|
5
|
+
* timeouts and end included; a save at a version that is not the next one is refused with the
|
|
6
|
+
* code `CONCURRENCY_CONFLICT`, also when two first saves race; and, in a unit of work, a save
|
|
7
|
+
* shares its fate.
|
|
8
|
+
*/
|
|
9
|
+
export function processStoreContract(runner, options) {
|
|
10
|
+
new ProcessStoreSuite(runner, options).describe(`${options.name} (process store contract)`);
|
|
11
|
+
}
|
|
12
|
+
const NAME = 'billing.dunning';
|
|
13
|
+
class ProcessStoreSuite extends ContractSuite {
|
|
14
|
+
options;
|
|
15
|
+
constructor(runner, options) {
|
|
16
|
+
super(runner, () => options.createStore());
|
|
17
|
+
this.options = options;
|
|
18
|
+
}
|
|
19
|
+
tests() {
|
|
20
|
+
this.keeps();
|
|
21
|
+
this.refusesStaleVersions();
|
|
22
|
+
if (this.options.unitOfWork !== undefined) {
|
|
23
|
+
this.sharesTheUnitOfWork(this.options.unitOfWork);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
keeps() {
|
|
27
|
+
this.it('knows no instance before it is saved, and gives one back as saved', async () => {
|
|
28
|
+
this.expect(await this.subject.load(NAME, 'invoice-1')).toBe(undefined);
|
|
29
|
+
await this.subject.save(anInstance(1));
|
|
30
|
+
this.expect(await this.subject.load(NAME, 'invoice-1')).toEqual(anInstance(1));
|
|
31
|
+
this.expect(await this.subject.load('billing.collection', 'invoice-1')).toBe(undefined);
|
|
32
|
+
});
|
|
33
|
+
this.it('keeps the next versions, the end and the timeouts', async () => {
|
|
34
|
+
await this.subject.save(anInstance(1));
|
|
35
|
+
await this.subject.save({ ...anInstance(2), ended: true, timeouts: {} });
|
|
36
|
+
this.expect(await this.subject.load(NAME, 'invoice-1')).toEqual({ ...anInstance(2), ended: true, timeouts: {} });
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
refusesStaleVersions() {
|
|
40
|
+
this.it('refuses a version that is not the next one', async () => {
|
|
41
|
+
await this.subject.save(anInstance(1));
|
|
42
|
+
const failures = [
|
|
43
|
+
await failureOf(() => this.subject.save(anInstance(1))),
|
|
44
|
+
await failureOf(() => this.subject.save(anInstance(3))),
|
|
45
|
+
];
|
|
46
|
+
this.expect(failures.map(codeOf)).toEqual(['CONCURRENCY_CONFLICT', 'CONCURRENCY_CONFLICT']);
|
|
47
|
+
this.expect((await this.subject.load(NAME, 'invoice-1'))?.version).toBe(1);
|
|
48
|
+
});
|
|
49
|
+
this.it('lets one of two first saves of an instance win', async () => {
|
|
50
|
+
const outcomes = await Promise.all([
|
|
51
|
+
failureOf(() => this.subject.save(anInstance(1))),
|
|
52
|
+
failureOf(() => this.subject.save({ ...anInstance(1), state: { stage: 'other' } })),
|
|
53
|
+
]);
|
|
54
|
+
this.expect(outcomes.filter((failure) => failure === undefined).length).toBe(1);
|
|
55
|
+
this.expect(outcomes.map(codeOf).filter((code) => code !== undefined)).toEqual(['CONCURRENCY_CONFLICT']);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
sharesTheUnitOfWork(unitOfWork) {
|
|
59
|
+
this.it('leaves nothing behind a unit of work that rolled back', async () => {
|
|
60
|
+
await failureOf(() => unitOfWork().run(async () => {
|
|
61
|
+
await this.subject.save(anInstance(1));
|
|
62
|
+
throw new Error('the step failed');
|
|
63
|
+
}));
|
|
64
|
+
this.expect(await this.subject.load(NAME, 'invoice-1')).toBe(undefined);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function anInstance(version) {
|
|
69
|
+
return {
|
|
70
|
+
name: NAME,
|
|
71
|
+
id: 'invoice-1',
|
|
72
|
+
state: { stage: 'reminded', reminders: [1, 2], amount: 12.5, note: null },
|
|
73
|
+
version,
|
|
74
|
+
ended: false,
|
|
75
|
+
timeouts: { 'second-reminder': 'job-7' },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function codeOf(failure) {
|
|
79
|
+
return typeof failure === 'object' && failure !== null ? Reflect.get(failure, 'code') : undefined;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=process-store-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-store-contract.js","sourceRoot":"","sources":["../../src/contracts/process-store-contract.ts"],"names":[],"mappings":"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"}
|
package/dist/contracts.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export { followWithFetch } from './contracts/file-storage/follow-with-fetch.js';
|
|
|
15
15
|
export type { FollowedResponse } from './contracts/file-storage/followed-response.js';
|
|
16
16
|
export type { SignedRequest } from './contracts/file-storage/signed-request.js';
|
|
17
17
|
export { jobSchedulerContract, type JobSchedulerContractOptions, type JobSchedulerUnderContract, } from './contracts/job-scheduler-contract.js';
|
|
18
|
+
export { eventStoreContract, type EventStoreContractOptions } from './contracts/event-store-contract.js';
|
|
19
|
+
export type { EventStoreUnderContract } from './contracts/event-store/event-store-under-contract.js';
|
|
20
|
+
export { eventStoreUnitOfWorkContract, type EventStoreUnitOfWorkContractOptions, } from './contracts/event-store-unit-of-work-contract.js';
|
|
21
|
+
export { jobStoreContract, type JobStoreContractOptions } from './contracts/job-store-contract.js';
|
|
22
|
+
export type { JobStoreUnderContract } from './contracts/job-store/job-store-under-contract.js';
|
|
18
23
|
export { lockContract, type LockContractOptions } from './contracts/lock-contract.js';
|
|
19
24
|
export { loggerContract, type LoggerContractOptions } from './contracts/logger-contract.js';
|
|
20
25
|
export type { CreateLogger } from './contracts/logger/create-logger.js';
|
|
@@ -22,6 +27,7 @@ export type { LoggedLine } from './contracts/logger/logged-line.js';
|
|
|
22
27
|
export type { LoggerContractSetup } from './contracts/logger/logger-contract-setup.js';
|
|
23
28
|
export type { LoggerUnderContract } from './contracts/logger/logger-under-contract.js';
|
|
24
29
|
export { outboxStoreContract, type OutboxStoreContractOptions, type OutboxStoreUnderContract, } from './contracts/outbox-store-contract.js';
|
|
30
|
+
export { processStoreContract, type ProcessStoreContractOptions, type ProcessStoreUnderContract, } from './contracts/process-store-contract.js';
|
|
25
31
|
export { processedEventStoreContract, type ProcessedEventStoreContractOptions, } from './contracts/processed-event-store-contract.js';
|
|
26
32
|
export { rateLimiterContract, type RateLimiterContractOptions } from './contracts/rate-limiter-contract.js';
|
|
27
33
|
export { readModelStoreContract, type ReadModelStoreContractOptions } from './contracts/read-model-store-contract.js';
|
package/dist/contracts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACzF,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,GAC5B,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sDAAsD,CAAC;AAClG,YAAY,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,GAC3B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,2DAA2D,CAAC;AAC5G,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,+CAA+C,CAAC;AAChF,YAAY,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,YAAY,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAChF,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,GAC/B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5F,YAAY,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACxE,YAAY,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,YAAY,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AACvF,YAAY,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AACvF,OAAO,EACL,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,GAC9B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,2BAA2B,EAC3B,KAAK,kCAAkC,GACxC,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAC5G,OAAO,EAAE,sBAAsB,EAAE,KAAK,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACtH,YAAY,EAAE,2BAA2B,EAAE,MAAM,2DAA2D,CAAC;AAC7G,OAAO,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACxG,OAAO,EACL,uBAAuB,EACvB,KAAK,8BAA8B,GACpC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAC1G,YAAY,EAAE,uBAAuB,EAAE,MAAM,yDAAyD,CAAC"}
|
|
1
|
+
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
|
package/dist/contracts.js
CHANGED
|
@@ -8,9 +8,13 @@ export { fileStorageContract } from './contracts/file-storage-contract.js';
|
|
|
8
8
|
export { followServed } from './contracts/file-storage/follow-served.js';
|
|
9
9
|
export { followWithFetch } from './contracts/file-storage/follow-with-fetch.js';
|
|
10
10
|
export { jobSchedulerContract, } from './contracts/job-scheduler-contract.js';
|
|
11
|
+
export { eventStoreContract } from './contracts/event-store-contract.js';
|
|
12
|
+
export { eventStoreUnitOfWorkContract, } from './contracts/event-store-unit-of-work-contract.js';
|
|
13
|
+
export { jobStoreContract } from './contracts/job-store-contract.js';
|
|
11
14
|
export { lockContract } from './contracts/lock-contract.js';
|
|
12
15
|
export { loggerContract } from './contracts/logger-contract.js';
|
|
13
16
|
export { outboxStoreContract, } from './contracts/outbox-store-contract.js';
|
|
17
|
+
export { processStoreContract, } from './contracts/process-store-contract.js';
|
|
14
18
|
export { processedEventStoreContract, } from './contracts/processed-event-store-contract.js';
|
|
15
19
|
export { rateLimiterContract } from './contracts/rate-limiter-contract.js';
|
|
16
20
|
export { readModelStoreContract } from './contracts/read-model-store-contract.js';
|
package/dist/contracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA6B,MAAM,+BAA+B,CAAC;AAEzF,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EACL,gBAAgB,GAGjB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,sDAAsD,CAAC;AAElG,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,gBAAgB,GAGjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,+CAA+C,CAAC;AAGhF,OAAO,EACL,oBAAoB,GAGrB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAA4B,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,cAAc,EAA8B,MAAM,gCAAgC,CAAC;AAK5F,OAAO,EACL,mBAAmB,GAGpB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,2BAA2B,GAE5B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAmC,MAAM,sCAAsC,CAAC;AAC5G,OAAO,EAAE,sBAAsB,EAAsC,MAAM,0CAA0C,CAAC;AAEtH,OAAO,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAkC,MAAM,oCAAoC,CAAC;AACxG,OAAO,EACL,uBAAuB,GAExB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAkC,MAAM,sCAAsC,CAAC"}
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA6B,MAAM,+BAA+B,CAAC;AAEzF,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EACL,gBAAgB,GAGjB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,sDAAsD,CAAC;AAElG,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,gBAAgB,GAGjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,+CAA+C,CAAC;AAGhF,OAAO,EACL,oBAAoB,GAGrB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAkC,MAAM,qCAAqC,CAAC;AAEzG,OAAO,EACL,4BAA4B,GAE7B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAgC,MAAM,mCAAmC,CAAC;AAEnG,OAAO,EAAE,YAAY,EAA4B,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,cAAc,EAA8B,MAAM,gCAAgC,CAAC;AAK5F,OAAO,EACL,mBAAmB,GAGpB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,oBAAoB,GAGrB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,2BAA2B,GAE5B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAmC,MAAM,sCAAsC,CAAC;AAC5G,OAAO,EAAE,sBAAsB,EAAsC,MAAM,0CAA0C,CAAC;AAEtH,OAAO,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAkC,MAAM,oCAAoC,CAAC;AACxG,OAAO,EACL,uBAAuB,GAExB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAkC,MAAM,sCAAsC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DomainEvent } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import type { SnapshotEquivalence } from './snapshot-equivalence.js';
|
|
3
|
+
/**
|
|
4
|
+
* Proves that the snapshots of an aggregate give the state a full replay gives. For every version
|
|
5
|
+
* of the history, the aggregate replayed up to it is encoded as its snapshot policy would store it,
|
|
6
|
+
* decoded, and rebuilt from that snapshot and the events after it: its state and its version must
|
|
7
|
+
* equal those of the aggregate replayed from the first event. Throws an AssertionError at the
|
|
8
|
+
* first version that differs. A personal value is read back as it was given, as a cipher would
|
|
9
|
+
* open it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function expectSnapshotEquivalence<A extends {
|
|
12
|
+
readonly version: number;
|
|
13
|
+
}, E extends DomainEvent, S>(proof: SnapshotEquivalence<A, E, S>): void;
|
|
14
|
+
//# sourceMappingURL=expect-snapshot-equivalence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expect-snapshot-equivalence.d.ts","sourceRoot":"","sources":["../../src/event-sourcing/expect-snapshot-equivalence.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,WAAW,EAGjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,SAAS,WAAW,EAAE,CAAC,EACxG,KAAK,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAClC,IAAI,CAiBN"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AssertionError } from 'node:assert';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { StoredEventData, isStoredData, isStoredList, storedDataOf, } from '@ddd-ts-kit/kernel';
|
|
4
|
+
/**
|
|
5
|
+
* Proves that the snapshots of an aggregate give the state a full replay gives. For every version
|
|
6
|
+
* of the history, the aggregate replayed up to it is encoded as its snapshot policy would store it,
|
|
7
|
+
* decoded, and rebuilt from that snapshot and the events after it: its state and its version must
|
|
8
|
+
* equal those of the aggregate replayed from the first event. Throws an AssertionError at the
|
|
9
|
+
* first version that differs. A personal value is read back as it was given, as a cipher would
|
|
10
|
+
* open it.
|
|
11
|
+
*/
|
|
12
|
+
export function expectSnapshotEquivalence(proof) {
|
|
13
|
+
const { events } = proof;
|
|
14
|
+
const id = events[0]?.aggregateId ?? '';
|
|
15
|
+
const replayed = proof.rehydrate(id, { events });
|
|
16
|
+
for (let at = 1; at <= events.length; at += 1) {
|
|
17
|
+
const restored = fromSnapshotAt(proof, id, at);
|
|
18
|
+
if (restored.version !== replayed.version || !isDeepStrictEqual(proof.state(restored), proof.state(replayed))) {
|
|
19
|
+
throw new AssertionError({
|
|
20
|
+
message: `Rebuilt from a snapshot taken after event ${at} of ${events.length}, the aggregate differs from its full replay.`,
|
|
21
|
+
actual: { version: restored.version, state: proof.state(restored) },
|
|
22
|
+
expected: { version: replayed.version, state: proof.state(replayed) },
|
|
23
|
+
operator: 'expectSnapshotEquivalence',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function fromSnapshotAt(proof, id, at) {
|
|
29
|
+
const { events, policy } = proof;
|
|
30
|
+
const kept = opened(storedDataOf(policy.encode(proof.rehydrate(id, { events: events.slice(0, at) }))));
|
|
31
|
+
const state = policy.decode(new StoredEventData(kept, `the snapshot after event ${at}`));
|
|
32
|
+
return proof.rehydrate(id, { snapshot: { version: at, state }, events: events.slice(at) });
|
|
33
|
+
}
|
|
34
|
+
/** The data as a store would give it back: a copy, each personal value in place of its marker. */
|
|
35
|
+
function opened(data) {
|
|
36
|
+
return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, openedValue(value)]));
|
|
37
|
+
}
|
|
38
|
+
function openedValue(value) {
|
|
39
|
+
if (isStoredList(value)) {
|
|
40
|
+
return value.map(openedValue);
|
|
41
|
+
}
|
|
42
|
+
if (!isStoredData(value)) {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
const personal = value['$personal'];
|
|
46
|
+
return isStoredData(personal) && typeof personal['value'] === 'string' ? personal['value'] : opened(value);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=expect-snapshot-equivalence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expect-snapshot-equivalence.js","sourceRoot":"","sources":["../../src/event-sourcing/expect-snapshot-equivalence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AggregateHistory, DomainEvent } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import type { EventSource } from '../scenarios/event-source.js';
|
|
3
|
+
import { type Scenario } from '../scenarios/scenario.js';
|
|
4
|
+
/**
|
|
5
|
+
* Starts a scenario of an event-sourced aggregate from its history, without a database: the
|
|
6
|
+
* aggregate is rebuilt by replaying `events` through `rehydrate`, then `when` runs one command and
|
|
7
|
+
* `thenEvents` asserts what it recorded.
|
|
8
|
+
* `givenEvents((id, history) => Invoice.rehydrate(id, history), [issued]).when((invoice) => invoice.pay(now)).thenEvents([InvoicePaid])`.
|
|
9
|
+
* The id is the one the events carry; give it for an empty history.
|
|
10
|
+
*/
|
|
11
|
+
export declare function givenEvents<A extends EventSource, E extends DomainEvent>(rehydrate: (id: string, history: AggregateHistory<E>) => A, events: readonly E[], id?: string): Scenario<A>;
|
|
12
|
+
//# sourceMappingURL=given-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"given-events.d.ts","sourceRoot":"","sources":["../../src/event-sourcing/given-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,WAAW,EACtE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,EAC1D,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,EAAE,CAAC,EAAE,MAAM,GACV,QAAQ,CAAC,CAAC,CAAC,CAQb"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { scenario } from '../scenarios/scenario.js';
|
|
2
|
+
/**
|
|
3
|
+
* Starts a scenario of an event-sourced aggregate from its history, without a database: the
|
|
4
|
+
* aggregate is rebuilt by replaying `events` through `rehydrate`, then `when` runs one command and
|
|
5
|
+
* `thenEvents` asserts what it recorded.
|
|
6
|
+
* `givenEvents((id, history) => Invoice.rehydrate(id, history), [issued]).when((invoice) => invoice.pay(now)).thenEvents([InvoicePaid])`.
|
|
7
|
+
* The id is the one the events carry; give it for an empty history.
|
|
8
|
+
*/
|
|
9
|
+
export function givenEvents(rehydrate, events, id) {
|
|
10
|
+
const identity = id ?? events[0]?.aggregateId;
|
|
11
|
+
if (identity === undefined) {
|
|
12
|
+
throw new TypeError('givenEvents needs the id of the aggregate when the history is empty');
|
|
13
|
+
}
|
|
14
|
+
return scenario(() => rehydrate(identity, { events }));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=given-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"given-events.js","sourceRoot":"","sources":["../../src/event-sourcing/given-events.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,SAA0D,EAC1D,MAAoB,EACpB,EAAW;IAEX,MAAM,QAAQ,GAAG,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC;IAE9C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,SAAS,CAAC,qEAAqE,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AggregateHistory, DomainEvent, SnapshotPolicy } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/** What `expectSnapshotEquivalence` replays, both ways. */
|
|
3
|
+
export interface SnapshotEquivalence<A, E extends DomainEvent, S> {
|
|
4
|
+
/** A history long enough to cross the snapshot interval, in order. */
|
|
5
|
+
readonly events: readonly E[];
|
|
6
|
+
rehydrate(id: string, history: AggregateHistory<E, S>): A;
|
|
7
|
+
/** The snapshot policy of the repository: its `encode` and `decode` are what is proven. */
|
|
8
|
+
readonly policy: Pick<SnapshotPolicy<A, S>, 'encode' | 'decode'>;
|
|
9
|
+
/** What two aggregates must share: their state as a value. */
|
|
10
|
+
state(aggregate: A): unknown;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=snapshot-equivalence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-equivalence.d.ts","sourceRoot":"","sources":["../../src/event-sourcing/snapshot-equivalence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAExF,2DAA2D;AAC3D,MAAM,WAAW,mBAAmB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,EAAE,CAAC;IAC9D,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1D,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACjE,8DAA8D;IAC9D,KAAK,CAAC,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC;CAC9B"}
|