feat(config): persist config.patch updates atomically
This commit is contained in:
@@ -718,9 +718,10 @@ describe('config handlers', () => {
|
||||
};
|
||||
const result = await handlers['config.patch'](req) as GatewayResponse;
|
||||
|
||||
const r = result.result as { applied: string[]; rejected: string[] };
|
||||
const r = result.result as { applied: string[]; rejected: string[]; persisted: boolean };
|
||||
expect(r.applied).toEqual(['hooks.confirm', 'hooks.log']);
|
||||
expect(r.rejected).toEqual([]);
|
||||
expect(r.persisted).toBe(false);
|
||||
// Verify the config was actually mutated
|
||||
expect(config.hooks.confirm).toEqual(['shell.exec', 'file.write']);
|
||||
expect(config.hooks.log).toEqual(['file.read']);
|
||||
@@ -741,9 +742,10 @@ describe('config handlers', () => {
|
||||
};
|
||||
const result = await handlers['config.patch'](req) as GatewayResponse;
|
||||
|
||||
const r = result.result as { applied: string[]; rejected: string[] };
|
||||
const r = result.result as { applied: string[]; rejected: string[]; persisted: boolean };
|
||||
expect(r.applied).toEqual(['hooks.confirm']);
|
||||
expect(r.rejected).toEqual(['telegram.bot_token']);
|
||||
expect(r.persisted).toBe(false);
|
||||
});
|
||||
|
||||
it('config.patch rejects invalid value types', async () => {
|
||||
@@ -760,9 +762,49 @@ describe('config handlers', () => {
|
||||
};
|
||||
const result = await handlers['config.patch'](req) as GatewayResponse;
|
||||
|
||||
const r = result.result as { applied: string[]; rejected: string[] };
|
||||
const r = result.result as { applied: string[]; rejected: string[]; persisted: boolean };
|
||||
expect(r.applied).toEqual([]);
|
||||
expect(r.rejected).toEqual(['hooks.confirm']);
|
||||
expect(r.persisted).toBe(false);
|
||||
});
|
||||
|
||||
it('config.patch persists changes when persistence callback is provided', async () => {
|
||||
const config = makeConfig();
|
||||
const persist = vi.fn();
|
||||
const handlers = createConfigHandlers({ config: config as any, persistConfig: persist as any });
|
||||
const req: GatewayRequest = {
|
||||
id: 6,
|
||||
method: 'config.patch',
|
||||
params: { patches: { 'hooks.confirm': ['shell.exec', 'file.write'] } },
|
||||
};
|
||||
const result = await handlers['config.patch'](req) as GatewayResponse;
|
||||
const r = result.result as { applied: string[]; rejected: string[]; persisted: boolean };
|
||||
|
||||
expect(r.applied).toEqual(['hooks.confirm']);
|
||||
expect(r.rejected).toEqual([]);
|
||||
expect(r.persisted).toBe(true);
|
||||
expect(persist).toHaveBeenCalledTimes(1);
|
||||
expect(config.hooks.confirm).toEqual(['shell.exec', 'file.write']);
|
||||
});
|
||||
|
||||
it('config.patch does not mutate runtime config when persistence fails', async () => {
|
||||
const config = makeConfig();
|
||||
const before = [...config.hooks.confirm];
|
||||
const persist = vi.fn().mockRejectedValue(new Error('disk full'));
|
||||
const handlers = createConfigHandlers({ config: config as any, persistConfig: persist as any });
|
||||
const req: GatewayRequest = {
|
||||
id: 7,
|
||||
method: 'config.patch',
|
||||
params: { patches: { 'hooks.confirm': ['file.write'] } },
|
||||
};
|
||||
const result = await handlers['config.patch'](req) as GatewayResponse;
|
||||
const r = result.result as { applied: string[]; rejected: string[]; persisted: boolean; persistError?: string };
|
||||
|
||||
expect(r.applied).toEqual([]);
|
||||
expect(r.rejected).toEqual([]);
|
||||
expect(r.persisted).toBe(false);
|
||||
expect(r.persistError).toContain('disk full');
|
||||
expect(config.hooks.confirm).toEqual(before);
|
||||
});
|
||||
|
||||
it('config.patch requires patches object', async () => {
|
||||
|
||||
Reference in New Issue
Block a user