@cratis/chronicle 6.21.0 → 6.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/reactors/ReactorDispatcher.d.ts +20 -0
  2. package/dist/reactors/ReactorDispatcher.d.ts.map +1 -0
  3. package/dist/reactors/ReactorDispatcher.js +76 -0
  4. package/dist/reactors/ReactorDispatcher.js.map +1 -0
  5. package/dist/reactors/ReactorSideEffects.d.ts +3 -0
  6. package/dist/reactors/ReactorSideEffects.d.ts.map +1 -1
  7. package/dist/reactors/ReactorSideEffects.js +16 -11
  8. package/dist/reactors/ReactorSideEffects.js.map +1 -1
  9. package/dist/reactors/Reactors.d.ts +0 -1
  10. package/dist/reactors/Reactors.d.ts.map +1 -1
  11. package/dist/reactors/Reactors.js +5 -71
  12. package/dist/reactors/Reactors.js.map +1 -1
  13. package/dist/testing/InProcessEventSequence.d.ts +8 -0
  14. package/dist/testing/InProcessEventSequence.d.ts.map +1 -1
  15. package/dist/testing/InProcessEventSequence.js +14 -1
  16. package/dist/testing/InProcessEventSequence.js.map +1 -1
  17. package/dist/testing/ReactorDeliveryResult.d.ts +10 -0
  18. package/dist/testing/ReactorDeliveryResult.d.ts.map +1 -0
  19. package/dist/testing/ReactorDeliveryResult.js +4 -0
  20. package/dist/testing/ReactorDeliveryResult.js.map +1 -0
  21. package/dist/testing/ReactorScenario.d.ts +55 -0
  22. package/dist/testing/ReactorScenario.d.ts.map +1 -0
  23. package/dist/testing/ReactorScenario.js +275 -0
  24. package/dist/testing/ReactorScenario.js.map +1 -0
  25. package/dist/testing/ReactorScenarioOptions.d.ts +17 -0
  26. package/dist/testing/ReactorScenarioOptions.d.ts.map +1 -0
  27. package/dist/testing/ReactorScenarioOptions.js +4 -0
  28. package/dist/testing/ReactorScenarioOptions.js.map +1 -0
  29. package/dist/testing/RecordedReactorSideEffect.d.ts +13 -0
  30. package/dist/testing/RecordedReactorSideEffect.d.ts.map +1 -0
  31. package/dist/testing/RecordedReactorSideEffect.js +4 -0
  32. package/dist/testing/RecordedReactorSideEffect.js.map +1 -0
  33. package/dist/testing/UnsupportedReactorOperation.d.ts +7 -0
  34. package/dist/testing/UnsupportedReactorOperation.d.ts.map +1 -0
  35. package/dist/testing/UnsupportedReactorOperation.js +14 -0
  36. package/dist/testing/UnsupportedReactorOperation.js.map +1 -0
  37. package/dist/testing/index.d.ts +5 -0
  38. package/dist/testing/index.d.ts.map +1 -1
  39. package/dist/testing/index.js +2 -0
  40. package/dist/testing/index.js.map +1 -1
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +1 -1
  43. package/reactors/ReactorDispatcher.ts +95 -0
  44. package/reactors/ReactorSideEffects.ts +21 -13
  45. package/reactors/Reactors.ts +11 -83
  46. package/testing/InProcessEventSequence.ts +16 -1
  47. package/testing/ReactorDeliveryResult.ts +13 -0
  48. package/testing/ReactorScenario.rejections.spec.ts +100 -0
  49. package/testing/ReactorScenario.spec.ts +611 -0
  50. package/testing/ReactorScenario.ts +279 -0
  51. package/testing/ReactorScenarioOptions.ts +20 -0
  52. package/testing/RecordedReactorSideEffect.ts +16 -0
  53. package/testing/UnsupportedReactorOperation.ts +10 -0
  54. package/testing/index.ts +5 -0
@@ -0,0 +1,100 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { field } from '@cratis/fundamentals';
5
+ import { chai, describe, it } from 'vitest';
6
+ import { ArtifactCompletionFailed } from '../artifacts/ArtifactCompletionFailed.js';
7
+ import { eventType } from '../events/eventTypeDecorator.js';
8
+ import { reactor } from '../reactors/reactor.js';
9
+ import { ReactorScenario } from './ReactorScenario.js';
10
+ import { UnsupportedReactorOperation } from './UnsupportedReactorOperation.js';
11
+
12
+ chai.should();
13
+ @eventType('rejection-event')
14
+ class Registered {
15
+ @field(String) name: string;
16
+ constructor(name: string) { this.name = name; }
17
+ }
18
+ class Unregistered { constructor(readonly name: string) {} }
19
+ const options = { artifacts: { eventTypes: [Registered] }, constraints: 'disabled' as const };
20
+
21
+ async function rejected(operation: string, call: () => Promise<unknown>): Promise<void> {
22
+ await call().then(() => { throw new Error('expected rejection'); }, error => {
23
+ (error instanceof UnsupportedReactorOperation).should.be.true;
24
+ error.operation.should.equal(operation);
25
+ error.message.should.contain('Use a kernel-backed test.');
26
+ });
27
+ }
28
+
29
+ describe('unsupported reactor operations', () => {
30
+ it('rejects missing registration, custom sequence and commands during configuration', () => {
31
+ class Plain {}
32
+ (() => new ReactorScenario(Plain, options)).should.throw(UnsupportedReactorOperation, 'Use a kernel-backed test.');
33
+ @reactor('custom-sequence', 'custom') class Custom { registered() {} }
34
+ (() => new ReactorScenario(Custom, options)).should.throw(UnsupportedReactorOperation, 'Use a kernel-backed test.');
35
+ @reactor('commands') class Commands { registered() {} }
36
+ (() => new ReactorScenario(Commands, { ...options, commandTypes: [Unregistered] }))
37
+ .should.throw(UnsupportedReactorOperation, 'Use a kernel-backed test.');
38
+ });
39
+
40
+ it('rejects replay and redelivery asynchronously without mutation', async () => {
41
+ @reactor('replay-rejection') class R { registered() {} }
42
+ const scenario = new ReactorScenario(R, options);
43
+ await rejected('replay', () => scenario.replay());
44
+ await rejected('redeliver', () => scenario.redeliver());
45
+ await rejected('when.events', () => scenario.when.forEventSource('A').events());
46
+ scenario.results.length.should.equal(0);
47
+ scenario.sideEffects.length.should.equal(0);
48
+ });
49
+
50
+ it('rejects unsupported returned values instead of silently dropping them', async () => {
51
+ for (const value of [{ command: true }, [new Registered('ok'), { command: true }], [undefined]]) {
52
+ @reactor('unknown-result') class R { registered() { return value; } }
53
+ const scenario = new ReactorScenario(R, options);
54
+ await rejected('handler.return', () => scenario.when.forEventSource('A').events(new Registered('one')));
55
+ scenario.sideEffects.length.should.equal(0);
56
+ scenario.results[0].completed.should.be.false;
57
+ }
58
+ });
59
+
60
+ it('rejects unsupported setup before invocation and without delivery results', async () => {
61
+ let called = 0;
62
+ @reactor('unsupported-input') class R { registered() { called++; } }
63
+ const scenario = new ReactorScenario(R, options);
64
+ await scenario.given.forEventSource('A').events(new Registered('ok'), new Unregistered('bad')).then(() => {
65
+ throw new Error('expected rejection');
66
+ }, error => { error.message.should.contain('Use a kernel-backed test.'); });
67
+ called.should.equal(0);
68
+ scenario.results.length.should.equal(0);
69
+ await scenario.when.forEventSource('A').events(new Registered('after'));
70
+ scenario.results[0].handled[0].sequenceNumber.should.equal(0n);
71
+ });
72
+
73
+ it('rejects missing read-model services instead of supplying fake data', async () => {
74
+ @reactor('read-model-rejection') class R {
75
+ registered(_event: Registered, _context: unknown, services: { readModels: { findInstanceById(): unknown } }) {
76
+ return services.readModels.findInstanceById();
77
+ }
78
+ }
79
+ const scenario = new ReactorScenario(R, options);
80
+ await rejected('services.readModels.findInstanceById', () => scenario.when.forEventSource('A').events(new Registered('one')));
81
+ });
82
+
83
+ it('preserves processing-only, completion-only and activation failures', async () => {
84
+ @reactor('failure-rejection') class R { registered() { throw new Error('handler'); } }
85
+ const processing = new ReactorScenario(R, { ...options, artifactActivator: type => ({ instance: new type() }) });
86
+ await processing.when.forEventSource('A').events(new Registered('one')).then(() => { throw new Error('expected rejection'); },
87
+ error => { String(error).should.contain('handler'); });
88
+ processing.results[0].completed.should.be.false;
89
+ @reactor('completion-rejection') class Complete { registered() {} }
90
+ const completion = new ReactorScenario(Complete, { ...options, artifactActivator: type => ({ instance: new type(),
91
+ complete: () => { throw new Error('completion'); } }) });
92
+ await completion.when.forEventSource('A').events(new Registered('one')).then(() => { throw new Error('expected rejection'); },
93
+ error => { (error instanceof ArtifactCompletionFailed).should.be.true; chai.expect(error.processingError).to.be.undefined; });
94
+ completion.results[0].completed.should.be.false;
95
+ const activation = new ReactorScenario(Complete, { ...options, artifactActivator: () => { throw new Error('activation'); } });
96
+ await activation.when.forEventSource('A').events(new Registered('one')).then(() => { throw new Error('expected rejection'); },
97
+ error => { String(error).should.contain('activation'); });
98
+ activation.results[0].handled.length.should.equal(0);
99
+ });
100
+ });