@kronos-ts/test 0.1.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/dist/fixture.d.ts +78 -0
- package/dist/fixture.d.ts.map +1 -0
- package/dist/fixture.js +424 -0
- package/dist/fixture.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/recording-enhancer.d.ts +40 -0
- package/dist/recording-enhancer.d.ts.map +1 -0
- package/dist/recording-enhancer.js +82 -0
- package/dist/recording-enhancer.js.map +1 -0
- package/package.json +57 -0
- package/src/fixture.ts +535 -0
- package/src/index.ts +18 -0
- package/src/recording-enhancer.ts +113 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Extension, App } from "@kronos-ts/app"
|
|
2
|
+
import type { CommandBus, CommandMessage, EventMessage } from "@kronos-ts/messaging"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Recorded state from the test fixture.
|
|
6
|
+
* Events and commands are captured by decorators installed at the
|
|
7
|
+
* INNERMOST position (after all interceptors have run).
|
|
8
|
+
*/
|
|
9
|
+
export interface Recordings {
|
|
10
|
+
/** Events recorded since the last reset. */
|
|
11
|
+
events(): ReadonlyArray<EventMessage>
|
|
12
|
+
/** Commands dispatched since the last reset. */
|
|
13
|
+
commands(): ReadonlyArray<CommandMessage>
|
|
14
|
+
/** Clear all recordings. Called between Given and When phases. */
|
|
15
|
+
reset(): void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Internal-shape extension of {@link Recordings} carrying the writer pair
|
|
20
|
+
* used by the decorators created in {@link testRecordingExtension}. Kept
|
|
21
|
+
* `private` to the module — callers see only the {@link Recordings} surface.
|
|
22
|
+
*/
|
|
23
|
+
interface RecordingsInternal extends Recordings {
|
|
24
|
+
readonly _push: {
|
|
25
|
+
event: (e: EventMessage) => void
|
|
26
|
+
command: (c: CommandMessage) => void
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a fresh Recordings handle. Pass it into {@link testRecordingExtension}
|
|
32
|
+
* so the fixture and the decorators share the same backing arrays.
|
|
33
|
+
*
|
|
34
|
+
* Replaces the legacy "register testRecordings as a component, retrieve via
|
|
35
|
+
* configuration.getComponent" pattern (removed in Phase 8).
|
|
36
|
+
*/
|
|
37
|
+
export function createRecordings(): Recordings {
|
|
38
|
+
const recordedEvents: EventMessage[] = []
|
|
39
|
+
const recordedCommands: CommandMessage[] = []
|
|
40
|
+
const internal: RecordingsInternal = {
|
|
41
|
+
events() {
|
|
42
|
+
return [...recordedEvents]
|
|
43
|
+
},
|
|
44
|
+
commands() {
|
|
45
|
+
return [...recordedCommands]
|
|
46
|
+
},
|
|
47
|
+
reset() {
|
|
48
|
+
recordedEvents.length = 0
|
|
49
|
+
recordedCommands.length = 0
|
|
50
|
+
},
|
|
51
|
+
_push: {
|
|
52
|
+
event: (e) => {
|
|
53
|
+
recordedEvents.push(e)
|
|
54
|
+
},
|
|
55
|
+
command: (c) => {
|
|
56
|
+
recordedCommands.push(c)
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
return internal
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Native Extension that decorates the eventStore and commandBus with
|
|
65
|
+
* recording wrappers.
|
|
66
|
+
*
|
|
67
|
+
* **Decoration order** (Phase 6 D-62): user decorators registered AFTER this
|
|
68
|
+
* extension's `app.use(...)` wrap OUTSIDE the recording decorators. To land
|
|
69
|
+
* the recording decorators at the INNERMOST position (capturing messages
|
|
70
|
+
* AFTER all interceptors have enriched them), call
|
|
71
|
+
* `app.use(testRecordingExtension(recordings))` BEFORE applying any user
|
|
72
|
+
* decorators / `configureFn(app)`.
|
|
73
|
+
*
|
|
74
|
+
* The legacy enhancer used `Number.MIN_SAFE_INTEGER` numeric priority for the
|
|
75
|
+
* same effect; Phase 6 dropped numeric priorities — innermost = first
|
|
76
|
+
* registered.
|
|
77
|
+
*/
|
|
78
|
+
export function testRecordingExtension(recordings: Recordings): Extension {
|
|
79
|
+
const push = (recordings as RecordingsInternal)._push
|
|
80
|
+
if (!push) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
"[testRecordingExtension] Recordings handle missing internal writers — pass an instance from createRecordings().",
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
return (app: App) => {
|
|
86
|
+
// EventStore append wrapper — records events after a successful append.
|
|
87
|
+
app.decorate("eventStore", (inner) => {
|
|
88
|
+
const originalAppend = inner.append.bind(inner)
|
|
89
|
+
return {
|
|
90
|
+
...inner,
|
|
91
|
+
async append(events: ReadonlyArray<EventMessage>, condition?: any) {
|
|
92
|
+
const result = await originalAppend(events, condition)
|
|
93
|
+
for (const e of events) push.event(e)
|
|
94
|
+
return result
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// CommandBus dispatch wrapper — records commands BEFORE dispatch (legacy
|
|
100
|
+
// semantics: legacy fixture inspected the raw dispatched command, not
|
|
101
|
+
// the post-handler outcome).
|
|
102
|
+
app.decorate("commandBus", (inner) => {
|
|
103
|
+
const originalDispatch = inner.dispatch.bind(inner)
|
|
104
|
+
return {
|
|
105
|
+
...inner,
|
|
106
|
+
async dispatch(message: CommandMessage) {
|
|
107
|
+
push.command(message)
|
|
108
|
+
return originalDispatch(message)
|
|
109
|
+
},
|
|
110
|
+
} as CommandBus
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|