feat(dashboard): configure services from clickable service cards

This commit is contained in:
William Valentin
2026-02-19 10:50:16 -08:00
parent 3b507d503f
commit 7a2176c15c
4 changed files with 442 additions and 7 deletions
+64
View File
@@ -1285,6 +1285,70 @@ describe('config handlers', () => {
expect(r.persisted).toBe(false);
});
it('config.patch applies service configuration keys for heartbeat and service toggles', async () => {
const config = makeConfig();
const handlers = createConfigHandlers({ config: asConfigValue(config) });
const req: GatewayRequest = {
id: 41,
method: 'config.patch',
params: {
patches: {
'automation.heartbeat.enabled': true,
'automation.heartbeat.interval': '1m',
'automation.heartbeat.notify_cooldown': '10m',
'automation.heartbeat.failure_threshold': 3,
'automation.heartbeat.disk_threshold_mb': 250,
'automation.heartbeat.process_memory_threshold_mb': 2048,
'automation.heartbeat.backup_failure_threshold': 2,
'automation.heartbeat.provider_error_rate_threshold': 0.4,
'automation.heartbeat.provider_error_min_calls': 8,
'automation.heartbeat.checks': ['gateway', 'model', 'disk'],
'automation.gmail.enabled': true,
'automation.gcal.enabled': true,
'backup.enabled': true,
'audio.enabled': true,
'sandbox.enabled': true,
},
},
};
const result = await handlers['config.patch'](req) as GatewayResponse;
const r = result.result as { applied: string[]; rejected: string[]; persisted: boolean };
expect(r.applied).toEqual([
'automation.heartbeat.enabled',
'automation.heartbeat.interval',
'automation.heartbeat.notify_cooldown',
'automation.heartbeat.failure_threshold',
'automation.heartbeat.disk_threshold_mb',
'automation.heartbeat.process_memory_threshold_mb',
'automation.heartbeat.backup_failure_threshold',
'automation.heartbeat.provider_error_rate_threshold',
'automation.heartbeat.provider_error_min_calls',
'automation.heartbeat.checks',
'automation.gmail.enabled',
'automation.gcal.enabled',
'backup.enabled',
'audio.enabled',
'sandbox.enabled',
]);
expect(r.rejected).toEqual([]);
expect(getPath(config, 'automation', 'heartbeat', 'enabled')).toBe(true);
expect(getPath(config, 'automation', 'heartbeat', 'interval')).toBe('1m');
expect(getPath(config, 'automation', 'heartbeat', 'notify_cooldown')).toBe('10m');
expect(getPath(config, 'automation', 'heartbeat', 'failure_threshold')).toBe(3);
expect(getPath(config, 'automation', 'heartbeat', 'disk_threshold_mb')).toBe(250);
expect(getPath(config, 'automation', 'heartbeat', 'process_memory_threshold_mb')).toBe(2048);
expect(getPath(config, 'automation', 'heartbeat', 'backup_failure_threshold')).toBe(2);
expect(getPath(config, 'automation', 'heartbeat', 'provider_error_rate_threshold')).toBe(0.4);
expect(getPath(config, 'automation', 'heartbeat', 'provider_error_min_calls')).toBe(8);
expect(getPath(config, 'automation', 'heartbeat', 'checks')).toEqual(['gateway', 'model', 'disk']);
expect(getPath(config, 'automation', 'gmail', 'enabled')).toBe(true);
expect(getPath(config, 'automation', 'gcal', 'enabled')).toBe(true);
expect(getPath(config, 'backup', 'enabled')).toBe(true);
expect(getPath(config, 'audio', 'enabled')).toBe(true);
expect(getPath(config, 'sandbox', 'enabled')).toBe(true);
});
it('config.patch persists changes when persistence callback is provided', async () => {
const config = makeConfig();
const persist = vi.fn();