返回 CodeWhale
draft-claim-lock.fake.ts
根目录 / web / lib / draft-claim-lock.fake.ts
1 /**
2 * Test double for the DRAFT_CLAIM_LOCK namespace. Each name gets its own
3 * storage, and each object runs one `act` at a time to completion, which is
4 * the guarantee a Durable Object's input gate gives the real class. Storage
5 * calls still yield to the event loop, so callers genuinely overlap.
6 */
7 import {
8 applyDraftLock,
9 type DraftClaimLockId,
10 type DraftClaimLockNamespace,
11 type DraftClaimLockStub,
12 type DraftLockRequest,
13 type DraftLockResponse,
14 type DraftLockStorage,
15 } from "./draft-claim-lock";
16
17 class MapStorage implements DraftLockStorage {
18 readonly values = new Map<string, unknown>();
19 private async tick() {
20 await new Promise((resolve) => setTimeout(resolve, 0));
21 }
22 async get<T>(key: string) {
23 await this.tick();
24 return this.values.get(key) as T | undefined;
25 }
26 async put<T>(key: string, value: T) {
27 await this.tick();
28 this.values.set(key, structuredClone(value));
29 }
30 async delete(key: string) {
31 await this.tick();
32 return this.values.delete(key);
33 }
34 }
35
36 class FakeObject implements DraftClaimLockStub {
37 readonly storage = new MapStorage();
38 private queue: Promise<unknown> = Promise.resolve();
39 constructor(private readonly clock: () => number, private readonly calls: DraftLockRequest[]) {}
40
41 act(req: DraftLockRequest): Promise<DraftLockResponse> {
42 this.calls.push(req);
43 const run = this.queue.then(() => applyDraftLock(this.storage, this.clock(), req));
44 this.queue = run.catch(() => undefined);
45 return run;
46 }
47 }
48
49 export class FakeDraftClaimLock implements DraftClaimLockNamespace {
50 now = 1_700_000_000_000;
51 readonly calls: DraftLockRequest[] = [];
52 private readonly objects = new Map<string, FakeObject>();
53
54 idFromName(name: string): DraftClaimLockId {
55 return { toString: () => name };
56 }
57
58 get(id: DraftClaimLockId): FakeObject {
59 const name = id.toString();
60 let obj = this.objects.get(name);
61 if (!obj) {
62 obj = new FakeObject(() => this.now, this.calls);
63 this.objects.set(name, obj);
64 }
65 return obj;
66 }
67 }
68
68 lines TYPESCRIPT