@boxxy/dispatch 0.1.1 → 0.1.3
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/attempts/attempt-machine.d.ts +804 -0
- package/dist/attempts/attempt-moves.d.ts +2 -0
- package/dist/attempts/bind-attempt-session.d.ts +14 -0
- package/dist/attempts/build-attempt-change.d.ts +73 -0
- package/dist/attempts/build-attempt-fact.d.ts +3 -0
- package/dist/attempts/end-item-attempts.d.ts +18 -0
- package/dist/attempts/is-same-guard.d.ts +5 -0
- package/dist/attempts/pick-call-attempt.d.ts +10 -0
- package/dist/attempts/stop-stray-session.d.ts +11 -0
- package/dist/attempts/types.d.ts +142 -0
- package/dist/attempts/write-attempt-move.d.ts +7 -0
- package/dist/attempts/write-session-bind.d.ts +3 -0
- package/dist/claims/build-snapshot-hash.d.ts +2 -0
- package/dist/claims/claim-next.d.ts +6 -0
- package/dist/claims/pick-kept-session.d.ts +9 -0
- package/dist/claims/request-wake.d.ts +3 -0
- package/dist/claims/resume-claim.d.ts +3 -0
- package/dist/claims/start-claim.d.ts +3 -0
- package/dist/claims/types.d.ts +44 -0
- package/dist/closeouts/build-settle-plans.d.ts +17 -0
- package/dist/closeouts/close-out-attempt.d.ts +3 -0
- package/dist/closeouts/effect-handlers.d.ts +2 -0
- package/dist/closeouts/pick-settlement.d.ts +14 -0
- package/dist/closeouts/settle-attempt.d.ts +3 -0
- package/dist/closeouts/types.d.ts +50 -0
- package/dist/index.d.ts +38 -0
- package/dist/notices/constants.d.ts +4 -0
- package/dist/notices/create-null-notifier.d.ts +2 -0
- package/dist/notices/send-notices.d.ts +2 -0
- package/dist/notices/types.d.ts +11 -0
- package/dist/outbox/build-backoff-ms.d.ts +2 -0
- package/dist/outbox/build-initial-sender-state.d.ts +2 -0
- package/dist/outbox/build-outbox-status.d.ts +15 -0
- package/dist/outbox/constants.d.ts +2 -0
- package/dist/outbox/measure-wire-bytes.d.ts +2 -0
- package/dist/outbox/pick-sender-move.d.ts +10 -0
- package/dist/outbox/run-sender-once.d.ts +2 -0
- package/dist/outbox/take-batch.d.ts +3 -0
- package/dist/outbox/types.d.ts +61 -0
- package/dist/pull-requests/build-link-change.d.ts +11 -0
- package/dist/pull-requests/build-state-fact.d.ts +8 -0
- package/dist/pull-requests/collect-attempt-trailers.d.ts +2 -0
- package/dist/pull-requests/collect-seen-state.d.ts +7 -0
- package/dist/pull-requests/constants.d.ts +3 -0
- package/dist/pull-requests/link-pull-request.d.ts +2 -0
- package/dist/pull-requests/parse-pull-request-url.d.ts +2 -0
- package/dist/pull-requests/types.d.ts +37 -0
- package/dist/pull-requests/watch-pull-requests.d.ts +2 -0
- package/dist/readings/act-on-reading.d.ts +3 -0
- package/dist/readings/find-waiting-since.d.ts +9 -0
- package/dist/readings/pick-reading-action.d.ts +2 -0
- package/dist/readings/types.d.ts +79 -0
- package/dist/sessions/build-keep-writes.d.ts +9 -0
- package/dist/sessions/build-kept-clear.d.ts +2 -0
- package/dist/sessions/forget-sessions.d.ts +2 -0
- package/dist/sessions/is-kept-due.d.ts +2 -0
- package/dist/sessions/resume-kept-session.d.ts +6 -0
- package/dist/sessions/types.d.ts +19 -0
- package/dist/test-utils/apply-memory-change.d.ts +3 -0
- package/dist/test-utils/build-memory-reader.d.ts +3 -0
- package/dist/test-utils/build-mock-claim-change.d.ts +2 -2
- package/dist/test-utils/build-mock-new-id.d.ts +2 -0
- package/dist/test-utils/build-mock-note-change.d.ts +2 -0
- package/dist/test-utils/build-mock-pull-request-change.d.ts +1 -0
- package/dist/test-utils/{build-mock-link-change.d.ts → build-mock-relation-change.d.ts} +2 -2
- package/dist/test-utils/build-mock-running-reading.d.ts +6 -0
- package/dist/test-utils/build-mock-start-change.d.ts +2 -2
- package/dist/test-utils/claim-store-close-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-copy-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-outbox-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-race-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-seat-cases.d.ts +2 -0
- package/dist/test-utils/claim-store-session-cases.d.ts +2 -0
- package/dist/test-utils/collect-memory-limit-fills.d.ts +3 -0
- package/dist/test-utils/compare-text.d.ts +1 -0
- package/dist/test-utils/create-fake-backoff.d.ts +2 -0
- package/dist/test-utils/create-fake-clock.d.ts +5 -0
- package/dist/test-utils/create-fake-fact-sink.d.ts +8 -0
- package/dist/test-utils/create-fake-forge.d.ts +17 -0
- package/dist/test-utils/create-fake-worker.d.ts +42 -0
- package/dist/test-utils/create-memory-claim-store.d.ts +2 -2
- package/dist/test-utils/create-test-dispatch.d.ts +46 -0
- package/dist/test-utils/forge-contract.d.ts +15 -0
- package/dist/test-utils/is-same-held-seat.d.ts +2 -0
- package/dist/test-utils/types.d.ts +12 -2
- package/dist/test-utils/worker-contract.d.ts +14 -0
- package/dist/timeouts.d.ts +2 -0
- package/dist/types.d.ts +13 -0
- package/package.json +10 -3
- package/src/attempts/attempt-machine.ts +147 -0
- package/src/attempts/attempt-moves.ts +218 -0
- package/src/attempts/bind-attempt-session.ts +61 -0
- package/src/attempts/build-attempt-change.ts +540 -0
- package/src/attempts/build-attempt-fact.ts +135 -0
- package/src/attempts/end-item-attempts.ts +95 -0
- package/src/attempts/is-same-guard.ts +22 -0
- package/src/attempts/pick-call-attempt.ts +32 -0
- package/src/attempts/stop-stray-session.ts +48 -0
- package/src/attempts/types.ts +231 -0
- package/src/attempts/write-attempt-move.ts +41 -0
- package/src/attempts/write-session-bind.ts +47 -0
- package/src/claims/build-snapshot-hash.ts +9 -0
- package/src/claims/claim-next.ts +306 -0
- package/src/claims/pick-kept-session.ts +41 -0
- package/src/claims/request-wake.ts +17 -0
- package/src/claims/resume-claim.ts +56 -0
- package/src/claims/start-claim.ts +99 -0
- package/src/claims/types.ts +67 -0
- package/src/closeouts/build-settle-plans.ts +173 -0
- package/src/closeouts/close-out-attempt.ts +156 -0
- package/src/closeouts/effect-handlers.ts +232 -0
- package/src/closeouts/pick-settlement.ts +80 -0
- package/src/closeouts/settle-attempt.ts +161 -0
- package/src/closeouts/types.ts +87 -0
- package/src/index.ts +61 -0
- package/src/notices/constants.ts +7 -0
- package/src/notices/create-null-notifier.ts +6 -0
- package/src/notices/send-notices.ts +41 -0
- package/src/notices/types.ts +14 -0
- package/src/outbox/build-backoff-ms.ts +14 -0
- package/src/outbox/build-initial-sender-state.ts +14 -0
- package/src/outbox/build-outbox-status.ts +43 -0
- package/src/outbox/constants.ts +15 -0
- package/src/outbox/measure-wire-bytes.ts +31 -0
- package/src/outbox/pick-sender-move.ts +37 -0
- package/src/outbox/run-sender-once.ts +174 -0
- package/src/outbox/take-batch.ts +35 -0
- package/src/outbox/types.ts +76 -0
- package/src/pull-requests/build-link-change.ts +95 -0
- package/src/pull-requests/build-state-fact.ts +39 -0
- package/src/pull-requests/collect-attempt-trailers.ts +20 -0
- package/src/pull-requests/collect-seen-state.ts +19 -0
- package/src/pull-requests/constants.ts +5 -0
- package/src/pull-requests/link-pull-request.ts +119 -0
- package/src/pull-requests/parse-pull-request-url.ts +30 -0
- package/src/pull-requests/types.ts +63 -0
- package/src/pull-requests/watch-pull-requests.ts +308 -0
- package/src/readings/act-on-reading.ts +318 -0
- package/src/readings/find-waiting-since.ts +46 -0
- package/src/readings/pick-reading-action.ts +315 -0
- package/src/readings/types.ts +98 -0
- package/src/sessions/build-keep-writes.ts +55 -0
- package/src/sessions/build-kept-clear.ts +12 -0
- package/src/sessions/forget-sessions.ts +106 -0
- package/src/sessions/is-kept-due.ts +27 -0
- package/src/sessions/resume-kept-session.ts +25 -0
- package/src/sessions/types.ts +39 -0
- package/src/test-utils/apply-memory-change.ts +252 -0
- package/src/test-utils/build-memory-reader.ts +364 -0
- package/src/test-utils/build-mock-bind-change.ts +18 -11
- package/src/test-utils/build-mock-claim-change.ts +18 -8
- package/src/test-utils/build-mock-file-item-change.ts +3 -1
- package/src/test-utils/build-mock-new-id.ts +12 -0
- package/src/test-utils/build-mock-note-change.ts +18 -0
- package/src/test-utils/build-mock-pause-change.ts +2 -10
- package/src/test-utils/build-mock-pull-request-change.ts +19 -9
- package/src/test-utils/{build-mock-link-change.ts → build-mock-relation-change.ts} +6 -6
- package/src/test-utils/build-mock-running-reading.ts +19 -0
- package/src/test-utils/build-mock-settle-change.ts +18 -6
- package/src/test-utils/build-mock-start-change.ts +13 -6
- package/src/test-utils/claim-store-attempt-reader-cases.ts +163 -133
- package/src/test-utils/claim-store-claim-cases.ts +168 -85
- package/src/test-utils/claim-store-close-cases.ts +95 -0
- package/src/test-utils/claim-store-contract.ts +12 -2
- package/src/test-utils/claim-store-copy-cases.ts +100 -0
- package/src/test-utils/claim-store-exclusive-cases.ts +65 -19
- package/src/test-utils/claim-store-guard-cases.ts +92 -82
- package/src/test-utils/claim-store-item-reader-cases.ts +291 -77
- package/src/test-utils/claim-store-outbox-cases.ts +281 -0
- package/src/test-utils/claim-store-race-cases.ts +150 -0
- package/src/test-utils/claim-store-rollback-cases.ts +50 -50
- package/src/test-utils/claim-store-seat-cases.ts +270 -0
- package/src/test-utils/claim-store-session-cases.ts +289 -0
- package/src/test-utils/collect-memory-limit-fills.ts +15 -0
- package/src/test-utils/compare-text.ts +8 -0
- package/src/test-utils/create-fake-backoff.ts +16 -0
- package/src/test-utils/create-fake-clock.ts +20 -0
- package/src/test-utils/create-fake-fact-sink.ts +74 -0
- package/src/test-utils/create-fake-forge.ts +117 -0
- package/src/test-utils/create-fake-worker.ts +303 -0
- package/src/test-utils/create-memory-claim-store.ts +73 -484
- package/src/test-utils/create-test-dispatch.ts +597 -0
- package/src/test-utils/forge-contract.ts +171 -0
- package/src/test-utils/is-same-held-seat.ts +11 -0
- package/src/test-utils/types.ts +14 -2
- package/src/test-utils/worker-contract.ts +192 -0
- package/src/timeouts.ts +17 -0
- package/src/types.ts +32 -0
- package/dist/test-utils/claim-store-pool-cases.d.ts +0 -2
- package/src/test-utils/claim-store-pool-cases.ts +0 -178
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { brandSchemas } from '@boxxy/model';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { buildMockItemRules } from '@boxxy/model/test-utils/build-mock-item-rules';
|
|
3
|
+
import { buildMockV1AttemptRow } from '@boxxy/model/test-utils/build-mock-v1-attempt-row';
|
|
4
|
+
import { buildMockV1ItemRow } from '@boxxy/model/test-utils/build-mock-v1-item-row';
|
|
5
|
+
import { buildMockV1LeverRow } from '@boxxy/model/test-utils/build-mock-v1-lever-row';
|
|
5
6
|
import { buildMockClaimChange } from './build-mock-claim-change.ts';
|
|
6
7
|
import { buildMockFileItemChange } from './build-mock-file-item-change.ts';
|
|
7
8
|
import { buildMockLeverChange } from './build-mock-lever-change.ts';
|
|
9
|
+
import { buildMockNoteChange } from './build-mock-note-change.ts';
|
|
10
|
+
import { buildMockPauseChange } from './build-mock-pause-change.ts';
|
|
8
11
|
import type { ClaimStoreCase } from './types.ts';
|
|
9
12
|
|
|
10
13
|
export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
11
14
|
[
|
|
12
15
|
'#applyChange returns lease-held for a second live lease on one item phase',
|
|
13
16
|
async (store) => {
|
|
14
|
-
const item =
|
|
15
|
-
const first =
|
|
17
|
+
const item = buildMockV1ItemRow();
|
|
18
|
+
const first = buildMockV1AttemptRow({ item: item.id, state: 'starting', leaseGen: 1 });
|
|
16
19
|
|
|
17
|
-
const second =
|
|
20
|
+
const second = buildMockV1AttemptRow({
|
|
18
21
|
item: item.id,
|
|
19
22
|
state: 'starting',
|
|
20
23
|
leaseGen: 2,
|
|
@@ -22,7 +25,6 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
await store.exclusive(async (transaction) => {
|
|
25
|
-
await transaction.syncPoolSlots([{ name: first.pool, slots: 2 }]);
|
|
26
28
|
await transaction.applyChange(buildMockFileItemChange(item));
|
|
27
29
|
await transaction.applyChange(buildMockClaimChange(first));
|
|
28
30
|
});
|
|
@@ -35,12 +37,13 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
35
37
|
},
|
|
36
38
|
],
|
|
37
39
|
[
|
|
38
|
-
'#applyChange writes no row
|
|
40
|
+
'#applyChange writes no row and takes no seat for a change that returns lease-held',
|
|
39
41
|
async (store) => {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
+
const workers = brandSchemas.limitName.parse('workers');
|
|
43
|
+
const item = buildMockV1ItemRow();
|
|
44
|
+
const first = buildMockV1AttemptRow({ item: item.id, state: 'starting', leaseGen: 1 });
|
|
42
45
|
|
|
43
|
-
const second =
|
|
46
|
+
const second = buildMockV1AttemptRow({
|
|
44
47
|
item: item.id,
|
|
45
48
|
state: 'starting',
|
|
46
49
|
leaseGen: 2,
|
|
@@ -48,21 +51,27 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
48
51
|
});
|
|
49
52
|
|
|
50
53
|
await store.exclusive(async (transaction) => {
|
|
51
|
-
await transaction.
|
|
54
|
+
await transaction.syncLimits(
|
|
55
|
+
[{ name: workers, max: 2, counts: {} }],
|
|
56
|
+
brandSchemas.instant.parse(500),
|
|
57
|
+
);
|
|
58
|
+
|
|
52
59
|
await transaction.applyChange(buildMockFileItemChange(item));
|
|
53
|
-
await transaction.applyChange(buildMockClaimChange(first));
|
|
54
|
-
await transaction.applyChange(buildMockClaimChange(second));
|
|
60
|
+
await transaction.applyChange(buildMockClaimChange(first, [workers]));
|
|
61
|
+
await transaction.applyChange(buildMockClaimChange(second, [workers]));
|
|
55
62
|
});
|
|
56
63
|
|
|
57
64
|
const state: unknown = await store.read(async (transaction) => {
|
|
58
65
|
const snapshot = await transaction.readSnapshot(
|
|
59
66
|
brandSchemas.instant.parse(5000),
|
|
60
|
-
|
|
67
|
+
buildMockItemRules(),
|
|
61
68
|
);
|
|
62
69
|
|
|
70
|
+
const seats = await transaction.listLimitSeats();
|
|
71
|
+
|
|
63
72
|
return {
|
|
64
73
|
attempt: await transaction.readAttempt(second.id),
|
|
65
|
-
|
|
74
|
+
seats: seats.map((seat) => seat.attempt),
|
|
66
75
|
lastGen: await transaction.readLastLeaseGen(item.id, first.phase),
|
|
67
76
|
version: snapshot.storeVersion,
|
|
68
77
|
};
|
|
@@ -70,41 +79,38 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
70
79
|
|
|
71
80
|
return {
|
|
72
81
|
received: state,
|
|
73
|
-
expected: {
|
|
74
|
-
attempt: null,
|
|
75
|
-
slots: [
|
|
76
|
-
{ pool: first.pool, slot: 1, holder: first.id, gen: 1, retired: false },
|
|
77
|
-
{ pool: first.pool, slot: 2, holder: null, gen: 0, retired: false },
|
|
78
|
-
],
|
|
79
|
-
lastGen: 1,
|
|
80
|
-
version: 2,
|
|
81
|
-
},
|
|
82
|
+
expected: { attempt: null, seats: [first.id], lastGen: 1, version: 2 },
|
|
82
83
|
};
|
|
83
84
|
},
|
|
84
85
|
],
|
|
85
86
|
[
|
|
86
|
-
'#applyChange returns
|
|
87
|
+
'#applyChange returns limit-full and changes no row when a limit has no free seat',
|
|
87
88
|
async (store) => {
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
89
|
+
const workers = brandSchemas.limitName.parse('workers');
|
|
90
|
+
const held = buildMockV1ItemRow();
|
|
91
|
+
const waiting = buildMockV1ItemRow();
|
|
92
|
+
const first = buildMockV1AttemptRow({ item: held.id, state: 'starting' });
|
|
93
|
+
const second = buildMockV1AttemptRow({ item: waiting.id, state: 'starting' });
|
|
92
94
|
|
|
93
95
|
await store.exclusive(async (transaction) => {
|
|
94
|
-
await transaction.
|
|
96
|
+
await transaction.syncLimits(
|
|
97
|
+
[{ name: workers, max: 1, counts: {} }],
|
|
98
|
+
brandSchemas.instant.parse(500),
|
|
99
|
+
);
|
|
100
|
+
|
|
95
101
|
await transaction.applyChange(buildMockFileItemChange(held));
|
|
96
102
|
await transaction.applyChange(buildMockFileItemChange(waiting));
|
|
97
|
-
await transaction.applyChange(buildMockClaimChange(first));
|
|
103
|
+
await transaction.applyChange(buildMockClaimChange(first, [workers]));
|
|
98
104
|
});
|
|
99
105
|
|
|
100
106
|
const result = await store.exclusive((transaction) =>
|
|
101
|
-
transaction.applyChange(buildMockClaimChange(second)),
|
|
107
|
+
transaction.applyChange(buildMockClaimChange(second, [workers])),
|
|
102
108
|
);
|
|
103
109
|
|
|
104
110
|
const state: unknown = await store.read(async (transaction) => {
|
|
105
111
|
const snapshot = await transaction.readSnapshot(
|
|
106
112
|
brandSchemas.instant.parse(5000),
|
|
107
|
-
|
|
113
|
+
buildMockItemRules(),
|
|
108
114
|
);
|
|
109
115
|
|
|
110
116
|
return {
|
|
@@ -117,88 +123,92 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
117
123
|
return {
|
|
118
124
|
received: { result, state },
|
|
119
125
|
expected: {
|
|
120
|
-
result: { kind: '
|
|
126
|
+
result: { kind: 'limit-full', limit: 'workers' },
|
|
121
127
|
state: { attempt: null, lastGen: 0, version: 3 },
|
|
122
128
|
},
|
|
123
129
|
};
|
|
124
130
|
},
|
|
125
131
|
],
|
|
126
132
|
[
|
|
127
|
-
'#applyChange
|
|
133
|
+
'#applyChange takes the seats of a claim all or none',
|
|
128
134
|
async (store) => {
|
|
129
|
-
const
|
|
130
|
-
const
|
|
135
|
+
const workers = brandSchemas.limitName.parse('workers');
|
|
136
|
+
const ownerDesign = brandSchemas.limitName.parse('owner-design');
|
|
137
|
+
|
|
138
|
+
const first = buildMockV1AttemptRow({
|
|
139
|
+
state: 'starting',
|
|
140
|
+
phase: brandSchemas.phaseName.parse('design'),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const second = buildMockV1AttemptRow({
|
|
144
|
+
state: 'starting',
|
|
145
|
+
phase: brandSchemas.phaseName.parse('design'),
|
|
146
|
+
});
|
|
131
147
|
|
|
132
148
|
await store.exclusive(async (transaction) => {
|
|
133
|
-
await transaction.
|
|
134
|
-
|
|
149
|
+
await transaction.syncLimits(
|
|
150
|
+
[
|
|
151
|
+
{ name: workers, max: 7, counts: {} },
|
|
152
|
+
{ name: ownerDesign, max: 1, counts: { designMode: ['attended'] } },
|
|
153
|
+
],
|
|
154
|
+
brandSchemas.instant.parse(500),
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
await transaction.applyChange(
|
|
158
|
+
buildMockFileItemChange(buildMockV1ItemRow({ id: first.item })),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
await transaction.applyChange(
|
|
162
|
+
buildMockFileItemChange(buildMockV1ItemRow({ id: second.item })),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
await transaction.applyChange(buildMockClaimChange(first, [ownerDesign, workers]));
|
|
135
166
|
});
|
|
136
167
|
|
|
137
168
|
const result = await store.exclusive((transaction) =>
|
|
138
|
-
transaction.applyChange(buildMockClaimChange(
|
|
169
|
+
transaction.applyChange(buildMockClaimChange(second, [workers, ownerDesign])),
|
|
139
170
|
);
|
|
140
171
|
|
|
172
|
+
const limits = await store.read((transaction) => transaction.listLimits());
|
|
173
|
+
|
|
141
174
|
return {
|
|
142
|
-
received: result,
|
|
175
|
+
received: { result, held: limits.map((limit) => [limit.name, limit.held]) },
|
|
143
176
|
expected: {
|
|
144
|
-
kind: '
|
|
145
|
-
|
|
177
|
+
result: { kind: 'limit-full', limit: 'owner-design' },
|
|
178
|
+
held: [
|
|
179
|
+
['owner-design', 1],
|
|
180
|
+
['workers', 1],
|
|
181
|
+
],
|
|
146
182
|
},
|
|
147
183
|
};
|
|
148
184
|
},
|
|
149
185
|
],
|
|
150
186
|
[
|
|
151
|
-
'#applyChange
|
|
187
|
+
'#applyChange returns unknown-limit for a take that names a limit the store does not hold',
|
|
152
188
|
async (store) => {
|
|
153
|
-
const
|
|
154
|
-
const attempt = buildMockAttemptRow({ item: item.id, state: 'starting' });
|
|
189
|
+
const attempt = buildMockV1AttemptRow({ state: 'starting' });
|
|
155
190
|
|
|
156
191
|
await store.exclusive(async (transaction) => {
|
|
157
|
-
await transaction.
|
|
158
|
-
|
|
159
|
-
|
|
192
|
+
await transaction.applyChange(
|
|
193
|
+
buildMockFileItemChange(buildMockV1ItemRow({ id: attempt.item })),
|
|
194
|
+
);
|
|
160
195
|
});
|
|
161
196
|
|
|
162
|
-
const
|
|
197
|
+
const result = await store.exclusive((transaction) =>
|
|
198
|
+
transaction.applyChange(
|
|
199
|
+
buildMockClaimChange(attempt, [brandSchemas.limitName.parse('reviews')]),
|
|
200
|
+
),
|
|
201
|
+
);
|
|
163
202
|
|
|
164
|
-
return { received:
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
[
|
|
168
|
-
'#applyChange writes the taken slot and slot gen into the claimed fact',
|
|
169
|
-
async (store) => {
|
|
170
|
-
const item = buildMockItemRow();
|
|
171
|
-
const attempt = buildMockAttemptRow({ item: item.id, state: 'starting' });
|
|
172
|
-
|
|
173
|
-
await store.exclusive(async (transaction) => {
|
|
174
|
-
await transaction.syncPoolSlots([{ name: attempt.pool, slots: 2 }]);
|
|
175
|
-
await transaction.applyChange(buildMockFileItemChange(item));
|
|
176
|
-
await transaction.applyChange(buildMockClaimChange(attempt));
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const facts = await store.read((transaction) => transaction.readUnsentFacts(10));
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
received: facts[1]?.payload,
|
|
183
|
-
expected: {
|
|
184
|
-
item: item.id,
|
|
185
|
-
phase: attempt.phase,
|
|
186
|
-
number: attempt.number,
|
|
187
|
-
pool: attempt.pool,
|
|
188
|
-
slot: 1,
|
|
189
|
-
gen: 1,
|
|
190
|
-
recipeHash: attempt.recipeHash,
|
|
191
|
-
rulesVersion: attempt.rulesVersion,
|
|
192
|
-
},
|
|
193
|
-
};
|
|
203
|
+
return { received: result, expected: { kind: 'unknown-limit', limit: 'reviews' } };
|
|
194
204
|
},
|
|
195
205
|
],
|
|
196
206
|
[
|
|
197
207
|
'#applyChange gives each lever insert its seq, in the row and the fact',
|
|
198
208
|
async (store) => {
|
|
199
|
-
const item =
|
|
200
|
-
const defer =
|
|
201
|
-
const expedite =
|
|
209
|
+
const item = buildMockV1ItemRow();
|
|
210
|
+
const defer = buildMockV1LeverRow({ seq: 0, item: item.id, kind: 'plan.defer' });
|
|
211
|
+
const expedite = buildMockV1LeverRow({ seq: 0, item: item.id, kind: 'plan.expedite' });
|
|
202
212
|
|
|
203
213
|
await store.exclusive(async (transaction) => {
|
|
204
214
|
await transaction.applyChange(buildMockFileItemChange(item));
|
|
@@ -207,7 +217,12 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
207
217
|
});
|
|
208
218
|
|
|
209
219
|
const state: unknown = await store.read(async (transaction) => {
|
|
210
|
-
const items = await transaction.listItems(
|
|
220
|
+
const items = await transaction.listItems(
|
|
221
|
+
{},
|
|
222
|
+
brandSchemas.instant.parse(5000),
|
|
223
|
+
buildMockItemRules(),
|
|
224
|
+
);
|
|
225
|
+
|
|
211
226
|
const facts = await transaction.readUnsentFacts(10);
|
|
212
227
|
|
|
213
228
|
return { levers: items[0]?.levers, payloads: facts.slice(1).map((fact) => fact.payload) };
|
|
@@ -228,4 +243,72 @@ export const claimStoreClaimCases: readonly ClaimStoreCase[] = [
|
|
|
228
243
|
};
|
|
229
244
|
},
|
|
230
245
|
],
|
|
246
|
+
[
|
|
247
|
+
'#applyChange throws and writes nothing for a fact whose payload its type refuses',
|
|
248
|
+
async (store) => {
|
|
249
|
+
const item = buildMockV1ItemRow();
|
|
250
|
+
|
|
251
|
+
await store.exclusive((transaction) =>
|
|
252
|
+
transaction.applyChange(buildMockFileItemChange(item)),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const outcome = await Promise.allSettled([
|
|
256
|
+
store.exclusive((transaction) =>
|
|
257
|
+
transaction.applyChange(
|
|
258
|
+
buildMockNoteChange({
|
|
259
|
+
item: item.id,
|
|
260
|
+
seq: 1,
|
|
261
|
+
text: '',
|
|
262
|
+
by: 'boxxy:owner:local',
|
|
263
|
+
at: brandSchemas.instant.parse(4500),
|
|
264
|
+
}),
|
|
265
|
+
),
|
|
266
|
+
),
|
|
267
|
+
]);
|
|
268
|
+
|
|
269
|
+
const state: unknown = await store.read(async (transaction) => {
|
|
270
|
+
const facts = await transaction.readUnsentFacts(10);
|
|
271
|
+
|
|
272
|
+
return { notes: await transaction.listItemNotes(item.id), facts: facts.length };
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
received: { status: outcome[0]?.status, state },
|
|
277
|
+
expected: { status: 'rejected', state: { notes: [], facts: 1 } },
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
[
|
|
282
|
+
'#applyChange applies a change with no fact and moves no store version',
|
|
283
|
+
async (store) => {
|
|
284
|
+
const runner = brandSchemas.runnerId.parse('runner-1');
|
|
285
|
+
const item = buildMockV1ItemRow();
|
|
286
|
+
|
|
287
|
+
await store.exclusive(async (transaction) => {
|
|
288
|
+
await transaction.applyChange(buildMockFileItemChange(item));
|
|
289
|
+
await transaction.writeHeartbeat(runner, brandSchemas.instant.parse(1000));
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const result = await store.exclusive((transaction) =>
|
|
293
|
+
transaction.applyChange(buildMockPauseChange(runner)),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const state: unknown = await store.read(async (transaction) => {
|
|
297
|
+
const snapshot = await transaction.readSnapshot(
|
|
298
|
+
brandSchemas.instant.parse(5000),
|
|
299
|
+
buildMockItemRules(),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return { runner: await transaction.readRunner(runner), version: snapshot.storeVersion };
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
received: { result, state },
|
|
307
|
+
expected: {
|
|
308
|
+
result: { kind: 'applied' },
|
|
309
|
+
state: { runner: { id: 'runner-1', heartbeatAt: 1000, paused: true }, version: 1 },
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
},
|
|
313
|
+
],
|
|
231
314
|
];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { setImmediate } from 'node:timers/promises';
|
|
2
|
+
import type { ClaimStoreCase } from './types.ts';
|
|
3
|
+
|
|
4
|
+
export const claimStoreCloseCases: readonly ClaimStoreCase[] = [
|
|
5
|
+
[
|
|
6
|
+
'#read rejects after the store closes',
|
|
7
|
+
async (store) => {
|
|
8
|
+
await store.close();
|
|
9
|
+
|
|
10
|
+
const settled = await store
|
|
11
|
+
.read((transaction) => transaction.listLimits())
|
|
12
|
+
.then(
|
|
13
|
+
() => 'resolved',
|
|
14
|
+
() => 'rejected',
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return { received: settled, expected: 'rejected' };
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
'#exclusive rejects after the store closes',
|
|
22
|
+
async (store) => {
|
|
23
|
+
await store.close();
|
|
24
|
+
|
|
25
|
+
const settled = await store
|
|
26
|
+
.exclusive((transaction) => transaction.listLimits())
|
|
27
|
+
.then(
|
|
28
|
+
() => 'resolved',
|
|
29
|
+
() => 'rejected',
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return { received: settled, expected: 'rejected' };
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
'#close waits for a read that is running',
|
|
37
|
+
async (store) => {
|
|
38
|
+
const events: string[] = [];
|
|
39
|
+
const gate = Promise.withResolvers<void>();
|
|
40
|
+
|
|
41
|
+
const reading = store.read(async () => {
|
|
42
|
+
await gate.promise;
|
|
43
|
+
|
|
44
|
+
events.push('read end');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const close = async () => {
|
|
48
|
+
await store.close();
|
|
49
|
+
|
|
50
|
+
events.push('close end');
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const closing = close();
|
|
54
|
+
|
|
55
|
+
// one turn of the event loop lets a store that doesn't wait finish the close
|
|
56
|
+
await setImmediate();
|
|
57
|
+
|
|
58
|
+
gate.resolve();
|
|
59
|
+
|
|
60
|
+
await Promise.all([reading, closing]);
|
|
61
|
+
|
|
62
|
+
return { received: events, expected: ['read end', 'close end'] };
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
'#close waits for an exclusive call that is running',
|
|
67
|
+
async (store) => {
|
|
68
|
+
const events: string[] = [];
|
|
69
|
+
const gate = Promise.withResolvers<void>();
|
|
70
|
+
|
|
71
|
+
const writing = store.exclusive(async () => {
|
|
72
|
+
await gate.promise;
|
|
73
|
+
|
|
74
|
+
events.push('exclusive end');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const close = async () => {
|
|
78
|
+
await store.close();
|
|
79
|
+
|
|
80
|
+
events.push('close end');
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const closing = close();
|
|
84
|
+
|
|
85
|
+
// one turn of the event loop lets a store that doesn't wait finish the close
|
|
86
|
+
await setImmediate();
|
|
87
|
+
|
|
88
|
+
gate.resolve();
|
|
89
|
+
|
|
90
|
+
await Promise.all([writing, closing]);
|
|
91
|
+
|
|
92
|
+
return { received: events, expected: ['exclusive end', 'close end'] };
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
];
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { claimStoreAttemptReaderCases } from './claim-store-attempt-reader-cases.ts';
|
|
2
2
|
import { claimStoreClaimCases } from './claim-store-claim-cases.ts';
|
|
3
|
+
import { claimStoreCloseCases } from './claim-store-close-cases.ts';
|
|
4
|
+
import { claimStoreCopyCases } from './claim-store-copy-cases.ts';
|
|
3
5
|
import { claimStoreExclusiveCases } from './claim-store-exclusive-cases.ts';
|
|
4
6
|
import { claimStoreGuardCases } from './claim-store-guard-cases.ts';
|
|
5
7
|
import { claimStoreItemReaderCases } from './claim-store-item-reader-cases.ts';
|
|
6
|
-
import {
|
|
8
|
+
import { claimStoreOutboxCases } from './claim-store-outbox-cases.ts';
|
|
9
|
+
import { claimStoreRaceCases } from './claim-store-race-cases.ts';
|
|
7
10
|
import { claimStoreRollbackCases } from './claim-store-rollback-cases.ts';
|
|
11
|
+
import { claimStoreSeatCases } from './claim-store-seat-cases.ts';
|
|
12
|
+
import { claimStoreSessionCases } from './claim-store-session-cases.ts';
|
|
8
13
|
import type { ClaimStoreCase } from './types.ts';
|
|
9
14
|
|
|
10
15
|
export const claimStoreContract: readonly ClaimStoreCase[] = [
|
|
@@ -12,7 +17,12 @@ export const claimStoreContract: readonly ClaimStoreCase[] = [
|
|
|
12
17
|
...claimStoreClaimCases,
|
|
13
18
|
...claimStoreGuardCases,
|
|
14
19
|
...claimStoreRollbackCases,
|
|
15
|
-
...
|
|
20
|
+
...claimStoreCopyCases,
|
|
21
|
+
...claimStoreCloseCases,
|
|
22
|
+
...claimStoreSeatCases,
|
|
16
23
|
...claimStoreItemReaderCases,
|
|
17
24
|
...claimStoreAttemptReaderCases,
|
|
25
|
+
...claimStoreSessionCases,
|
|
26
|
+
...claimStoreOutboxCases,
|
|
27
|
+
...claimStoreRaceCases,
|
|
18
28
|
];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { brandSchemas } from '@boxxy/model';
|
|
2
|
+
import type { Criterion } from '@boxxy/model';
|
|
3
|
+
import { buildMockFactId } from '@boxxy/model/test-utils/build-mock-fact-id';
|
|
4
|
+
import { buildMockItemRules } from '@boxxy/model/test-utils/build-mock-item-rules';
|
|
5
|
+
import { buildMockV1AttemptRow } from '@boxxy/model/test-utils/build-mock-v1-attempt-row';
|
|
6
|
+
import { buildMockV1ItemRow } from '@boxxy/model/test-utils/build-mock-v1-item-row';
|
|
7
|
+
import { buildMockClaimChange } from './build-mock-claim-change.ts';
|
|
8
|
+
import { buildMockFileItemChange } from './build-mock-file-item-change.ts';
|
|
9
|
+
import { buildMockStartChange } from './build-mock-start-change.ts';
|
|
10
|
+
import type { ClaimStoreCase } from './types.ts';
|
|
11
|
+
|
|
12
|
+
export const claimStoreCopyCases: readonly ClaimStoreCase[] = [
|
|
13
|
+
[
|
|
14
|
+
'#readAttempt returns a row that a later change in the same call leaves as it was',
|
|
15
|
+
async (store) => {
|
|
16
|
+
const item = buildMockV1ItemRow();
|
|
17
|
+
const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting' });
|
|
18
|
+
|
|
19
|
+
await store.exclusive(async (transaction) => {
|
|
20
|
+
await transaction.applyChange(buildMockFileItemChange(item));
|
|
21
|
+
await transaction.applyChange(buildMockClaimChange(attempt));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const before = await store.exclusive(async (transaction) => {
|
|
25
|
+
const read = await transaction.readAttempt(attempt.id);
|
|
26
|
+
|
|
27
|
+
await transaction.applyChange(buildMockStartChange(attempt));
|
|
28
|
+
|
|
29
|
+
return read;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return { received: before?.state, expected: 'starting' };
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
'#read returns rows whose edits by the caller leave the store as it was',
|
|
37
|
+
async (store) => {
|
|
38
|
+
const item = buildMockV1ItemRow();
|
|
39
|
+
const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting' });
|
|
40
|
+
|
|
41
|
+
await store.exclusive(async (transaction) => {
|
|
42
|
+
await transaction.applyChange(buildMockFileItemChange(item));
|
|
43
|
+
await transaction.applyChange(buildMockClaimChange(attempt));
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const read = await store.read((transaction) => transaction.readAttempt(attempt.id));
|
|
47
|
+
|
|
48
|
+
Object.assign(read ?? {}, { state: 'failed' });
|
|
49
|
+
|
|
50
|
+
const reread = await store.read((transaction) => transaction.readAttempt(attempt.id));
|
|
51
|
+
|
|
52
|
+
return { received: reread?.state, expected: 'starting' };
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
'#applyChange stores a copy of a list that an update sets',
|
|
57
|
+
async (store) => {
|
|
58
|
+
const item = buildMockV1ItemRow();
|
|
59
|
+
|
|
60
|
+
const acceptance: Criterion[] = [
|
|
61
|
+
{ id: brandSchemas.criterionId.parse('a1'), text: 'The gate latches', evidence: [] },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
await store.exclusive(async (transaction) => {
|
|
65
|
+
await transaction.applyChange(buildMockFileItemChange(item));
|
|
66
|
+
|
|
67
|
+
await transaction.applyChange({
|
|
68
|
+
at: brandSchemas.instant.parse(3000),
|
|
69
|
+
writes: [
|
|
70
|
+
{ op: 'update', table: 'items', key: { id: item.id }, expect: {}, set: { acceptance } },
|
|
71
|
+
],
|
|
72
|
+
facts: [
|
|
73
|
+
{
|
|
74
|
+
id: buildMockFactId(),
|
|
75
|
+
type: 'boxxy.item.edited',
|
|
76
|
+
entity: `boxxy:item:${item.id}`,
|
|
77
|
+
actor: 'boxxy:owner:local',
|
|
78
|
+
payload: { acceptance: [...acceptance], byAttempt: null },
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
acceptance.push({
|
|
85
|
+
id: brandSchemas.criterionId.parse('a2'),
|
|
86
|
+
text: 'The hinge is oiled',
|
|
87
|
+
evidence: [],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const reread = await store.read((transaction) =>
|
|
91
|
+
transaction.readItem(item.id, brandSchemas.instant.parse(5000), buildMockItemRules()),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
received: reread?.acceptance,
|
|
96
|
+
expected: [{ id: 'a1', text: 'The gate latches', evidence: [] }],
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
];
|