fix(core): prevent race condition in policy persistence (#18506)

Co-authored-by: Allen Hutchison <adh@google.com>
This commit is contained in:
Brad Dux
2026-02-10 15:35:09 -08:00
committed by GitHub
parent be2ebd1772
commit 6d3fff2ea4
6 changed files with 256 additions and 86 deletions
+35 -12
View File
@@ -52,7 +52,12 @@ describe('createPolicyUpdater', () => {
(fs.readFile as unknown as Mock).mockRejectedValue(
new Error('File not found'),
); // Simulate new file
(fs.writeFile as unknown as Mock).mockResolvedValue(undefined);
const mockFileHandle = {
writeFile: vi.fn().mockResolvedValue(undefined),
close: vi.fn().mockResolvedValue(undefined),
};
(fs.open as unknown as Mock).mockResolvedValue(mockFileHandle);
(fs.rename as unknown as Mock).mockResolvedValue(undefined);
const toolName = 'test_tool';
@@ -70,10 +75,11 @@ describe('createPolicyUpdater', () => {
recursive: true,
});
expect(fs.open).toHaveBeenCalledWith(expect.stringMatching(/\.tmp$/), 'wx');
// Check written content
const expectedContent = expect.stringContaining(`toolName = "test_tool"`);
expect(fs.writeFile).toHaveBeenCalledWith(
expect.stringMatching(/\.tmp$/),
expect(mockFileHandle.writeFile).toHaveBeenCalledWith(
expectedContent,
'utf-8',
);
@@ -106,7 +112,12 @@ describe('createPolicyUpdater', () => {
(fs.readFile as unknown as Mock).mockRejectedValue(
new Error('File not found'),
);
(fs.writeFile as unknown as Mock).mockResolvedValue(undefined);
const mockFileHandle = {
writeFile: vi.fn().mockResolvedValue(undefined),
close: vi.fn().mockResolvedValue(undefined),
};
(fs.open as unknown as Mock).mockResolvedValue(mockFileHandle);
(fs.rename as unknown as Mock).mockResolvedValue(undefined);
const toolName = 'run_shell_command';
@@ -131,8 +142,8 @@ describe('createPolicyUpdater', () => {
);
// Verify file written
expect(fs.writeFile).toHaveBeenCalledWith(
expect.stringMatching(/\.tmp$/),
expect(fs.open).toHaveBeenCalledWith(expect.stringMatching(/\.tmp$/), 'wx');
expect(mockFileHandle.writeFile).toHaveBeenCalledWith(
expect.stringContaining(`commandPrefix = "git status"`),
'utf-8',
);
@@ -147,7 +158,12 @@ describe('createPolicyUpdater', () => {
(fs.readFile as unknown as Mock).mockRejectedValue(
new Error('File not found'),
);
(fs.writeFile as unknown as Mock).mockResolvedValue(undefined);
const mockFileHandle = {
writeFile: vi.fn().mockResolvedValue(undefined),
close: vi.fn().mockResolvedValue(undefined),
};
(fs.open as unknown as Mock).mockResolvedValue(mockFileHandle);
(fs.rename as unknown as Mock).mockResolvedValue(undefined);
const mcpName = 'my-jira-server';
@@ -164,8 +180,9 @@ describe('createPolicyUpdater', () => {
await new Promise((resolve) => setTimeout(resolve, 0));
// Verify file written
const writeCall = (fs.writeFile as unknown as Mock).mock.calls[0];
const writtenContent = writeCall[1] as string;
expect(fs.open).toHaveBeenCalledWith(expect.stringMatching(/\.tmp$/), 'wx');
const writeCall = mockFileHandle.writeFile.mock.calls[0];
const writtenContent = writeCall[0] as string;
expect(writtenContent).toContain(`mcpName = "${mcpName}"`);
expect(writtenContent).toContain(`toolName = "${simpleToolName}"`);
expect(writtenContent).toContain('priority = 200');
@@ -180,7 +197,12 @@ describe('createPolicyUpdater', () => {
(fs.readFile as unknown as Mock).mockRejectedValue(
new Error('File not found'),
);
(fs.writeFile as unknown as Mock).mockResolvedValue(undefined);
const mockFileHandle = {
writeFile: vi.fn().mockResolvedValue(undefined),
close: vi.fn().mockResolvedValue(undefined),
};
(fs.open as unknown as Mock).mockResolvedValue(mockFileHandle);
(fs.rename as unknown as Mock).mockResolvedValue(undefined);
const mcpName = 'my"jira"server';
@@ -195,8 +217,9 @@ describe('createPolicyUpdater', () => {
await new Promise((resolve) => setTimeout(resolve, 0));
const writeCall = (fs.writeFile as unknown as Mock).mock.calls[0];
const writtenContent = writeCall[1] as string;
expect(fs.open).toHaveBeenCalledWith(expect.stringMatching(/\.tmp$/), 'wx');
const writeCall = mockFileHandle.writeFile.mock.calls[0];
const writtenContent = writeCall[0] as string;
// Verify escaping - should be valid TOML
// Note: @iarna/toml optimizes for shortest representation, so it may use single quotes 'foo"bar'