Skip to content

Commit 2f66db7

Browse files
fix(mock): do not persist snapshots on close in playback mode (#5359)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 9b1d58f commit 2f66db7

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

lib/mock/snapshot-agent.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,15 @@ class SnapshotAgent extends MockAgent {
354354
* @returns {Promise<void>}
355355
*/
356356
async close () {
357-
await this[kSnapshotRecorder].close()
357+
// In playback mode the recorder must not persist to disk. findSnapshot()
358+
// mutates each matched snapshot's callCount, so saving on close would
359+
// rewrite the snapshot file even though nothing new was recorded. Only
360+
// record/update modes should write snapshots; playback just cleans up.
361+
if (this[kSnapshotMode] === 'playback') {
362+
this[kSnapshotRecorder].destroy()
363+
} else {
364+
await this[kSnapshotRecorder].close()
365+
}
358366
await this[kRealAgent]?.close()
359367
await super.close()
360368
}

test/snapshot-testing.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,55 @@ describe('SnapshotAgent - Close Method', () => {
15601560
}, 'Should not throw when closing agent without snapshot path')
15611561
})
15621562

1563+
it('close() does not rewrite the snapshot file in playback mode', async (t) => {
1564+
const snapshotPath = createSnapshotPath('close-playback-no-rewrite')
1565+
setupCleanup(t, { snapshotPath })
1566+
1567+
const server = createTestServer(createDefaultHandler())
1568+
const { origin } = await setupServer(server)
1569+
setupCleanup(t, { server })
1570+
1571+
const originalDispatcher = getGlobalDispatcher()
1572+
setupCleanup(t, { originalDispatcher })
1573+
1574+
// Record a snapshot so there is a file on disk to play back.
1575+
const recordingAgent = new SnapshotAgent({
1576+
mode: 'record',
1577+
snapshotPath,
1578+
autoFlush: false
1579+
})
1580+
setGlobalDispatcher(recordingAgent)
1581+
await request(`${origin}/test`)
1582+
await recordingAgent.close()
1583+
1584+
// Capture the exact bytes written by the recording session.
1585+
const recordedContent = await readFile(snapshotPath, 'utf8')
1586+
1587+
// Play the snapshot back. Each matched request increments the snapshot's
1588+
// callCount in memory, so a save on close would change the file on disk.
1589+
const playbackAgent = new SnapshotAgent({
1590+
mode: 'playback',
1591+
snapshotPath,
1592+
autoFlush: false
1593+
})
1594+
setGlobalDispatcher(playbackAgent)
1595+
1596+
await request(`${origin}/test`)
1597+
await request(`${origin}/test`)
1598+
await request(`${origin}/test`)
1599+
1600+
await playbackAgent.close()
1601+
1602+
// The file must be byte-for-byte identical: playback is a read-only path
1603+
// and must never rewrite the fixture (and thus never churn its callCount).
1604+
const contentAfterPlayback = await readFile(snapshotPath, 'utf8')
1605+
assert.strictEqual(
1606+
contentAfterPlayback,
1607+
recordedContent,
1608+
'Playback close() should not modify the snapshot file on disk'
1609+
)
1610+
})
1611+
15631612
it('recorder close() method works independently', async (t) => {
15641613
const { SnapshotRecorder } = require('../lib/mock/snapshot-recorder')
15651614
const snapshotPath = createSnapshotPath('recorder-close')

0 commit comments

Comments
 (0)