Files
stack/apps/gateway/src/preferences/preferences.service.spec.ts
Jarvis 774b76447d
Some checks failed
ci/woodpecker/pr/ci Pipeline failed
ci/woodpecker/push/ci Pipeline failed
fix: rename all packages from @mosaic/* to @mosaicstack/*
- Updated all package.json name fields and dependency references
- Updated all TypeScript/JavaScript imports
- Updated .woodpecker/publish.yml filters and registry paths
- Updated tools/install.sh scope default
- Updated .npmrc registry paths (worktree + host)
- Enhanced update-checker.ts with checkForAllUpdates() multi-package support
- Updated CLI update command to show table of all packages
- Added KNOWN_PACKAGES, formatAllPackagesTable, getInstallAllCommand
- Marked checkForUpdate() with @deprecated JSDoc

Closes #391
2026-04-04 21:43:23 -05:00

153 lines
5.5 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { PreferencesService, PLATFORM_DEFAULTS, IMMUTABLE_KEYS } from './preferences.service.js';
import type { Db } from '@mosaicstack/db';
/**
* Build a mock Drizzle DB where the select chain supports:
* db.select().from().where() → resolves to `listRows`
* db.insert().values().onConflictDoUpdate() → resolves to []
*/
function makeMockDb(listRows: Array<{ key: string; value: unknown }> = []): Db {
const chainWithLimit = {
limit: vi.fn().mockResolvedValue([]),
then: (resolve: (v: typeof listRows) => unknown) => Promise.resolve(listRows).then(resolve),
};
const selectFrom = {
from: vi.fn().mockReturnThis(),
where: vi.fn().mockReturnValue(chainWithLimit),
};
const deleteResult = {
where: vi.fn().mockResolvedValue([]),
};
// Single-round-trip upsert chain: insert().values().onConflictDoUpdate()
const insertResult = {
values: vi.fn().mockReturnThis(),
onConflictDoUpdate: vi.fn().mockResolvedValue([]),
};
return {
select: vi.fn().mockReturnValue(selectFrom),
delete: vi.fn().mockReturnValue(deleteResult),
insert: vi.fn().mockReturnValue(insertResult),
} as unknown as Db;
}
describe('PreferencesService', () => {
describe('getEffective', () => {
it('returns platform defaults when user has no overrides', async () => {
const db = makeMockDb([]);
const service = new PreferencesService(db);
const result = await service.getEffective('user-1');
expect(result['agent.thinkingLevel']).toBe('auto');
expect(result['agent.streamingEnabled']).toBe(true);
expect(result['session.autoCompactEnabled']).toBe(true);
expect(result['session.autoCompactThreshold']).toBe(0.8);
});
it('applies user overrides for mutable keys', async () => {
const db = makeMockDb([
{ key: 'agent.thinkingLevel', value: 'high' },
{ key: 'response.language', value: 'es' },
]);
const service = new PreferencesService(db);
const result = await service.getEffective('user-1');
expect(result['agent.thinkingLevel']).toBe('high');
expect(result['response.language']).toBe('es');
});
it('ignores user overrides for immutable keys — enforcement always wins', async () => {
const db = makeMockDb([
{ key: 'limits.maxThinkingLevel', value: 'high' },
{ key: 'limits.rateLimit', value: 9999 },
]);
const service = new PreferencesService(db);
const result = await service.getEffective('user-1');
// Should still be null (platform default), not the user-supplied values
expect(result['limits.maxThinkingLevel']).toBeNull();
expect(result['limits.rateLimit']).toBeNull();
});
});
describe('set', () => {
it('returns error when attempting to override an immutable key', async () => {
const db = makeMockDb();
const service = new PreferencesService(db);
const result = await service.set('user-1', 'limits.maxThinkingLevel', 'high');
expect(result.success).toBe(false);
expect(result.message).toContain('platform enforcement');
});
it('returns error when attempting to override limits.rateLimit', async () => {
const db = makeMockDb();
const service = new PreferencesService(db);
const result = await service.set('user-1', 'limits.rateLimit', 100);
expect(result.success).toBe(false);
expect(result.message).toContain('platform enforcement');
});
it('upserts a mutable preference and returns success', async () => {
// Single-round-trip INSERT … ON CONFLICT DO UPDATE path.
const db = makeMockDb([]);
const service = new PreferencesService(db);
const result = await service.set('user-1', 'agent.thinkingLevel', 'high');
expect(result.success).toBe(true);
expect(result.message).toContain('"agent.thinkingLevel"');
});
});
describe('reset', () => {
it('returns error when attempting to reset an immutable key', async () => {
const db = makeMockDb();
const service = new PreferencesService(db);
const result = await service.reset('user-1', 'limits.rateLimit');
expect(result.success).toBe(false);
expect(result.message).toContain('platform enforcement');
});
it('deletes user override and returns default value in message', async () => {
const db = makeMockDb();
const service = new PreferencesService(db);
const result = await service.reset('user-1', 'agent.thinkingLevel');
expect(result.success).toBe(true);
expect(result.message).toContain('"auto"'); // platform default for agent.thinkingLevel
});
});
describe('IMMUTABLE_KEYS', () => {
it('contains only the enforcement keys', () => {
expect(IMMUTABLE_KEYS.has('limits.maxThinkingLevel')).toBe(true);
expect(IMMUTABLE_KEYS.has('limits.rateLimit')).toBe(true);
expect(IMMUTABLE_KEYS.has('agent.thinkingLevel')).toBe(false);
});
});
describe('PLATFORM_DEFAULTS', () => {
it('has all expected keys', () => {
const expectedKeys = [
'agent.defaultModel',
'agent.thinkingLevel',
'agent.streamingEnabled',
'response.language',
'response.codeAnnotations',
'safety.confirmDestructiveTools',
'session.autoCompactThreshold',
'session.autoCompactEnabled',
'limits.maxThinkingLevel',
'limits.rateLimit',
];
for (const key of expectedKeys) {
expect(Object.prototype.hasOwnProperty.call(PLATFORM_DEFAULTS, key)).toBe(true);
}
});
});
});