diff --git a/lib/mock/snapshot-agent.js b/lib/mock/snapshot-agent.js index 362745c414c..90bc3e0a50d 100644 --- a/lib/mock/snapshot-agent.js +++ b/lib/mock/snapshot-agent.js @@ -354,7 +354,15 @@ class SnapshotAgent extends MockAgent { * @returns {Promise} */ async close () { - await this[kSnapshotRecorder].close() + // In playback mode the recorder must not persist to disk. findSnapshot() + // mutates each matched snapshot's callCount, so saving on close would + // rewrite the snapshot file even though nothing new was recorded. Only + // record/update modes should write snapshots; playback just cleans up. + if (this[kSnapshotMode] === 'playback') { + this[kSnapshotRecorder].destroy() + } else { + await this[kSnapshotRecorder].close() + } await this[kRealAgent]?.close() await super.close() } diff --git a/test/snapshot-testing.js b/test/snapshot-testing.js index ab805792dc2..082ee07a090 100644 --- a/test/snapshot-testing.js +++ b/test/snapshot-testing.js @@ -1560,6 +1560,55 @@ describe('SnapshotAgent - Close Method', () => { }, 'Should not throw when closing agent without snapshot path') }) + it('close() does not rewrite the snapshot file in playback mode', async (t) => { + const snapshotPath = createSnapshotPath('close-playback-no-rewrite') + setupCleanup(t, { snapshotPath }) + + const server = createTestServer(createDefaultHandler()) + const { origin } = await setupServer(server) + setupCleanup(t, { server }) + + const originalDispatcher = getGlobalDispatcher() + setupCleanup(t, { originalDispatcher }) + + // Record a snapshot so there is a file on disk to play back. + const recordingAgent = new SnapshotAgent({ + mode: 'record', + snapshotPath, + autoFlush: false + }) + setGlobalDispatcher(recordingAgent) + await request(`${origin}/test`) + await recordingAgent.close() + + // Capture the exact bytes written by the recording session. + const recordedContent = await readFile(snapshotPath, 'utf8') + + // Play the snapshot back. Each matched request increments the snapshot's + // callCount in memory, so a save on close would change the file on disk. + const playbackAgent = new SnapshotAgent({ + mode: 'playback', + snapshotPath, + autoFlush: false + }) + setGlobalDispatcher(playbackAgent) + + await request(`${origin}/test`) + await request(`${origin}/test`) + await request(`${origin}/test`) + + await playbackAgent.close() + + // The file must be byte-for-byte identical: playback is a read-only path + // and must never rewrite the fixture (and thus never churn its callCount). + const contentAfterPlayback = await readFile(snapshotPath, 'utf8') + assert.strictEqual( + contentAfterPlayback, + recordedContent, + 'Playback close() should not modify the snapshot file on disk' + ) + }) + it('recorder close() method works independently', async (t) => { const { SnapshotRecorder } = require('../lib/mock/snapshot-recorder') const snapshotPath = createSnapshotPath('recorder-close')