@decentrl/sdk 0.0.1
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/client.d.ts +36 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +192 -0
- package/dist/contract-manager.d.ts +23 -0
- package/dist/contract-manager.d.ts.map +1 -0
- package/dist/contract-manager.js +91 -0
- package/dist/define-app.d.ts +8 -0
- package/dist/define-app.d.ts.map +1 -0
- package/dist/define-app.js +7 -0
- package/dist/direct-transport.d.ts +69 -0
- package/dist/direct-transport.d.ts.map +1 -0
- package/dist/direct-transport.js +450 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +10 -0
- package/dist/event-processor.d.ts +19 -0
- package/dist/event-processor.d.ts.map +1 -0
- package/dist/event-processor.js +93 -0
- package/dist/identity-manager.d.ts +22 -0
- package/dist/identity-manager.d.ts.map +1 -0
- package/dist/identity-manager.js +62 -0
- package/dist/identity-serialization.d.ts +5 -0
- package/dist/identity-serialization.d.ts.map +1 -0
- package/dist/identity-serialization.js +30 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/persistence.d.ts +11 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +82 -0
- package/dist/state-store.d.ts +12 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +32 -0
- package/dist/sync-manager.d.ts +33 -0
- package/dist/sync-manager.d.ts.map +1 -0
- package/dist/sync-manager.js +244 -0
- package/dist/tag-templates.d.ts +2 -0
- package/dist/tag-templates.d.ts.map +1 -0
- package/dist/tag-templates.js +23 -0
- package/dist/test-helpers.d.ts +15 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +65 -0
- package/dist/transport.d.ts +41 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/websocket-transport.d.ts +36 -0
- package/dist/websocket-transport.d.ts.map +1 -0
- package/dist/websocket-transport.js +160 -0
- package/package.json +35 -0
- package/src/client.ts +277 -0
- package/src/contract-manager.test.ts +207 -0
- package/src/contract-manager.ts +130 -0
- package/src/define-app.ts +25 -0
- package/src/direct-transport.test.ts +460 -0
- package/src/direct-transport.ts +729 -0
- package/src/errors.ts +23 -0
- package/src/event-processor.ts +133 -0
- package/src/identity-manager.ts +91 -0
- package/src/identity-serialization.ts +33 -0
- package/src/index.ts +43 -0
- package/src/persistence.ts +103 -0
- package/src/sdk.e2e.test.ts +367 -0
- package/src/state-store.ts +42 -0
- package/src/sync-manager.test.ts +414 -0
- package/src/sync-manager.ts +308 -0
- package/src/tag-templates.test.ts +111 -0
- package/src/tag-templates.ts +30 -0
- package/src/test-helpers.ts +88 -0
- package/src/transport.ts +65 -0
- package/src/types.ts +191 -0
- package/src/websocket-transport.ts +233 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { DirectTransport } from './direct-transport.js';
|
|
3
|
+
import { makeContract } from './test-helpers.js';
|
|
4
|
+
|
|
5
|
+
describe('DirectTransport', () => {
|
|
6
|
+
describe('processContractCleanup', () => {
|
|
7
|
+
let transport: DirectTransport;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
transport = new DirectTransport({
|
|
11
|
+
httpPost: vi.fn(async () => ({ data: { type: 'SUCCESS' } })),
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('does nothing when only one contract per participant', () => {
|
|
16
|
+
const now = Math.floor(Date.now() / 1000);
|
|
17
|
+
const contract = makeContract({
|
|
18
|
+
id: 'only-one',
|
|
19
|
+
participantDid: 'did:decentrl:bob',
|
|
20
|
+
expiresAt: now + 3600,
|
|
21
|
+
timestamp: now,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Inject contracts via the internal array
|
|
25
|
+
(transport as any).activeContracts = [contract];
|
|
26
|
+
|
|
27
|
+
transport.processContractCleanup();
|
|
28
|
+
|
|
29
|
+
expect(transport.getActiveContracts()).toHaveLength(1);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('marks superseded contracts and purges on counterparty confirmation', () => {
|
|
33
|
+
const now = Math.floor(Date.now() / 1000);
|
|
34
|
+
|
|
35
|
+
const oldContract = makeContract({
|
|
36
|
+
id: 'old',
|
|
37
|
+
participantDid: 'did:decentrl:bob',
|
|
38
|
+
expiresAt: now + 3600,
|
|
39
|
+
timestamp: now,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const newContract = makeContract({
|
|
43
|
+
id: 'new',
|
|
44
|
+
participantDid: 'did:decentrl:bob',
|
|
45
|
+
expiresAt: now + 7200,
|
|
46
|
+
timestamp: now + 100,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
(transport as any).activeContracts = [oldContract, newContract];
|
|
50
|
+
|
|
51
|
+
// First cleanup: mark as superseded, but don't purge yet
|
|
52
|
+
transport.processContractCleanup();
|
|
53
|
+
expect(transport.getActiveContracts()).toHaveLength(2);
|
|
54
|
+
|
|
55
|
+
// Simulate counterparty confirmation
|
|
56
|
+
(transport as any).confirmedNewContracts.add('new');
|
|
57
|
+
|
|
58
|
+
// Second cleanup: should purge old
|
|
59
|
+
transport.processContractCleanup();
|
|
60
|
+
expect(transport.getActiveContracts()).toHaveLength(1);
|
|
61
|
+
expect(transport.getActiveContracts()[0].id).toBe('new');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('purges superseded contracts after 7-day timeout', () => {
|
|
65
|
+
const now = Math.floor(Date.now() / 1000);
|
|
66
|
+
|
|
67
|
+
const oldContract = makeContract({
|
|
68
|
+
id: 'old',
|
|
69
|
+
participantDid: 'did:decentrl:bob',
|
|
70
|
+
expiresAt: now + 3600,
|
|
71
|
+
timestamp: now,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const newContract = makeContract({
|
|
75
|
+
id: 'new',
|
|
76
|
+
participantDid: 'did:decentrl:bob',
|
|
77
|
+
expiresAt: now + 7200,
|
|
78
|
+
timestamp: now + 100,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
(transport as any).activeContracts = [oldContract, newContract];
|
|
82
|
+
|
|
83
|
+
// First cleanup: mark as superseded
|
|
84
|
+
transport.processContractCleanup();
|
|
85
|
+
expect(transport.getActiveContracts()).toHaveLength(2);
|
|
86
|
+
|
|
87
|
+
// Advance time past 7 days
|
|
88
|
+
const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
|
|
89
|
+
vi.spyOn(Date, 'now').mockReturnValue(Date.now() + sevenDaysMs);
|
|
90
|
+
|
|
91
|
+
transport.processContractCleanup();
|
|
92
|
+
expect(transport.getActiveContracts()).toHaveLength(1);
|
|
93
|
+
expect(transport.getActiveContracts()[0].id).toBe('new');
|
|
94
|
+
|
|
95
|
+
vi.restoreAllMocks();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('does not purge before timeout without confirmation', () => {
|
|
99
|
+
const now = Math.floor(Date.now() / 1000);
|
|
100
|
+
|
|
101
|
+
const oldContract = makeContract({
|
|
102
|
+
id: 'old',
|
|
103
|
+
participantDid: 'did:decentrl:bob',
|
|
104
|
+
expiresAt: now + 3600,
|
|
105
|
+
timestamp: now,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const newContract = makeContract({
|
|
109
|
+
id: 'new',
|
|
110
|
+
participantDid: 'did:decentrl:bob',
|
|
111
|
+
expiresAt: now + 7200,
|
|
112
|
+
timestamp: now + 100,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
(transport as any).activeContracts = [oldContract, newContract];
|
|
116
|
+
|
|
117
|
+
// First cleanup: mark as superseded
|
|
118
|
+
transport.processContractCleanup();
|
|
119
|
+
|
|
120
|
+
// Advance time by 3 days (less than 7)
|
|
121
|
+
const threeDaysMs = 3 * 24 * 60 * 60 * 1000;
|
|
122
|
+
vi.spyOn(Date, 'now').mockReturnValue(Date.now() + threeDaysMs);
|
|
123
|
+
|
|
124
|
+
// Second cleanup: should NOT purge yet
|
|
125
|
+
transport.processContractCleanup();
|
|
126
|
+
expect(transport.getActiveContracts()).toHaveLength(2);
|
|
127
|
+
|
|
128
|
+
vi.restoreAllMocks();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('handles multiple participants independently', () => {
|
|
132
|
+
const now = Math.floor(Date.now() / 1000);
|
|
133
|
+
|
|
134
|
+
const bobOld = makeContract({
|
|
135
|
+
id: 'bob-old',
|
|
136
|
+
participantDid: 'did:decentrl:bob',
|
|
137
|
+
expiresAt: now + 3600,
|
|
138
|
+
timestamp: now,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const bobNew = makeContract({
|
|
142
|
+
id: 'bob-new',
|
|
143
|
+
participantDid: 'did:decentrl:bob',
|
|
144
|
+
expiresAt: now + 7200,
|
|
145
|
+
timestamp: now + 100,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const carolOnly = makeContract({
|
|
149
|
+
id: 'carol-only',
|
|
150
|
+
participantDid: 'did:decentrl:carol',
|
|
151
|
+
expiresAt: now + 3600,
|
|
152
|
+
timestamp: now,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
(transport as any).activeContracts = [bobOld, bobNew, carolOnly];
|
|
156
|
+
|
|
157
|
+
// Mark as superseded
|
|
158
|
+
transport.processContractCleanup();
|
|
159
|
+
|
|
160
|
+
// Confirm bob's new contract
|
|
161
|
+
(transport as any).confirmedNewContracts.add('bob-new');
|
|
162
|
+
|
|
163
|
+
transport.processContractCleanup();
|
|
164
|
+
|
|
165
|
+
const active = transport.getActiveContracts();
|
|
166
|
+
expect(active).toHaveLength(2);
|
|
167
|
+
expect(active.map((c) => c.id).sort()).toEqual(['bob-new', 'carol-only']);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('processAutoRenewals', () => {
|
|
172
|
+
let transport: DirectTransport;
|
|
173
|
+
let httpPost: ReturnType<typeof vi.fn>;
|
|
174
|
+
|
|
175
|
+
beforeEach(() => {
|
|
176
|
+
httpPost = vi.fn(async () => ({ data: { type: 'SUCCESS' } }));
|
|
177
|
+
transport = new DirectTransport({ httpPost });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('initiates renewal when contract is near expiry and DID sorts lower', async () => {
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
const nowSec = Math.floor(now / 1000);
|
|
183
|
+
const lifetime = 3600;
|
|
184
|
+
const timestamp = nowSec - lifetime * 0.9; // 90% elapsed
|
|
185
|
+
|
|
186
|
+
// "alice" < "bob" alphabetically, so alice initiates
|
|
187
|
+
const contract = makeContract({
|
|
188
|
+
id: 'c1',
|
|
189
|
+
participantDid: 'did:decentrl:bob',
|
|
190
|
+
expiresAt: timestamp + lifetime,
|
|
191
|
+
timestamp,
|
|
192
|
+
});
|
|
193
|
+
contract.signedCommunicationContract.communication_contract.requestor_did =
|
|
194
|
+
'did:decentrl:alice';
|
|
195
|
+
|
|
196
|
+
// Set up identity
|
|
197
|
+
(transport as any).identity = {
|
|
198
|
+
did: 'did:decentrl:alice',
|
|
199
|
+
mediatorDid: 'did:web:mediator',
|
|
200
|
+
mediatorEndpoint: 'http://mediator',
|
|
201
|
+
keys: {
|
|
202
|
+
signing: {
|
|
203
|
+
privateKey: new Uint8Array(32),
|
|
204
|
+
publicKey: new Uint8Array(32),
|
|
205
|
+
},
|
|
206
|
+
encryption: {
|
|
207
|
+
privateKey: new Uint8Array(32),
|
|
208
|
+
publicKey: new Uint8Array(32),
|
|
209
|
+
},
|
|
210
|
+
storageKey: new Uint8Array(32),
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
(transport as any).activeContracts = [contract];
|
|
214
|
+
|
|
215
|
+
const requestSpy = vi.spyOn(transport, 'requestContract').mockResolvedValue();
|
|
216
|
+
|
|
217
|
+
await transport.processAutoRenewals(0.2);
|
|
218
|
+
|
|
219
|
+
expect(requestSpy).toHaveBeenCalledWith('did:decentrl:bob', lifetime);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('does NOT initiate when DID sorts higher than participant', async () => {
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
const nowSec = Math.floor(now / 1000);
|
|
225
|
+
const lifetime = 3600;
|
|
226
|
+
const timestamp = nowSec - lifetime * 0.9;
|
|
227
|
+
|
|
228
|
+
const contract = makeContract({
|
|
229
|
+
id: 'c1',
|
|
230
|
+
participantDid: 'did:decentrl:aaa', // alice > aaa
|
|
231
|
+
expiresAt: timestamp + lifetime,
|
|
232
|
+
timestamp,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
(transport as any).identity = {
|
|
236
|
+
did: 'did:decentrl:alice',
|
|
237
|
+
mediatorDid: 'did:web:mediator',
|
|
238
|
+
mediatorEndpoint: 'http://mediator',
|
|
239
|
+
keys: {
|
|
240
|
+
signing: {
|
|
241
|
+
privateKey: new Uint8Array(32),
|
|
242
|
+
publicKey: new Uint8Array(32),
|
|
243
|
+
},
|
|
244
|
+
encryption: {
|
|
245
|
+
privateKey: new Uint8Array(32),
|
|
246
|
+
publicKey: new Uint8Array(32),
|
|
247
|
+
},
|
|
248
|
+
storageKey: new Uint8Array(32),
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
(transport as any).activeContracts = [contract];
|
|
252
|
+
|
|
253
|
+
const requestSpy = vi.spyOn(transport, 'requestContract').mockResolvedValue();
|
|
254
|
+
|
|
255
|
+
await transport.processAutoRenewals(0.2);
|
|
256
|
+
|
|
257
|
+
expect(requestSpy).not.toHaveBeenCalled();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('does NOT initiate when contract is not near expiry', async () => {
|
|
261
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
262
|
+
const lifetime = 3600;
|
|
263
|
+
const timestamp = nowSec - lifetime * 0.1; // Only 10% elapsed
|
|
264
|
+
|
|
265
|
+
const contract = makeContract({
|
|
266
|
+
id: 'c1',
|
|
267
|
+
participantDid: 'did:decentrl:bob',
|
|
268
|
+
expiresAt: timestamp + lifetime,
|
|
269
|
+
timestamp,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
(transport as any).identity = {
|
|
273
|
+
did: 'did:decentrl:alice',
|
|
274
|
+
mediatorDid: 'did:web:mediator',
|
|
275
|
+
mediatorEndpoint: 'http://mediator',
|
|
276
|
+
keys: {
|
|
277
|
+
signing: {
|
|
278
|
+
privateKey: new Uint8Array(32),
|
|
279
|
+
publicKey: new Uint8Array(32),
|
|
280
|
+
},
|
|
281
|
+
encryption: {
|
|
282
|
+
privateKey: new Uint8Array(32),
|
|
283
|
+
publicKey: new Uint8Array(32),
|
|
284
|
+
},
|
|
285
|
+
storageKey: new Uint8Array(32),
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
(transport as any).activeContracts = [contract];
|
|
289
|
+
|
|
290
|
+
const requestSpy = vi.spyOn(transport, 'requestContract').mockResolvedValue();
|
|
291
|
+
|
|
292
|
+
await transport.processAutoRenewals(0.2);
|
|
293
|
+
|
|
294
|
+
expect(requestSpy).not.toHaveBeenCalled();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('prevents duplicate in-flight renewals for the same DID', async () => {
|
|
298
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
299
|
+
const lifetime = 3600;
|
|
300
|
+
const timestamp = nowSec - lifetime * 0.9;
|
|
301
|
+
|
|
302
|
+
const contract = makeContract({
|
|
303
|
+
id: 'c1',
|
|
304
|
+
participantDid: 'did:decentrl:bob',
|
|
305
|
+
expiresAt: timestamp + lifetime,
|
|
306
|
+
timestamp,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
(transport as any).identity = {
|
|
310
|
+
did: 'did:decentrl:alice',
|
|
311
|
+
mediatorDid: 'did:web:mediator',
|
|
312
|
+
mediatorEndpoint: 'http://mediator',
|
|
313
|
+
keys: {
|
|
314
|
+
signing: {
|
|
315
|
+
privateKey: new Uint8Array(32),
|
|
316
|
+
publicKey: new Uint8Array(32),
|
|
317
|
+
},
|
|
318
|
+
encryption: {
|
|
319
|
+
privateKey: new Uint8Array(32),
|
|
320
|
+
publicKey: new Uint8Array(32),
|
|
321
|
+
},
|
|
322
|
+
storageKey: new Uint8Array(32),
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
(transport as any).activeContracts = [contract];
|
|
326
|
+
|
|
327
|
+
let resolveRequest: () => void;
|
|
328
|
+
const blockingPromise = new Promise<void>((resolve) => {
|
|
329
|
+
resolveRequest = resolve;
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const requestSpy = vi.spyOn(transport, 'requestContract').mockReturnValue(blockingPromise);
|
|
333
|
+
|
|
334
|
+
// Start first renewal (will block)
|
|
335
|
+
const renewal1 = transport.processAutoRenewals(0.2);
|
|
336
|
+
|
|
337
|
+
// Start second renewal while first is in-flight
|
|
338
|
+
const renewal2 = transport.processAutoRenewals(0.2);
|
|
339
|
+
|
|
340
|
+
resolveRequest!();
|
|
341
|
+
await renewal1;
|
|
342
|
+
await renewal2;
|
|
343
|
+
|
|
344
|
+
// Should only request once
|
|
345
|
+
expect(requestSpy).toHaveBeenCalledTimes(1);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('clears in-flight flag even when request fails', async () => {
|
|
349
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
350
|
+
const lifetime = 3600;
|
|
351
|
+
const timestamp = nowSec - lifetime * 0.9;
|
|
352
|
+
|
|
353
|
+
const contract = makeContract({
|
|
354
|
+
id: 'c1',
|
|
355
|
+
participantDid: 'did:decentrl:bob',
|
|
356
|
+
expiresAt: timestamp + lifetime,
|
|
357
|
+
timestamp,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
(transport as any).identity = {
|
|
361
|
+
did: 'did:decentrl:alice',
|
|
362
|
+
mediatorDid: 'did:web:mediator',
|
|
363
|
+
mediatorEndpoint: 'http://mediator',
|
|
364
|
+
keys: {
|
|
365
|
+
signing: {
|
|
366
|
+
privateKey: new Uint8Array(32),
|
|
367
|
+
publicKey: new Uint8Array(32),
|
|
368
|
+
},
|
|
369
|
+
encryption: {
|
|
370
|
+
privateKey: new Uint8Array(32),
|
|
371
|
+
publicKey: new Uint8Array(32),
|
|
372
|
+
},
|
|
373
|
+
storageKey: new Uint8Array(32),
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
(transport as any).activeContracts = [contract];
|
|
377
|
+
|
|
378
|
+
const requestSpy = vi
|
|
379
|
+
.spyOn(transport, 'requestContract')
|
|
380
|
+
.mockRejectedValueOnce(new Error('network error'))
|
|
381
|
+
.mockResolvedValueOnce();
|
|
382
|
+
|
|
383
|
+
await transport.processAutoRenewals(0.2);
|
|
384
|
+
|
|
385
|
+
// First call fails, but in-flight flag should be cleared
|
|
386
|
+
expect(requestSpy).toHaveBeenCalledTimes(1);
|
|
387
|
+
|
|
388
|
+
// Second call should work
|
|
389
|
+
await transport.processAutoRenewals(0.2);
|
|
390
|
+
expect(requestSpy).toHaveBeenCalledTimes(2);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
describe('refreshContracts', () => {
|
|
395
|
+
it('filters out purged contracts', async () => {
|
|
396
|
+
const now = Math.floor(Date.now() / 1000);
|
|
397
|
+
|
|
398
|
+
const httpPost = vi.fn(async () => ({
|
|
399
|
+
data: {
|
|
400
|
+
type: 'SUCCESS',
|
|
401
|
+
payload: {
|
|
402
|
+
communication_contracts: [
|
|
403
|
+
{
|
|
404
|
+
signed_communication_contract: {
|
|
405
|
+
communication_contract: {
|
|
406
|
+
requestor_did: 'did:decentrl:alice',
|
|
407
|
+
recipient_did: 'did:decentrl:bob',
|
|
408
|
+
requestor_signing_key_id: 'did:decentrl:alice#signing',
|
|
409
|
+
recipient_signing_key_id: 'did:decentrl:bob#signing',
|
|
410
|
+
requestor_encryption_public_key: 'key-a',
|
|
411
|
+
recipient_encryption_public_key: 'key-b',
|
|
412
|
+
expires_at: now + 3600,
|
|
413
|
+
timestamp: now,
|
|
414
|
+
},
|
|
415
|
+
requestor_signature: 'sig-a',
|
|
416
|
+
recipient_signature: 'sig-b',
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
}));
|
|
423
|
+
|
|
424
|
+
const transport = new DirectTransport({ httpPost });
|
|
425
|
+
|
|
426
|
+
(transport as any).identity = {
|
|
427
|
+
did: 'did:decentrl:alice',
|
|
428
|
+
mediatorDid: 'did:web:mediator',
|
|
429
|
+
mediatorEndpoint: 'http://mediator',
|
|
430
|
+
keys: {
|
|
431
|
+
signing: {
|
|
432
|
+
privateKey: new Uint8Array(32),
|
|
433
|
+
publicKey: new Uint8Array(32),
|
|
434
|
+
},
|
|
435
|
+
encryption: {
|
|
436
|
+
privateKey: new Uint8Array(32),
|
|
437
|
+
publicKey: new Uint8Array(32),
|
|
438
|
+
},
|
|
439
|
+
storageKey: new Uint8Array(32),
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// Refresh to get the contract
|
|
444
|
+
const contracts = await transport.refreshContracts();
|
|
445
|
+
expect(contracts).toHaveLength(1);
|
|
446
|
+
|
|
447
|
+
const contractId = contracts[0].id;
|
|
448
|
+
|
|
449
|
+
// Add to purged set
|
|
450
|
+
(transport as any).purgedContractIds.add(contractId);
|
|
451
|
+
|
|
452
|
+
// Refresh again — purged contract should be filtered out
|
|
453
|
+
await transport.refreshContracts();
|
|
454
|
+
expect(transport.getActiveContracts()).toHaveLength(0);
|
|
455
|
+
|
|
456
|
+
// purgedContractIds should be cleared after refresh
|
|
457
|
+
expect((transport as any).purgedContractIds.size).toBe(0);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
});
|