Files
openmaic/OpenMAIC/tests/server/provider-access-policy.test.ts
2026-08-16 14:58:47 +08:00

122 lines
4.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
assertProviderAccess,
decideProviderAccess,
ServerManagedProviderRequiredError,
} from '@/lib/server/provider-access-policy';
describe('provider access policy', () => {
it('rejects an unmanaged provider on a dedicated server deployment', () => {
expect(decideProviderAccess('server', false)).toEqual({
allowed: false,
reason: 'server-managed-provider-required',
});
expect(() => assertProviderAccess('server', false)).toThrow(ServerManagedProviderRequiredError);
});
it('allows a managed provider but never accepts client connection configuration', () => {
expect(decideProviderAccess('server', true)).toEqual({
allowed: true,
acceptClientConfiguration: false,
});
});
it.each(['all', 'ops', 'learner'] as const)(
'keeps unmanaged provider compatibility for role=%s',
(role) => {
expect(decideProviderAccess(role, false)).toEqual({
allowed: true,
acceptClientConfiguration: true,
});
},
);
});
describe('provider-config deployment integration', () => {
beforeEach(() => {
vi.resetModules();
vi.unstubAllEnvs();
vi.stubEnv('NODE_ENV', 'test');
});
it('server + unmanaged rejects client key and base URL before model construction', async () => {
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
const { resolveApiKey, resolveBaseUrl } = await import('@/lib/server/provider-config');
// `vi.resetModules()` intentionally reloads the policy class together with
// provider-config, so assert the stable public error contract rather than
// identity against the pre-reset constructor.
expect(() => resolveApiKey('unmanaged-provider', 'forged-client-key')).toThrow(
/only permits providers configured by the server operator/,
);
expect(() => resolveBaseUrl('unmanaged-provider', 'https://client.invalid/v1')).toThrow(
/only permits providers configured by the server operator/,
);
});
it('the shared LLM resolver rejects server-role BYOK before creating a provider client', async () => {
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
const { resolveModel } = await import('@/lib/server/resolve-model');
await expect(
resolveModel({
modelString: 'caller-owned-provider:test-model',
apiKey: 'forged-client-key',
baseUrl: 'https://attacker.invalid/v1',
}),
).rejects.toThrow(/only permits providers configured by the server operator/);
});
it('server + managed ignores forged client values, including operator self-hosted URLs', async () => {
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
vi.stubEnv('OLLAMA_BASE_URL', 'http://ollama.operator.internal:11434');
const { resolveApiKey, resolveBaseUrl } = await import('@/lib/server/provider-config');
expect(resolveApiKey('ollama', 'forged-client-key')).toBe('');
expect(resolveBaseUrl('ollama', 'https://attacker.invalid/v1')).toBe(
'http://ollama.operator.internal:11434',
);
});
it('server keeps the intrinsic local PDF parser without accepting caller connection data', async () => {
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
const { requireProviderAccess, resolvePDFApiKey, resolvePDFBaseUrl } =
await import('@/lib/server/provider-config');
expect(requireProviderAccess('pdf', 'unpdf')).toEqual({
isServerConfigured: false,
acceptClientConfiguration: false,
});
expect(resolvePDFApiKey('unpdf', 'forged-client-key')).toBe('');
expect(resolvePDFBaseUrl('unpdf', 'https://attacker.invalid')).toBeUndefined();
});
it('the shared LLM resolver keeps operator self-hosted configuration authoritative', async () => {
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', 'server');
vi.stubEnv('OLLAMA_BASE_URL', 'http://ollama.operator.internal:11434');
const { resolveModel } = await import('@/lib/server/resolve-model');
const resolved = await resolveModel({
modelString: 'ollama:llama3.3',
apiKey: 'forged-client-key',
baseUrl: 'https://attacker.invalid/v1',
});
expect(resolved.apiKey).toBe('');
expect(resolved.baseUrl).toBe('http://ollama.operator.internal:11434');
});
it.each(['all', 'ops', 'learner'] as const)(
'role=%s keeps existing unmanaged BYOK resolution',
async (role) => {
vi.stubEnv('OPENMAIC_DEPLOYMENT_ROLE', role);
const { resolveApiKey, resolveBaseUrl } = await import('@/lib/server/provider-config');
expect(resolveApiKey('custom-provider', 'client-key')).toBe('client-key');
expect(resolveBaseUrl('custom-provider', 'https://client.example/v1')).toBe(
'https://client.example/v1',
);
},
);
});