Skip to content

Commit abe6d08

Browse files
committed
vfs: improve test coverage for watch implementation
- Add tests for async iterator return() and throw() methods - Add test for pending events buffered before next() is called - Add test for close while iteration is pending - Add test for VFSStatWatcher ref() and unref() methods - Remove unreachable error handling code in VFSWatchAsyncIterable (VFSWatcher never emits 'error' events) This brings watcher.js coverage above the 95% threshold.
1 parent 826d4ae commit abe6d08

File tree

2 files changed

+122
-14
lines changed

2 files changed

+122
-14
lines changed

lib/internal/vfs/watcher.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,6 @@ class VFSWatchAsyncIterable {
458458
#closed = false;
459459
#pendingEvents = [];
460460
#pendingResolvers = [];
461-
#error = null;
462461

463462
/**
464463
* @param {VirtualProvider} provider The VFS provider
@@ -486,15 +485,6 @@ class VFSWatchAsyncIterable {
486485
resolve({ value: undefined, done: true });
487486
}
488487
});
489-
490-
this.#watcher.on('error', (err) => {
491-
this.#error = err;
492-
// Reject any pending iterators
493-
while (this.#pendingResolvers.length > 0) {
494-
const resolve = this.#pendingResolvers.shift();
495-
resolve(PromiseResolve({ value: undefined, done: true }));
496-
}
497-
});
498488
}
499489

500490
/**
@@ -510,10 +500,6 @@ class VFSWatchAsyncIterable {
510500
* @returns {Promise<IteratorResult>}
511501
*/
512502
next() {
513-
if (this.#error) {
514-
return PromiseResolve({ value: undefined, done: true });
515-
}
516-
517503
if (this.#closed) {
518504
return PromiseResolve({ value: undefined, done: true });
519505
}

test/parallel/test-vfs-watch.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,125 @@ const vfs = require('node:vfs');
373373
myVfs.addFile('/data/subdir/nested.txt', 'updated nested');
374374
}, 100);
375375
}
376+
377+
// Test async iterator return() method
378+
{
379+
const myVfs = vfs.create();
380+
myVfs.writeFileSync('/return-test.txt', 'initial');
381+
382+
(async () => {
383+
const watcher = myVfs.promises.watch('/return-test.txt', { persistent: false });
384+
385+
// Call return() to close the iterator early
386+
const result = await watcher.return();
387+
assert.strictEqual(result.done, true);
388+
assert.strictEqual(result.value, undefined);
389+
})().then(common.mustCall());
390+
}
391+
392+
// Test async iterator throw() method
393+
{
394+
const myVfs = vfs.create();
395+
myVfs.writeFileSync('/throw-test.txt', 'initial');
396+
397+
(async () => {
398+
const watcher = myVfs.promises.watch('/throw-test.txt', { persistent: false });
399+
400+
// Call throw() to close the iterator with an error
401+
const result = await watcher.throw(new Error('test error'));
402+
assert.strictEqual(result.done, true);
403+
assert.strictEqual(result.value, undefined);
404+
})().then(common.mustCall());
405+
}
406+
407+
// Test pending events (events buffered before next() is called)
408+
{
409+
const myVfs = vfs.create();
410+
myVfs.writeFileSync('/pending-test.txt', 'initial');
411+
412+
(async () => {
413+
const watcher = myVfs.promises.watch('/pending-test.txt', {
414+
interval: 20,
415+
persistent: false,
416+
});
417+
418+
// Trigger a change and wait for it to be buffered
419+
setTimeout(() => {
420+
myVfs.writeFileSync('/pending-test.txt', 'updated');
421+
}, 50);
422+
423+
// Wait a bit longer than the poll interval to ensure event is buffered
424+
await new Promise((resolve) => setTimeout(resolve, 100));
425+
426+
// Now iterate - the event should already be pending
427+
let eventCount = 0;
428+
const ac = new AbortController();
429+
setTimeout(() => ac.abort(), 200);
430+
431+
try {
432+
for await (const event of watcher) {
433+
assert.ok(event.eventType);
434+
eventCount++;
435+
break; // Exit after first event
436+
}
437+
} catch {
438+
// Ignore abort errors
439+
}
440+
assert.strictEqual(eventCount, 1);
441+
await watcher.return();
442+
})().then(common.mustCall());
443+
}
444+
445+
// Test close while iteration is pending (waiting for next event)
446+
{
447+
const myVfs = vfs.create();
448+
myVfs.writeFileSync('/close-pending.txt', 'initial');
449+
450+
(async () => {
451+
const watcher = myVfs.promises.watch('/close-pending.txt', {
452+
interval: 50,
453+
persistent: false,
454+
});
455+
456+
// Start iterating in background (this will wait for events)
457+
const iterPromise = (async () => {
458+
const events = [];
459+
for await (const event of watcher) {
460+
events.push(event);
461+
}
462+
return events;
463+
})();
464+
465+
// Close the watcher while iteration is waiting
466+
await new Promise((resolve) => setTimeout(resolve, 30));
467+
await watcher.return();
468+
469+
// The iteration should complete with no events
470+
const events = await iterPromise;
471+
assert.strictEqual(events.length, 0);
472+
})().then(common.mustCall());
473+
}
474+
475+
// Test VFSStatWatcher ref() and unref() methods
476+
{
477+
const myVfs = vfs.create();
478+
myVfs.writeFileSync('/stat-ref-test.txt', 'content');
479+
480+
const listener = common.mustNotCall();
481+
const statWatcher = myVfs.watchFile(
482+
'/stat-ref-test.txt',
483+
{ interval: 50, persistent: false },
484+
listener,
485+
);
486+
487+
// Test unref() returns this
488+
const unrefResult = statWatcher.unref();
489+
assert.strictEqual(unrefResult, statWatcher);
490+
491+
// Test ref() returns this
492+
const refResult = statWatcher.ref();
493+
assert.strictEqual(refResult, statWatcher);
494+
495+
// Clean up
496+
myVfs.unwatchFile('/stat-ref-test.txt', listener);
497+
}

0 commit comments

Comments
 (0)