@pi-unipi/background-tasks 2.6.3 → 2.9.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.
Files changed (58) hide show
  1. package/package.json +6 -5
  2. package/src/__tests__/anthropic-attribution.test.ts +0 -195
  3. package/src/__tests__/config.test.ts +0 -137
  4. package/src/__tests__/core.test.ts +0 -493
  5. package/src/__tests__/delegate-artifacts.test.ts +0 -528
  6. package/src/__tests__/delegate-budget.test.ts +0 -456
  7. package/src/__tests__/delegate-launch.test.ts +0 -676
  8. package/src/__tests__/delegate-result-package.test.ts +0 -350
  9. package/src/__tests__/delegate-seed.test.ts +0 -392
  10. package/src/__tests__/durable-fs.test.ts +0 -559
  11. package/src/__tests__/extension-api.test.ts +0 -579
  12. package/src/__tests__/fusion-artifacts.test.ts +0 -1039
  13. package/src/__tests__/fusion-budget.test.ts +0 -1356
  14. package/src/__tests__/fusion-claude-cache.test.ts +0 -320
  15. package/src/__tests__/fusion-config.test.ts +0 -335
  16. package/src/__tests__/fusion-context-prompts.test.ts +0 -670
  17. package/src/__tests__/fusion-evaluation.test.ts +0 -315
  18. package/src/__tests__/fusion-extraction-equivalence.test.ts +0 -58
  19. package/src/__tests__/fusion-golden-bytes.test.ts +0 -35
  20. package/src/__tests__/fusion-high-cardinality.test.ts +0 -192
  21. package/src/__tests__/fusion-model-selector.test.ts +0 -205
  22. package/src/__tests__/fusion-orchestrator.test.ts +0 -1194
  23. package/src/__tests__/fusion-rpc.test.ts +0 -369
  24. package/src/__tests__/fusion-sdk.test.ts +0 -1226
  25. package/src/__tests__/fusion-v5-core.test.ts +0 -219
  26. package/src/__tests__/fusion-validate-orchestrator.test.ts +0 -240
  27. package/src/__tests__/fusion-web-fetch.test.ts +0 -485
  28. package/src/__tests__/fusion-workflows.test.ts +0 -59
  29. package/src/__tests__/helpers/delegate-deterministic-seed.ts +0 -109
  30. package/src/__tests__/helpers/delegate-seed-subprocess.ts +0 -10
  31. package/src/__tests__/helpers/fusion-canonical-subprocess.ts +0 -21
  32. package/src/__tests__/helpers/fusion-canonical.ts +0 -140
  33. package/src/__tests__/helpers/fusion-fake-pi.ts +0 -279
  34. package/src/__tests__/helpers/fusion-golden-corpus.ts +0 -500
  35. package/src/__tests__/helpers/fusion-high-cardinality.ts +0 -140
  36. package/src/__tests__/helpers/normalize.ts +0 -22
  37. package/src/__tests__/helpers/pi-hook-contract-evidence.json +0 -18
  38. package/src/__tests__/pi-launch.test.ts +0 -202
  39. package/src/__tests__/registry.test.ts +0 -1580
  40. package/src/__tests__/scripted-provider/delegate-ambient-provider.test.ts +0 -130
  41. package/src/__tests__/scripted-provider/delegate-child-guard.test.ts +0 -631
  42. package/src/__tests__/scripted-provider/delegate-guard-provider.ts +0 -403
  43. package/src/__tests__/scripted-provider/follow-up.test.ts +0 -448
  44. package/src/__tests__/scripted-provider/fusion-output-recovery.test.ts +0 -132
  45. package/src/__tests__/scripted-provider/fusion-reason.test.ts +0 -310
  46. package/src/__tests__/scripted-provider/fusion-runtime-guard.test.ts +0 -163
  47. package/src/__tests__/scripted-provider/hook-contract-provider.ts +0 -179
  48. package/src/__tests__/scripted-provider/hook-probe-a.ts +0 -3
  49. package/src/__tests__/scripted-provider/hook-probe-b.ts +0 -3
  50. package/src/__tests__/scripted-provider/hook-probe-extension.ts +0 -126
  51. package/src/__tests__/scripted-provider/output-recovery-provider.ts +0 -153
  52. package/src/__tests__/scripted-provider/pi-hook-contract-evidence.json +0 -18
  53. package/src/__tests__/scripted-provider/pi-hook-contract.test.ts +0 -477
  54. package/src/__tests__/scripted-provider/runtime-guard-probe.ts +0 -28
  55. package/src/__tests__/scripted-provider/runtime-guard-provider.ts +0 -49
  56. package/src/__tests__/scripted-provider/scripted-provider-extension.ts +0 -408
  57. package/src/__tests__/task-manager.test.ts +0 -479
  58. package/src/__tests__/windows-taskkill.test.ts +0 -161
@@ -1,559 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
4
- import { tmpdir } from 'node:os';
5
- import { dirname, join } from 'node:path';
6
- import {
7
- createDurableFileWriter,
8
- DurableFileError,
9
- replaceFileDurable,
10
- writeFileDurable,
11
- } from '../durable-fs.js';
12
- import type {
13
- DurableData,
14
- DurableDirectoryHandle,
15
- DurableFailure,
16
- DurableFileOperations,
17
- DurableOperation,
18
- DurableWritableHandle,
19
- DurableWriteFlag,
20
- } from '../durable-fs.js';
21
-
22
- interface OpenWritableCall {
23
- kind: 'openWritable';
24
- path: string;
25
- flag: string;
26
- mode: number | undefined;
27
- }
28
-
29
- interface WriteFileCall {
30
- kind: 'writeFile';
31
- path: string;
32
- data: DurableData;
33
- }
34
-
35
- interface PathCall {
36
- kind: 'syncFile' | 'closeFile' | 'remove' | 'openDirectory' | 'syncDirectory' | 'closeDirectory';
37
- path: string;
38
- }
39
-
40
- interface RenameCall {
41
- kind: 'rename';
42
- source: string;
43
- target: string;
44
- }
45
-
46
- type RecordedCall = OpenWritableCall | WriteFileCall | PathCall | RenameCall;
47
-
48
- interface RecordingOptions {
49
- readonly platform?: NodeJS.Platform;
50
- readonly temporaryPath?: string;
51
- }
52
-
53
- function codedError(message: string, code: string): Error {
54
- const error = new Error(message);
55
- Object.defineProperty(error, 'code', { value: code, enumerable: true });
56
- return error;
57
- }
58
-
59
- class RecordingDurableFileOperations implements DurableFileOperations {
60
- readonly platform: NodeJS.Platform;
61
- readonly calls: RecordedCall[] = [];
62
- private readonly failures = new Map<DurableOperation, Error>();
63
- private readonly existingPaths = new Set<string>();
64
- private readonly tempPath: string;
65
-
66
- constructor(options: RecordingOptions = {}) {
67
- this.platform = options.platform ?? 'linux';
68
- this.tempPath = options.temporaryPath ?? '/virtual/.target.tmp';
69
- }
70
-
71
- fail(operation: DurableOperation, error: Error = new Error(`${operation} failed`)): Error {
72
- this.failures.set(operation, error);
73
- return error;
74
- }
75
-
76
- failureFor(operation: DurableOperation): Error | undefined {
77
- return this.failures.get(operation);
78
- }
79
-
80
- addExisting(path: string): void {
81
- this.existingPaths.add(path);
82
- }
83
-
84
- hasPath(path: string): boolean {
85
- return this.existingPaths.has(path);
86
- }
87
-
88
- async openWritable(
89
- path: string,
90
- flag: DurableWriteFlag,
91
- mode?: number,
92
- ): Promise<DurableWritableHandle> {
93
- this.calls.push({ kind: 'openWritable', path, flag, mode });
94
- const configured = this.failureFor('open_file');
95
- if (configured !== undefined) throw configured;
96
- if (flag === 'wx' && this.existingPaths.has(path)) throw codedError('path exists', 'EEXIST');
97
- this.existingPaths.add(path);
98
- return new RecordingWritableHandle(this, path);
99
- }
100
-
101
- async openDirectory(path: string): Promise<DurableDirectoryHandle> {
102
- this.calls.push({ kind: 'openDirectory', path });
103
- const configured = this.failureFor('open_directory');
104
- if (configured !== undefined) throw configured;
105
- return new RecordingDirectoryHandle(this, path);
106
- }
107
-
108
- async rename(source: string, target: string): Promise<void> {
109
- this.calls.push({ kind: 'rename', source, target });
110
- const configured = this.failureFor('rename_file');
111
- if (configured !== undefined) throw configured;
112
- if (this.existingPaths.has(source)) {
113
- this.existingPaths.delete(source);
114
- this.existingPaths.add(target);
115
- }
116
- }
117
-
118
- async remove(path: string): Promise<void> {
119
- this.calls.push({ kind: 'remove', path });
120
- const configured = this.failureFor('remove_temp');
121
- if (configured !== undefined) throw configured;
122
- this.existingPaths.delete(path);
123
- }
124
-
125
- temporaryPath(_target: string): string {
126
- return this.tempPath;
127
- }
128
- }
129
-
130
- class RecordingWritableHandle implements DurableWritableHandle {
131
- constructor(
132
- private readonly operations: RecordingDurableFileOperations,
133
- private readonly path: string,
134
- ) {}
135
-
136
- async writeFile(data: DurableData): Promise<void> {
137
- this.operations.calls.push({ kind: 'writeFile', path: this.path, data });
138
- const configured = this.operations.failureFor('write_file');
139
- if (configured !== undefined) throw configured;
140
- }
141
-
142
- async sync(): Promise<void> {
143
- this.operations.calls.push({ kind: 'syncFile', path: this.path });
144
- const configured = this.operations.failureFor('sync_file');
145
- if (configured !== undefined) throw configured;
146
- }
147
-
148
- async close(): Promise<void> {
149
- this.operations.calls.push({ kind: 'closeFile', path: this.path });
150
- const configured = this.operations.failureFor('close_file');
151
- if (configured !== undefined) throw configured;
152
- }
153
- }
154
-
155
- class RecordingDirectoryHandle implements DurableDirectoryHandle {
156
- constructor(
157
- private readonly operations: RecordingDurableFileOperations,
158
- private readonly path: string,
159
- ) {}
160
-
161
- async sync(): Promise<void> {
162
- this.operations.calls.push({ kind: 'syncDirectory', path: this.path });
163
- const configured = this.operations.failureFor('sync_directory');
164
- if (configured !== undefined) throw configured;
165
- }
166
-
167
- async close(): Promise<void> {
168
- this.operations.calls.push({ kind: 'closeDirectory', path: this.path });
169
- const configured = this.operations.failureFor('close_directory');
170
- if (configured !== undefined) throw configured;
171
- }
172
- }
173
-
174
- function callsNamed<K extends RecordedCall['kind']>(
175
- calls: readonly RecordedCall[],
176
- kind: K,
177
- ): Array<RecordedCall & { kind: K }> {
178
- return calls.filter((call): call is RecordedCall & { kind: K } => call.kind === kind);
179
- }
180
-
181
- function single<T>(items: readonly T[], label: string): T {
182
- assert.equal(items.length, 1, label);
183
- const item = items[0];
184
- assert.ok(item !== undefined, label);
185
- return item;
186
- }
187
-
188
- function callKinds(calls: readonly RecordedCall[]): string[] {
189
- return calls.map((call) => call.kind);
190
- }
191
-
192
- function cleanupOperations(error: DurableFileError): DurableOperation[] {
193
- return error.cleanupFailures.map((entry: DurableFailure) => entry.operation);
194
- }
195
-
196
- async function durableRejects(promise: Promise<void>): Promise<DurableFileError> {
197
- try {
198
- await promise;
199
- } catch (error) {
200
- assert.ok(error instanceof DurableFileError, error instanceof Error ? error.stack : String(error));
201
- return error;
202
- }
203
- assert.fail('expected DurableFileError');
204
- }
205
-
206
- async function withTempDir<T>(prefix: string, fn: (dir: string) => Promise<T>): Promise<T> {
207
- const dir = await mkdtemp(join(tmpdir(), prefix));
208
- try {
209
- return await fn(dir);
210
- } finally {
211
- await rm(dir, { recursive: true, force: true });
212
- }
213
- }
214
-
215
- function posixMode(mode: number): number {
216
- return mode & 0o777;
217
- }
218
-
219
- void describe('durable file writer operation sequencing', () => {
220
- void it('direct write opens exactly once with write flag and inherited mode', async () => {
221
- const ops = new RecordingDurableFileOperations();
222
- const writer = createDurableFileWriter(ops);
223
-
224
- await writer.write('/virtual/data.txt', 'data');
225
-
226
- const open = single(callsNamed(ops.calls, 'openWritable'), 'direct write open count');
227
- assert.equal(open.path, '/virtual/data.txt');
228
- assert.equal(open.flag, 'w');
229
- assert.equal(open.mode, undefined);
230
- });
231
-
232
- void it('atomic replace opens the temp exactly once with exclusive create mode', async () => {
233
- const temp = '/virtual/.target.txt.tmp';
234
- const ops = new RecordingDurableFileOperations({ temporaryPath: temp });
235
- const writer = createDurableFileWriter(ops);
236
-
237
- await writer.replace('/virtual/target.txt', 'data');
238
-
239
- const tempOpens = callsNamed(ops.calls, 'openWritable').filter((call) => call.path === temp);
240
- const open = single(tempOpens, 'atomic temp open count');
241
- assert.equal(open.flag, 'wx');
242
- assert.equal(open.mode, 0o600);
243
- });
244
-
245
- void it('atomic replace records write, file sync, close, rename, and directory sync in order', async () => {
246
- const ops = new RecordingDurableFileOperations({ temporaryPath: '/virtual/.target.tmp' });
247
- const writer = createDurableFileWriter(ops);
248
-
249
- await writer.replace('/virtual/target.txt', 'data');
250
-
251
- assert.deepEqual(callKinds(ops.calls), [
252
- 'openWritable',
253
- 'writeFile',
254
- 'syncFile',
255
- 'closeFile',
256
- 'rename',
257
- 'openDirectory',
258
- 'syncDirectory',
259
- 'closeDirectory',
260
- ]);
261
- });
262
-
263
- void it('does not reopen the data file after writing or use read flags for sync', async () => {
264
- const path = '/virtual/data.txt';
265
- const ops = new RecordingDurableFileOperations();
266
- const writer = createDurableFileWriter(ops);
267
-
268
- await writer.write(path, 'data');
269
-
270
- const opens = callsNamed(ops.calls, 'openWritable');
271
- const dataOpens = opens.filter((call) => call.path === path);
272
- assert.equal(dataOpens.length, 1);
273
- assert.ok(!opens.some((call) => call.flag === 'r' || call.flag === 'r+'));
274
- const writeIndex = ops.calls.findIndex((call) => call.kind === 'writeFile' && call.path === path);
275
- assert.ok(writeIndex >= 0);
276
- const laterDataOpens = ops.calls
277
- .slice(writeIndex + 1)
278
- .filter((call): call is OpenWritableCall => call.kind === 'openWritable' && call.path === path);
279
- assert.equal(laterDataOpens.length, 0);
280
- });
281
-
282
- void it('write flag overwrites existing content on the real filesystem', async () => {
283
- await withTempDir('unipi-bg-durable-write-', async (dir) => {
284
- const file = join(dir, 'data.txt');
285
- await writeFile(file, 'old');
286
-
287
- await writeFileDurable(file, 'new');
288
-
289
- assert.equal(await readFile(file, 'utf8'), 'new');
290
- });
291
- });
292
-
293
- void it('exclusive temp collision fails without removing the pre-existing temp path', async () => {
294
- const temp = '/virtual/.target.tmp';
295
- const ops = new RecordingDurableFileOperations({ temporaryPath: temp });
296
- ops.addExisting(temp);
297
- const writer = createDurableFileWriter(ops);
298
-
299
- const error = await durableRejects(writer.replace('/virtual/target.txt', 'data'));
300
-
301
- assert.equal(error.operation, 'open_file');
302
- assert.equal(error.path, temp);
303
- assert.equal(error.nativeCode, 'EEXIST');
304
- assert.equal(ops.hasPath(temp), true);
305
- assert.equal(callsNamed(ops.calls, 'remove').length, 0);
306
- });
307
-
308
- void it('write failure still closes the handle', async () => {
309
- const ops = new RecordingDurableFileOperations();
310
- ops.fail('write_file');
311
- const writer = createDurableFileWriter(ops);
312
-
313
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
314
-
315
- assert.equal(error.operation, 'write_file');
316
- assert.deepEqual(callKinds(ops.calls), ['openWritable', 'writeFile', 'closeFile']);
317
- });
318
-
319
- void it('sync failure still closes the handle', async () => {
320
- const ops = new RecordingDurableFileOperations();
321
- ops.fail('sync_file');
322
- const writer = createDurableFileWriter(ops);
323
-
324
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
325
-
326
- assert.equal(error.operation, 'sync_file');
327
- assert.deepEqual(callKinds(ops.calls), ['openWritable', 'writeFile', 'syncFile', 'closeFile']);
328
- });
329
-
330
- void it('successful write followed by close failure reports close as the primary operation', async () => {
331
- const ops = new RecordingDurableFileOperations();
332
- ops.fail('close_file');
333
- const writer = createDurableFileWriter(ops);
334
-
335
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
336
-
337
- assert.equal(error.operation, 'close_file');
338
- assert.equal(error.path, '/virtual/data.txt');
339
- assert.deepEqual(cleanupOperations(error), []);
340
- });
341
-
342
- void it('write failure plus close failure keeps write primary and records close cleanup', async () => {
343
- const ops = new RecordingDurableFileOperations();
344
- ops.fail('write_file');
345
- ops.fail('close_file');
346
- const writer = createDurableFileWriter(ops);
347
-
348
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
349
-
350
- assert.equal(error.operation, 'write_file');
351
- assert.deepEqual(cleanupOperations(error), ['close_file']);
352
- });
353
-
354
- void it('sync failure plus close failure keeps sync primary and records close cleanup', async () => {
355
- const ops = new RecordingDurableFileOperations();
356
- ops.fail('sync_file');
357
- ops.fail('close_file');
358
- const writer = createDurableFileWriter(ops);
359
-
360
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
361
-
362
- assert.equal(error.operation, 'sync_file');
363
- assert.deepEqual(cleanupOperations(error), ['close_file']);
364
- });
365
-
366
- void it('temp write, sync, and close failures remove the owned temp path', async () => {
367
- for (const operation of ['write_file', 'sync_file', 'close_file'] as const) {
368
- const temp = `/virtual/.target.${operation}.tmp`;
369
- const ops = new RecordingDurableFileOperations({ temporaryPath: temp });
370
- ops.fail(operation);
371
- const writer = createDurableFileWriter(ops);
372
-
373
- const error = await durableRejects(writer.replace('/virtual/target.txt', 'data'));
374
-
375
- assert.equal(error.operation, operation);
376
- assert.equal(ops.hasPath(temp), false);
377
- assert.equal(single(callsNamed(ops.calls, 'remove'), `remove call for ${operation}`).path, temp);
378
- }
379
- });
380
-
381
- void it('temp cleanup failure is attached without replacing the primary operation', async () => {
382
- const temp = '/virtual/.target.tmp';
383
- const ops = new RecordingDurableFileOperations({ temporaryPath: temp });
384
- ops.fail('write_file');
385
- ops.fail('remove_temp');
386
- const writer = createDurableFileWriter(ops);
387
-
388
- const error = await durableRejects(writer.replace('/virtual/target.txt', 'data'));
389
-
390
- assert.equal(error.operation, 'write_file');
391
- assert.deepEqual(cleanupOperations(error), ['remove_temp']);
392
- assert.equal(ops.hasPath(temp), true);
393
- });
394
-
395
- void it('rename failure removes the owned temp path', async () => {
396
- const temp = '/virtual/.target.tmp';
397
- const ops = new RecordingDurableFileOperations({ temporaryPath: temp });
398
- ops.fail('rename_file');
399
- const writer = createDurableFileWriter(ops);
400
-
401
- const error = await durableRejects(writer.replace('/virtual/target.txt', 'data'));
402
-
403
- assert.equal(error.operation, 'rename_file');
404
- assert.equal(ops.hasPath(temp), false);
405
- assert.equal(single(callsNamed(ops.calls, 'remove'), 'rename cleanup call').path, temp);
406
- });
407
-
408
- void it('rename failure plus cleanup failure preserves both failures', async () => {
409
- const temp = '/virtual/.target.tmp';
410
- const ops = new RecordingDurableFileOperations({ temporaryPath: temp });
411
- ops.fail('rename_file');
412
- ops.fail('remove_temp');
413
- const writer = createDurableFileWriter(ops);
414
-
415
- const error = await durableRejects(writer.replace('/virtual/target.txt', 'data'));
416
-
417
- assert.equal(error.operation, 'rename_file');
418
- assert.deepEqual(cleanupOperations(error), ['remove_temp']);
419
- assert.equal(ops.hasPath(temp), true);
420
- });
421
-
422
- void it('directory open, directory sync, and directory close failures are loud', async () => {
423
- const target = '/virtual/target.txt';
424
- for (const operation of ['open_directory', 'sync_directory', 'close_directory'] as const) {
425
- const ops = new RecordingDurableFileOperations({ temporaryPath: `/virtual/.${operation}.tmp` });
426
- ops.fail(operation);
427
- const writer = createDurableFileWriter(ops);
428
-
429
- const error = await durableRejects(writer.replace(target, 'data'));
430
-
431
- assert.equal(error.operation, operation);
432
- assert.equal(error.path, dirname(target));
433
- }
434
- });
435
-
436
- void it('directory sync failure plus directory close failure preserves both failures', async () => {
437
- const target = '/virtual/target.txt';
438
- const ops = new RecordingDurableFileOperations({ temporaryPath: '/virtual/.target.tmp' });
439
- ops.fail('sync_directory');
440
- ops.fail('close_directory');
441
- const writer = createDurableFileWriter(ops);
442
-
443
- const error = await durableRejects(writer.replace(target, 'data'));
444
-
445
- assert.equal(error.operation, 'sync_directory');
446
- assert.deepEqual(cleanupOperations(error), ['close_directory']);
447
- });
448
-
449
- void it('post-rename directory failure marks the replacement as completed', async () => {
450
- const ops = new RecordingDurableFileOperations({ temporaryPath: '/virtual/.target.tmp' });
451
- ops.fail('open_directory');
452
- const writer = createDurableFileWriter(ops);
453
-
454
- const error = await durableRejects(writer.replace('/virtual/target.txt', 'data'));
455
-
456
- assert.equal(error.operation, 'open_directory');
457
- assert.equal(error.renameCompleted, true);
458
- });
459
-
460
- void it('win32 replacement skips directory opens', async () => {
461
- const ops = new RecordingDurableFileOperations({
462
- platform: 'win32',
463
- temporaryPath: 'C:\\virtual\\.target.tmp',
464
- });
465
- ops.fail('open_directory');
466
- const writer = createDurableFileWriter(ops);
467
-
468
- await writer.replace('C:\\virtual\\target.txt', 'data');
469
-
470
- assert.equal(callsNamed(ops.calls, 'openDirectory').length, 0);
471
- assert.deepEqual(callKinds(ops.calls), ['openWritable', 'writeFile', 'syncFile', 'closeFile', 'rename']);
472
- });
473
-
474
- void it('successful atomic replacement leaves no temp residue on the real filesystem', async () => {
475
- await withTempDir('unipi-bg-durable-replace-clean-', async (dir) => {
476
- const target = join(dir, 'target.txt');
477
-
478
- await replaceFileDurable(target, 'new');
479
-
480
- assert.equal(await readFile(target, 'utf8'), 'new');
481
- assert.deepEqual((await readdir(dir)).sort(), ['target.txt']);
482
- });
483
- });
484
-
485
- void it('DurableFileError exposes operation, path, native code, and primary cause', async () => {
486
- const ops = new RecordingDurableFileOperations();
487
- const native = codedError('denied', 'EACCES');
488
- ops.fail('open_file', native);
489
- const writer = createDurableFileWriter(ops);
490
-
491
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
492
-
493
- assert.equal(error.operation, 'open_file');
494
- assert.equal(error.path, '/virtual/data.txt');
495
- assert.equal(error.nativeCode, 'EACCES');
496
- assert.equal(error.primaryCause, native);
497
- });
498
-
499
- void it('file sync failure is never tolerated', async () => {
500
- const ops = new RecordingDurableFileOperations();
501
- ops.fail('sync_file');
502
- const writer = createDurableFileWriter(ops);
503
-
504
- const error = await durableRejects(writer.write('/virtual/data.txt', 'data'));
505
-
506
- assert.equal(error.operation, 'sync_file');
507
- });
508
- });
509
-
510
- void describe('durable file writer real filesystem integration', () => {
511
- void it('writeFileDurable overwrites content with writeFile mode parity', async () => {
512
- await withTempDir('unipi-bg-durable-mode-', async (dir) => {
513
- const durablePath = join(dir, 'durable.txt');
514
- const referencePath = join(dir, 'reference.txt');
515
-
516
- await writeFileDurable(durablePath, 'first');
517
- await writeFileDurable(durablePath, 'second');
518
- await writeFile(referencePath, 'reference');
519
-
520
- assert.equal(await readFile(durablePath, 'utf8'), 'second');
521
- if (process.platform !== 'win32') {
522
- const durableMode = posixMode((await stat(durablePath)).mode);
523
- const referenceMode = posixMode((await stat(referencePath)).mode);
524
- const expectedMode = 0o666 & ~process.umask();
525
- assert.equal(durableMode, referenceMode);
526
- assert.equal(durableMode, expectedMode);
527
- }
528
- });
529
- });
530
-
531
- void it('replaceFileDurable installs a private complete replacement', async () => {
532
- await withTempDir('unipi-bg-durable-private-', async (dir) => {
533
- const target = join(dir, 'target.txt');
534
- await writeFile(target, 'old');
535
-
536
- await replaceFileDurable(target, 'new');
537
-
538
- assert.equal(await readFile(target, 'utf8'), 'new');
539
- if (process.platform !== 'win32') assert.equal(posixMode((await stat(target)).mode), 0o600);
540
- });
541
- });
542
-
543
- void it('concurrent replaceFileDurable calls leave one complete payload', async () => {
544
- await withTempDir('unipi-bg-durable-concurrent-', async (dir) => {
545
- const target = join(dir, 'target.json');
546
- const payloads = Array.from({ length: 12 }, (_value, index) =>
547
- `${JSON.stringify({ index, marker: `payload-${String(index)}`, data: 'x'.repeat(8192) })}\n`,
548
- );
549
-
550
- await Promise.all(payloads.map((payload) => replaceFileDurable(target, payload)));
551
-
552
- const finalPayload = await readFile(target, 'utf8');
553
- assert.ok(payloads.includes(finalPayload));
554
- assert.doesNotThrow(() => JSON.parse(finalPayload));
555
- if (process.platform !== 'win32') assert.equal(posixMode((await stat(target)).mode), 0o600);
556
- });
557
- });
558
-
559
- });