feat: implement file system reversion utilities for rewind (#15715)

This commit is contained in:
Adib234
2026-01-08 17:21:15 -05:00
committed by GitHub
parent 9062a943e7
commit e5f7a9c424
6 changed files with 885 additions and 2 deletions
@@ -0,0 +1,104 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import {
getFileDiffFromResultDisplay,
computeAddedAndRemovedLines,
} from './fileDiffUtils.js';
import type { FileDiff, ToolResultDisplay } from '../tools/tools.js';
describe('fileDiffUtils', () => {
describe('getFileDiffFromResultDisplay', () => {
it('returns undefined if resultDisplay is undefined', () => {
expect(getFileDiffFromResultDisplay(undefined)).toBeUndefined();
});
it('returns undefined if resultDisplay is not an object', () => {
expect(
getFileDiffFromResultDisplay('string' as ToolResultDisplay),
).toBeUndefined();
});
it('returns undefined if resultDisplay missing diffStat', () => {
const resultDisplay = {
fileName: 'file.txt',
};
expect(
getFileDiffFromResultDisplay(resultDisplay as ToolResultDisplay),
).toBeUndefined();
});
it('returns the FileDiff object if structure is valid', () => {
const validDiffStat = {
model_added_lines: 1,
model_removed_lines: 2,
user_added_lines: 3,
user_removed_lines: 4,
model_added_chars: 10,
model_removed_chars: 20,
user_added_chars: 30,
user_removed_chars: 40,
};
const resultDisplay = {
fileName: 'file.txt',
diffStat: validDiffStat,
};
const result = getFileDiffFromResultDisplay(
resultDisplay as ToolResultDisplay,
);
expect(result).toBe(resultDisplay);
});
});
describe('computeAddedAndRemovedLines', () => {
it('returns 0 added and 0 removed if stats is undefined', () => {
expect(computeAddedAndRemovedLines(undefined)).toEqual({
addedLines: 0,
removedLines: 0,
});
});
it('correctly sums added and removed lines from stats', () => {
const stats: FileDiff['diffStat'] = {
model_added_lines: 10,
model_removed_lines: 5,
user_added_lines: 2,
user_removed_lines: 1,
model_added_chars: 100,
model_removed_chars: 50,
user_added_chars: 20,
user_removed_chars: 10,
};
const result = computeAddedAndRemovedLines(stats);
expect(result).toEqual({
addedLines: 12, // 10 + 2
removedLines: 6, // 5 + 1
});
});
it('handles zero values correctly', () => {
const stats: FileDiff['diffStat'] = {
model_added_lines: 0,
model_removed_lines: 0,
user_added_lines: 0,
user_removed_lines: 0,
model_added_chars: 0,
model_removed_chars: 0,
user_added_chars: 0,
user_removed_chars: 0,
};
const result = computeAddedAndRemovedLines(stats);
expect(result).toEqual({
addedLines: 0,
removedLines: 0,
});
});
});
});
+50
View File
@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type { FileDiff } from '../tools/tools.js';
import type { ToolCallRecord } from '../services/chatRecordingService.js';
/**
* Safely extracts the FileDiff object from a tool call's resultDisplay.
* This helper performs runtime checks to ensure the object conforms to the FileDiff structure.
* @param resultDisplay The resultDisplay property of a ToolCallRecord.
* @returns The FileDiff object if found and valid, otherwise undefined.
*/
export function getFileDiffFromResultDisplay(
resultDisplay: ToolCallRecord['resultDisplay'],
): FileDiff | undefined {
if (
resultDisplay &&
typeof resultDisplay === 'object' &&
'diffStat' in resultDisplay &&
typeof resultDisplay.diffStat === 'object' &&
resultDisplay.diffStat !== null
) {
const diffStat = resultDisplay.diffStat as FileDiff['diffStat'];
if (diffStat) {
return resultDisplay;
}
}
return undefined;
}
export function computeAddedAndRemovedLines(
stats: FileDiff['diffStat'] | undefined,
): {
addedLines: number;
removedLines: number;
} {
if (!stats) {
return {
addedLines: 0,
removedLines: 0,
};
}
return {
addedLines: stats.model_added_lines + stats.user_added_lines,
removedLines: stats.model_removed_lines + stats.user_removed_lines,
};
}