@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.
Files changed (190) hide show
  1. package/dist/attempts/attempt-machine.d.ts +804 -0
  2. package/dist/attempts/attempt-moves.d.ts +2 -0
  3. package/dist/attempts/bind-attempt-session.d.ts +14 -0
  4. package/dist/attempts/build-attempt-change.d.ts +73 -0
  5. package/dist/attempts/build-attempt-fact.d.ts +3 -0
  6. package/dist/attempts/end-item-attempts.d.ts +18 -0
  7. package/dist/attempts/is-same-guard.d.ts +5 -0
  8. package/dist/attempts/pick-call-attempt.d.ts +10 -0
  9. package/dist/attempts/stop-stray-session.d.ts +11 -0
  10. package/dist/attempts/types.d.ts +142 -0
  11. package/dist/attempts/write-attempt-move.d.ts +7 -0
  12. package/dist/attempts/write-session-bind.d.ts +3 -0
  13. package/dist/claims/build-snapshot-hash.d.ts +2 -0
  14. package/dist/claims/claim-next.d.ts +6 -0
  15. package/dist/claims/pick-kept-session.d.ts +9 -0
  16. package/dist/claims/request-wake.d.ts +3 -0
  17. package/dist/claims/resume-claim.d.ts +3 -0
  18. package/dist/claims/start-claim.d.ts +3 -0
  19. package/dist/claims/types.d.ts +44 -0
  20. package/dist/closeouts/build-settle-plans.d.ts +17 -0
  21. package/dist/closeouts/close-out-attempt.d.ts +3 -0
  22. package/dist/closeouts/effect-handlers.d.ts +2 -0
  23. package/dist/closeouts/pick-settlement.d.ts +14 -0
  24. package/dist/closeouts/settle-attempt.d.ts +3 -0
  25. package/dist/closeouts/types.d.ts +50 -0
  26. package/dist/index.d.ts +38 -0
  27. package/dist/notices/constants.d.ts +4 -0
  28. package/dist/notices/create-null-notifier.d.ts +2 -0
  29. package/dist/notices/send-notices.d.ts +2 -0
  30. package/dist/notices/types.d.ts +11 -0
  31. package/dist/outbox/build-backoff-ms.d.ts +2 -0
  32. package/dist/outbox/build-initial-sender-state.d.ts +2 -0
  33. package/dist/outbox/build-outbox-status.d.ts +15 -0
  34. package/dist/outbox/constants.d.ts +2 -0
  35. package/dist/outbox/measure-wire-bytes.d.ts +2 -0
  36. package/dist/outbox/pick-sender-move.d.ts +10 -0
  37. package/dist/outbox/run-sender-once.d.ts +2 -0
  38. package/dist/outbox/take-batch.d.ts +3 -0
  39. package/dist/outbox/types.d.ts +61 -0
  40. package/dist/pull-requests/build-link-change.d.ts +11 -0
  41. package/dist/pull-requests/build-state-fact.d.ts +8 -0
  42. package/dist/pull-requests/collect-attempt-trailers.d.ts +2 -0
  43. package/dist/pull-requests/collect-seen-state.d.ts +7 -0
  44. package/dist/pull-requests/constants.d.ts +3 -0
  45. package/dist/pull-requests/link-pull-request.d.ts +2 -0
  46. package/dist/pull-requests/parse-pull-request-url.d.ts +2 -0
  47. package/dist/pull-requests/types.d.ts +37 -0
  48. package/dist/pull-requests/watch-pull-requests.d.ts +2 -0
  49. package/dist/readings/act-on-reading.d.ts +3 -0
  50. package/dist/readings/find-waiting-since.d.ts +9 -0
  51. package/dist/readings/pick-reading-action.d.ts +2 -0
  52. package/dist/readings/types.d.ts +79 -0
  53. package/dist/sessions/build-keep-writes.d.ts +9 -0
  54. package/dist/sessions/build-kept-clear.d.ts +2 -0
  55. package/dist/sessions/forget-sessions.d.ts +2 -0
  56. package/dist/sessions/is-kept-due.d.ts +2 -0
  57. package/dist/sessions/resume-kept-session.d.ts +6 -0
  58. package/dist/sessions/types.d.ts +19 -0
  59. package/dist/test-utils/apply-memory-change.d.ts +3 -0
  60. package/dist/test-utils/build-memory-reader.d.ts +3 -0
  61. package/dist/test-utils/build-mock-claim-change.d.ts +2 -2
  62. package/dist/test-utils/build-mock-new-id.d.ts +2 -0
  63. package/dist/test-utils/build-mock-note-change.d.ts +2 -0
  64. package/dist/test-utils/build-mock-pull-request-change.d.ts +1 -0
  65. package/dist/test-utils/{build-mock-link-change.d.ts → build-mock-relation-change.d.ts} +2 -2
  66. package/dist/test-utils/build-mock-running-reading.d.ts +6 -0
  67. package/dist/test-utils/build-mock-start-change.d.ts +2 -2
  68. package/dist/test-utils/claim-store-close-cases.d.ts +2 -0
  69. package/dist/test-utils/claim-store-copy-cases.d.ts +2 -0
  70. package/dist/test-utils/claim-store-outbox-cases.d.ts +2 -0
  71. package/dist/test-utils/claim-store-race-cases.d.ts +2 -0
  72. package/dist/test-utils/claim-store-seat-cases.d.ts +2 -0
  73. package/dist/test-utils/claim-store-session-cases.d.ts +2 -0
  74. package/dist/test-utils/collect-memory-limit-fills.d.ts +3 -0
  75. package/dist/test-utils/compare-text.d.ts +1 -0
  76. package/dist/test-utils/create-fake-backoff.d.ts +2 -0
  77. package/dist/test-utils/create-fake-clock.d.ts +5 -0
  78. package/dist/test-utils/create-fake-fact-sink.d.ts +8 -0
  79. package/dist/test-utils/create-fake-forge.d.ts +17 -0
  80. package/dist/test-utils/create-fake-worker.d.ts +42 -0
  81. package/dist/test-utils/create-memory-claim-store.d.ts +2 -2
  82. package/dist/test-utils/create-test-dispatch.d.ts +46 -0
  83. package/dist/test-utils/forge-contract.d.ts +15 -0
  84. package/dist/test-utils/is-same-held-seat.d.ts +2 -0
  85. package/dist/test-utils/types.d.ts +12 -2
  86. package/dist/test-utils/worker-contract.d.ts +14 -0
  87. package/dist/timeouts.d.ts +2 -0
  88. package/dist/types.d.ts +13 -0
  89. package/package.json +10 -3
  90. package/src/attempts/attempt-machine.ts +147 -0
  91. package/src/attempts/attempt-moves.ts +218 -0
  92. package/src/attempts/bind-attempt-session.ts +61 -0
  93. package/src/attempts/build-attempt-change.ts +540 -0
  94. package/src/attempts/build-attempt-fact.ts +135 -0
  95. package/src/attempts/end-item-attempts.ts +95 -0
  96. package/src/attempts/is-same-guard.ts +22 -0
  97. package/src/attempts/pick-call-attempt.ts +32 -0
  98. package/src/attempts/stop-stray-session.ts +48 -0
  99. package/src/attempts/types.ts +231 -0
  100. package/src/attempts/write-attempt-move.ts +41 -0
  101. package/src/attempts/write-session-bind.ts +47 -0
  102. package/src/claims/build-snapshot-hash.ts +9 -0
  103. package/src/claims/claim-next.ts +306 -0
  104. package/src/claims/pick-kept-session.ts +41 -0
  105. package/src/claims/request-wake.ts +17 -0
  106. package/src/claims/resume-claim.ts +56 -0
  107. package/src/claims/start-claim.ts +99 -0
  108. package/src/claims/types.ts +67 -0
  109. package/src/closeouts/build-settle-plans.ts +173 -0
  110. package/src/closeouts/close-out-attempt.ts +156 -0
  111. package/src/closeouts/effect-handlers.ts +232 -0
  112. package/src/closeouts/pick-settlement.ts +80 -0
  113. package/src/closeouts/settle-attempt.ts +161 -0
  114. package/src/closeouts/types.ts +87 -0
  115. package/src/index.ts +61 -0
  116. package/src/notices/constants.ts +7 -0
  117. package/src/notices/create-null-notifier.ts +6 -0
  118. package/src/notices/send-notices.ts +41 -0
  119. package/src/notices/types.ts +14 -0
  120. package/src/outbox/build-backoff-ms.ts +14 -0
  121. package/src/outbox/build-initial-sender-state.ts +14 -0
  122. package/src/outbox/build-outbox-status.ts +43 -0
  123. package/src/outbox/constants.ts +15 -0
  124. package/src/outbox/measure-wire-bytes.ts +31 -0
  125. package/src/outbox/pick-sender-move.ts +37 -0
  126. package/src/outbox/run-sender-once.ts +174 -0
  127. package/src/outbox/take-batch.ts +35 -0
  128. package/src/outbox/types.ts +76 -0
  129. package/src/pull-requests/build-link-change.ts +95 -0
  130. package/src/pull-requests/build-state-fact.ts +39 -0
  131. package/src/pull-requests/collect-attempt-trailers.ts +20 -0
  132. package/src/pull-requests/collect-seen-state.ts +19 -0
  133. package/src/pull-requests/constants.ts +5 -0
  134. package/src/pull-requests/link-pull-request.ts +119 -0
  135. package/src/pull-requests/parse-pull-request-url.ts +30 -0
  136. package/src/pull-requests/types.ts +63 -0
  137. package/src/pull-requests/watch-pull-requests.ts +308 -0
  138. package/src/readings/act-on-reading.ts +318 -0
  139. package/src/readings/find-waiting-since.ts +46 -0
  140. package/src/readings/pick-reading-action.ts +315 -0
  141. package/src/readings/types.ts +98 -0
  142. package/src/sessions/build-keep-writes.ts +55 -0
  143. package/src/sessions/build-kept-clear.ts +12 -0
  144. package/src/sessions/forget-sessions.ts +106 -0
  145. package/src/sessions/is-kept-due.ts +27 -0
  146. package/src/sessions/resume-kept-session.ts +25 -0
  147. package/src/sessions/types.ts +39 -0
  148. package/src/test-utils/apply-memory-change.ts +252 -0
  149. package/src/test-utils/build-memory-reader.ts +364 -0
  150. package/src/test-utils/build-mock-bind-change.ts +18 -11
  151. package/src/test-utils/build-mock-claim-change.ts +18 -8
  152. package/src/test-utils/build-mock-file-item-change.ts +3 -1
  153. package/src/test-utils/build-mock-new-id.ts +12 -0
  154. package/src/test-utils/build-mock-note-change.ts +18 -0
  155. package/src/test-utils/build-mock-pause-change.ts +2 -10
  156. package/src/test-utils/build-mock-pull-request-change.ts +19 -9
  157. package/src/test-utils/{build-mock-link-change.ts → build-mock-relation-change.ts} +6 -6
  158. package/src/test-utils/build-mock-running-reading.ts +19 -0
  159. package/src/test-utils/build-mock-settle-change.ts +18 -6
  160. package/src/test-utils/build-mock-start-change.ts +13 -6
  161. package/src/test-utils/claim-store-attempt-reader-cases.ts +163 -133
  162. package/src/test-utils/claim-store-claim-cases.ts +168 -85
  163. package/src/test-utils/claim-store-close-cases.ts +95 -0
  164. package/src/test-utils/claim-store-contract.ts +12 -2
  165. package/src/test-utils/claim-store-copy-cases.ts +100 -0
  166. package/src/test-utils/claim-store-exclusive-cases.ts +65 -19
  167. package/src/test-utils/claim-store-guard-cases.ts +92 -82
  168. package/src/test-utils/claim-store-item-reader-cases.ts +291 -77
  169. package/src/test-utils/claim-store-outbox-cases.ts +281 -0
  170. package/src/test-utils/claim-store-race-cases.ts +150 -0
  171. package/src/test-utils/claim-store-rollback-cases.ts +50 -50
  172. package/src/test-utils/claim-store-seat-cases.ts +270 -0
  173. package/src/test-utils/claim-store-session-cases.ts +289 -0
  174. package/src/test-utils/collect-memory-limit-fills.ts +15 -0
  175. package/src/test-utils/compare-text.ts +8 -0
  176. package/src/test-utils/create-fake-backoff.ts +16 -0
  177. package/src/test-utils/create-fake-clock.ts +20 -0
  178. package/src/test-utils/create-fake-fact-sink.ts +74 -0
  179. package/src/test-utils/create-fake-forge.ts +117 -0
  180. package/src/test-utils/create-fake-worker.ts +303 -0
  181. package/src/test-utils/create-memory-claim-store.ts +73 -484
  182. package/src/test-utils/create-test-dispatch.ts +597 -0
  183. package/src/test-utils/forge-contract.ts +171 -0
  184. package/src/test-utils/is-same-held-seat.ts +11 -0
  185. package/src/test-utils/types.ts +14 -2
  186. package/src/test-utils/worker-contract.ts +192 -0
  187. package/src/timeouts.ts +17 -0
  188. package/src/types.ts +32 -0
  189. package/dist/test-utils/claim-store-pool-cases.d.ts +0 -2
  190. package/src/test-utils/claim-store-pool-cases.ts +0 -178
@@ -1,7 +1,7 @@
1
1
  import { expect } from 'bun:test';
2
2
  import { brandSchemas } from '@boxxy/model';
3
- import { buildMockAttemptRow } from '@boxxy/model/test-utils/build-mock-attempt-row';
4
- import { buildMockItemRow } from '@boxxy/model/test-utils/build-mock-item-row';
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
5
  import { buildMockClaimChange } from './build-mock-claim-change.ts';
6
6
  import { buildMockFileItemChange } from './build-mock-file-item-change.ts';
7
7
  import { buildMockPauseChange } from './build-mock-pause-change.ts';
@@ -15,7 +15,10 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
15
15
  '#readLastLeaseGen returns 0 for an item phase with no lease',
16
16
  async (store) => {
17
17
  const generation = await store.read((transaction) =>
18
- transaction.readLastLeaseGen(buildMockItemRow().id, brandSchemas.phaseName.parse('build')),
18
+ transaction.readLastLeaseGen(
19
+ buildMockV1ItemRow().id,
20
+ brandSchemas.phaseName.parse('build'),
21
+ ),
19
22
  );
20
23
 
21
24
  return { received: generation, expected: 0 };
@@ -24,17 +27,13 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
24
27
  [
25
28
  '#readLastLeaseGen returns the highest gen after a released lease',
26
29
  async (store) => {
27
- const item = buildMockItemRow();
28
- const attempt = buildMockAttemptRow({ item: item.id, state: 'starting', leaseGen: 3 });
30
+ const item = buildMockV1ItemRow();
31
+ const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting', leaseGen: 3 });
29
32
 
30
33
  await store.exclusive(async (transaction) => {
31
- await transaction.syncPoolSlots([{ name: attempt.pool, slots: 1 }]);
32
34
  await transaction.applyChange(buildMockFileItemChange(item));
33
35
  await transaction.applyChange(buildMockClaimChange(attempt));
34
-
35
- await transaction.applyChange(
36
- buildMockSettleChange({ attempt: { ...attempt, slot: 1, slotGen: 1 }, state: 'failed' }),
37
- );
36
+ await transaction.applyChange(buildMockSettleChange({ attempt, state: 'failed' }));
38
37
  });
39
38
 
40
39
  const generation = await store.read((transaction) =>
@@ -45,57 +44,96 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
45
44
  },
46
45
  ],
47
46
  [
48
- '#listLiveAttempts returns the attempts that have not settled',
47
+ '#listLiveAttempts returns every attempt that has not settled, a sleeping one too',
49
48
  async (store) => {
50
- const first = buildMockItemRow();
51
- const second = buildMockItemRow();
52
- const live = buildMockAttemptRow({ item: first.id, state: 'starting' });
53
- const settled = buildMockAttemptRow({ item: second.id, state: 'starting' });
49
+ const live = buildMockV1AttemptRow({
50
+ state: 'starting',
51
+ claimedAt: brandSchemas.instant.parse(1000),
52
+ });
53
+
54
+ const sleeping = buildMockV1AttemptRow({
55
+ state: 'sleeping',
56
+ claimedAt: brandSchemas.instant.parse(1100),
57
+ });
58
+
59
+ const settled = buildMockV1AttemptRow({ state: 'starting' });
54
60
 
55
61
  await store.exclusive(async (transaction) => {
56
- await transaction.syncPoolSlots([{ name: live.pool, slots: 2 }]);
57
- await transaction.applyChange(buildMockFileItemChange(first));
58
- await transaction.applyChange(buildMockFileItemChange(second));
59
- await transaction.applyChange(buildMockClaimChange(live));
60
- await transaction.applyChange(buildMockClaimChange(settled));
62
+ for (const attempt of [live, sleeping, settled]) {
63
+ await transaction.applyChange(
64
+ buildMockFileItemChange(buildMockV1ItemRow({ id: attempt.item })),
65
+ );
66
+
67
+ await transaction.applyChange(buildMockClaimChange(attempt));
68
+ }
69
+
70
+ await transaction.applyChange(buildMockSettleChange({ attempt: settled, state: 'failed' }));
71
+ });
72
+
73
+ const listed = await store.read((transaction) => transaction.listLiveAttempts());
74
+
75
+ return { received: listed.map((attempt) => attempt.id), expected: [live.id, sleeping.id] };
76
+ },
77
+ ],
78
+ [
79
+ '#listItemAttempts returns the attempts of one item, oldest first',
80
+ async (store) => {
81
+ const item = buildMockV1ItemRow();
82
+
83
+ const second = buildMockV1AttemptRow({
84
+ item: item.id,
85
+ state: 'starting',
86
+ number: 2,
87
+ leaseGen: 2,
88
+ claimedAt: brandSchemas.instant.parse(4000),
89
+ });
90
+
91
+ const first = buildMockV1AttemptRow({
92
+ item: item.id,
93
+ state: 'starting',
94
+ claimedAt: brandSchemas.instant.parse(1000),
95
+ });
96
+
97
+ const other = buildMockV1AttemptRow({ state: 'starting' });
98
+
99
+ await store.exclusive(async (transaction) => {
100
+ await transaction.applyChange(buildMockFileItemChange(item));
61
101
 
62
102
  await transaction.applyChange(
63
- buildMockSettleChange({ attempt: { ...settled, slot: 2, slotGen: 1 }, state: 'failed' }),
103
+ buildMockFileItemChange(buildMockV1ItemRow({ id: other.item })),
64
104
  );
105
+
106
+ await transaction.applyChange(buildMockClaimChange(first));
107
+ await transaction.applyChange(buildMockSettleChange({ attempt: first, state: 'failed' }));
108
+ await transaction.applyChange(buildMockClaimChange(second));
109
+ await transaction.applyChange(buildMockClaimChange(other));
65
110
  });
66
111
 
67
- const listed = await store.read((transaction) => transaction.listLiveAttempts());
112
+ const listed = await store.read((transaction) => transaction.listItemAttempts(item.id));
68
113
 
69
- return { received: listed.map((attempt) => attempt.id), expected: [live.id] };
114
+ return { received: listed.map((attempt) => attempt.id), expected: [first.id, second.id] };
70
115
  },
71
116
  ],
72
117
  [
73
- '#listSucceededAttemptsWithoutPr skips an attempt with a linked PR',
118
+ '#listSucceededAttemptsWithoutPullRequest skips an attempt with a linked pull request',
74
119
  async (store) => {
75
- const first = buildMockItemRow();
76
- const second = buildMockItemRow();
77
- const linked = buildMockAttemptRow({ item: first.id, state: 'starting' });
78
- const unlinked = buildMockAttemptRow({ item: second.id, state: 'starting' });
120
+ const first = buildMockV1ItemRow();
121
+ const second = buildMockV1ItemRow();
122
+ const linked = buildMockV1AttemptRow({ item: first.id, state: 'starting' });
123
+ const unlinked = buildMockV1AttemptRow({ item: second.id, state: 'starting' });
79
124
 
80
125
  await store.exclusive(async (transaction) => {
81
- await transaction.syncPoolSlots([{ name: linked.pool, slots: 2 }]);
82
126
  await transaction.applyChange(buildMockFileItemChange(first));
83
127
  await transaction.applyChange(buildMockFileItemChange(second));
84
128
  await transaction.applyChange(buildMockClaimChange(linked));
85
129
  await transaction.applyChange(buildMockClaimChange(unlinked));
86
130
 
87
131
  await transaction.applyChange(
88
- buildMockSettleChange({
89
- attempt: { ...linked, slot: 1, slotGen: 1 },
90
- state: 'succeeded',
91
- }),
132
+ buildMockSettleChange({ attempt: linked, state: 'succeeded' }),
92
133
  );
93
134
 
94
135
  await transaction.applyChange(
95
- buildMockSettleChange({
96
- attempt: { ...unlinked, slot: 2, slotGen: 1 },
97
- state: 'succeeded',
98
- }),
136
+ buildMockSettleChange({ attempt: unlinked, state: 'succeeded' }),
99
137
  );
100
138
 
101
139
  await transaction.applyChange(
@@ -104,7 +142,7 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
104
142
  });
105
143
 
106
144
  const listed = await store.read((transaction) =>
107
- transaction.listSucceededAttemptsWithoutPr(),
145
+ transaction.listSucceededAttemptsWithoutPullRequest(),
108
146
  );
109
147
 
110
148
  return { received: listed.map((attempt) => attempt.id), expected: [unlinked.id] };
@@ -113,11 +151,15 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
113
151
  [
114
152
  '#findAttemptBySession returns the attempt with an atc session id',
115
153
  async (store) => {
116
- const item = buildMockItemRow();
117
- const attempt = buildMockAttemptRow({ item: item.id, state: 'starting' });
154
+ const item = buildMockV1ItemRow();
155
+
156
+ const attempt = buildMockV1AttemptRow({
157
+ item: item.id,
158
+ state: 'starting',
159
+ atcSession: brandSchemas.atcSessionId.parse('atc-session-1'),
160
+ });
118
161
 
119
162
  await store.exclusive(async (transaction) => {
120
- await transaction.syncPoolSlots([{ name: attempt.pool, slots: 1 }]);
121
163
  await transaction.applyChange(buildMockFileItemChange(item));
122
164
  await transaction.applyChange(buildMockClaimChange(attempt));
123
165
  await transaction.applyChange(buildMockStartChange(attempt));
@@ -131,13 +173,46 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
131
173
  },
132
174
  ],
133
175
  [
134
- '#markPullRequestChecked sets the checked time of a watched PR',
176
+ '#findAttemptBySession returns the latest of two attempts on one kept session',
135
177
  async (store) => {
136
- const item = buildMockItemRow();
137
- const attempt = buildMockAttemptRow({ item: item.id, state: 'starting' });
178
+ const item = buildMockV1ItemRow();
179
+ const atcSession = brandSchemas.atcSessionId.parse('atc-session-1');
180
+
181
+ const earlier = buildMockV1AttemptRow({
182
+ item: item.id,
183
+ state: 'starting',
184
+ atcSession,
185
+ claimedAt: brandSchemas.instant.parse(1000),
186
+ });
187
+
188
+ const later = buildMockV1AttemptRow({
189
+ item: item.id,
190
+ state: 'starting',
191
+ number: 2,
192
+ leaseGen: 2,
193
+ atcSession,
194
+ claimedAt: brandSchemas.instant.parse(4000),
195
+ });
196
+
197
+ await store.exclusive(async (transaction) => {
198
+ await transaction.applyChange(buildMockFileItemChange(item));
199
+ await transaction.applyChange(buildMockClaimChange(earlier));
200
+ await transaction.applyChange(buildMockSettleChange({ attempt: earlier, state: 'failed' }));
201
+ await transaction.applyChange(buildMockClaimChange(later));
202
+ });
203
+
204
+ const found = await store.read((transaction) => transaction.findAttemptBySession(atcSession));
205
+
206
+ return { received: found?.id, expected: later.id };
207
+ },
208
+ ],
209
+ [
210
+ '#markPullRequestChecked sets the checked time of a watched pull request',
211
+ async (store) => {
212
+ const item = buildMockV1ItemRow();
213
+ const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting' });
138
214
 
139
215
  await store.exclusive(async (transaction) => {
140
- await transaction.syncPoolSlots([{ name: attempt.pool, slots: 1 }]);
141
216
  await transaction.applyChange(buildMockFileItemChange(item));
142
217
  await transaction.applyChange(buildMockClaimChange(attempt));
143
218
  await transaction.applyChange(buildMockPullRequestChange({ attempt, state: 'open' }));
@@ -156,31 +231,64 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
156
231
  },
157
232
  ],
158
233
  [
159
- '#listWatchedPullRequests skips a merged PR',
234
+ '#readPullRequestRow returns a merged pull request, and null for one never linked',
160
235
  async (store) => {
161
- const item = buildMockItemRow();
162
- const attempt = buildMockAttemptRow({ item: item.id, state: 'starting' });
236
+ const item = buildMockV1ItemRow();
237
+ const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting' });
238
+ const repo = brandSchemas.repoName.parse('acme/lamps');
163
239
 
164
240
  await store.exclusive(async (transaction) => {
165
- await transaction.syncPoolSlots([{ name: attempt.pool, slots: 1 }]);
166
241
  await transaction.applyChange(buildMockFileItemChange(item));
167
242
  await transaction.applyChange(buildMockClaimChange(attempt));
168
243
  await transaction.applyChange(buildMockPullRequestChange({ attempt, state: 'merged' }));
169
244
  });
170
245
 
246
+ const found = await store.read((transaction) =>
247
+ transaction.readPullRequestRow({ repo, number: 7 }),
248
+ );
249
+
250
+ const missing = await store.read((transaction) =>
251
+ transaction.readPullRequestRow({ repo, number: 8 }),
252
+ );
253
+
254
+ return { received: [found?.state, missing], expected: ['merged', null] };
255
+ },
256
+ ],
257
+ [
258
+ '#listWatchedPullRequests returns an open pull request with its GitHub id and url',
259
+ async (store) => {
260
+ const item = buildMockV1ItemRow();
261
+ const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting' });
262
+
263
+ await store.exclusive(async (transaction) => {
264
+ await transaction.applyChange(buildMockFileItemChange(item));
265
+ await transaction.applyChange(buildMockClaimChange(attempt));
266
+ await transaction.applyChange(buildMockPullRequestChange({ attempt, state: 'merged' }));
267
+
268
+ await transaction.applyChange(
269
+ buildMockPullRequestChange({ attempt, state: 'open', number: 8 }),
270
+ );
271
+ });
272
+
171
273
  const watched = await store.read((transaction) => transaction.listWatchedPullRequests());
172
274
 
173
- return { received: watched, expected: [] };
275
+ return {
276
+ received: watched.map((pullRequest) => [
277
+ pullRequest.number,
278
+ pullRequest.githubId,
279
+ pullRequest.url,
280
+ ]),
281
+ expected: [[8, 2_000_008, 'https://github.com/acme/lamps/pull/8']],
282
+ };
174
283
  },
175
284
  ],
176
285
  [
177
286
  '#listEvidence returns the evidence of one attempt',
178
287
  async (store) => {
179
- const item = buildMockItemRow();
180
- const attempt = buildMockAttemptRow({ item: item.id, state: 'starting' });
288
+ const item = buildMockV1ItemRow();
289
+ const attempt = buildMockV1AttemptRow({ item: item.id, state: 'starting' });
181
290
 
182
291
  await store.exclusive(async (transaction) => {
183
- await transaction.syncPoolSlots([{ name: attempt.pool, slots: 1 }]);
184
292
  await transaction.applyChange(buildMockFileItemChange(item));
185
293
  await transaction.applyChange(buildMockClaimChange(attempt));
186
294
  await transaction.applyChange(buildMockPullRequestChange({ attempt, state: 'open' }));
@@ -194,9 +302,9 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
194
302
  received: evidence,
195
303
  expected: [
196
304
  {
197
- factId: expect.stringMatching(/^manual:/u),
305
+ factId: expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-7/u),
198
306
  attempt: attempt.id,
199
- kind: 'pr',
307
+ kind: 'pull_request',
200
308
  ref: 'https://github.com/acme/lamps/pull/7',
201
309
  observed: null,
202
310
  source: 'hook',
@@ -206,84 +314,6 @@ export const claimStoreAttemptReaderCases: readonly ClaimStoreCase[] = [
206
314
  };
207
315
  },
208
316
  ],
209
- [
210
- '#markFactsSent drops the facts from the unsent facts and the outbox lag',
211
- async (store) => {
212
- const first = buildMockFileItemChange(
213
- buildMockItemRow({ filedAt: brandSchemas.instant.parse(1000) }),
214
- );
215
-
216
- const second = buildMockFileItemChange(
217
- buildMockItemRow({ filedAt: brandSchemas.instant.parse(2000) }),
218
- );
219
-
220
- await store.exclusive(async (transaction) => {
221
- await transaction.applyChange(first);
222
- await transaction.applyChange(second);
223
- });
224
-
225
- await store.exclusive((transaction) =>
226
- transaction.markFactsSent(
227
- first.facts.map((fact) => fact.id),
228
- brandSchemas.instant.parse(3000),
229
- ),
230
- );
231
-
232
- const outbox: unknown = await store.read(async (transaction) => {
233
- const unsent = await transaction.readUnsentFacts(10);
234
-
235
- return {
236
- unsent: unsent.map((fact) => fact.factId),
237
- lag: await transaction.readOutboxLag(),
238
- };
239
- });
240
-
241
- return {
242
- received: outbox,
243
- expected: {
244
- unsent: second.facts.map((fact) => fact.id),
245
- lag: { unsent: 1, oldestUnsentAt: 2000 },
246
- },
247
- };
248
- },
249
- ],
250
- [
251
- '#readUnsentFacts returns at most the limit, oldest first',
252
- async (store) => {
253
- await store.exclusive(async (transaction) => {
254
- await transaction.applyChange(buildMockFileItemChange(buildMockItemRow()));
255
- await transaction.applyChange(buildMockFileItemChange(buildMockItemRow()));
256
- });
257
-
258
- const unsent = await store.read((transaction) => transaction.readUnsentFacts(1));
259
-
260
- return { received: unsent.map((fact) => fact.seq), expected: [1] };
261
- },
262
- ],
263
- [
264
- '#readOutboxLag reads no lag from an empty outbox',
265
- async (store) => {
266
- const lag: unknown = await store.read((transaction) => transaction.readOutboxLag());
267
-
268
- return { received: lag, expected: { unsent: 0, oldestUnsentAt: null } };
269
- },
270
- ],
271
- [
272
- '#readOutboxLag reads the earliest time of the unsent facts, whatever their order',
273
- async (store) => {
274
- const later = buildMockItemRow({ filedAt: brandSchemas.instant.parse(2000) });
275
- const earlier = buildMockItemRow({ filedAt: brandSchemas.instant.parse(1000) });
276
-
277
- await store.exclusive(async (transaction) => {
278
- await transaction.applyChange(buildMockFileItemChange(later));
279
- await transaction.applyChange(buildMockFileItemChange(earlier));
280
- });
281
-
282
- const lag: unknown = await store.read((transaction) => transaction.readOutboxLag());
283
-
284
- return { received: lag, expected: { unsent: 2, oldestUnsentAt: 1000 } };
285
- },
286
- ],
287
317
  [
288
318
  '#writeHeartbeat inserts the runner row on its first beat, not paused',
289
319
  async (store) => {