import { strict as assert } from 'node:assert'; import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'; import { createServer } from 'node:http'; import { brotliDecompressSync, gunzipSync } from 'node:zlib'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { createLocalApiServer } from './local-api-server.mjs'; async function listen(server, host = '127.0.0.1', port = 0) { await new Promise((resolve, reject) => { const onListening = () => { server.off('error', onError); resolve(); }; const onError = (error) => { server.off('listening', onListening); reject(error); }; server.once('listening', onListening); server.once('error', onError); server.listen(port, host); }); const address = server.address(); if (!address || typeof address === 'string') { throw new Error('Failed to resolve server address'); } return address.port; } async function setupRemoteServer() { const hits = []; const origins = []; const server = createServer((req, res) => { const url = new URL(req.url || '/', 'http://127.0.0.1'); hits.push(url.pathname); origins.push(req.headers.origin || null); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ source: 'remote', path: url.pathname, origin: req.headers.origin || null, })); }); const port = await listen(server); return { hits, origins, remoteBase: `http://127.0.0.1:${port}`, async close() { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); }, }; } async function setupApiDir(files) { const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'wm-sidecar-test-')); const apiDir = path.join(tempRoot, 'api'); await mkdir(apiDir, { recursive: true }); await Promise.all( Object.entries(files).map(async ([relativePath, source]) => { const absolute = path.join(apiDir, relativePath); await mkdir(path.dirname(absolute), { recursive: true }); await writeFile(absolute, source, 'utf8'); }) ); return { apiDir, async cleanup() { await rm(tempRoot, { recursive: true, force: true }); }, }; } async function setupResourceDirWithUpApi(files) { const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'wm-sidecar-resource-test-')); const apiDir = path.join(tempRoot, '_up_', 'api'); await mkdir(apiDir, { recursive: true }); await Promise.all( Object.entries(files).map(async ([relativePath, source]) => { const absolute = path.join(apiDir, relativePath); await mkdir(path.dirname(absolute), { recursive: true }); await writeFile(absolute, source, 'utf8'); }) ); return { resourceDir: tempRoot, apiDir, async cleanup() { await rm(tempRoot, { recursive: true, force: true }); }, }; } test('returns local error directly when cloudFallback is off (default)', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({ 'fred-data.js': ` export default async function handler() { return new Response(JSON.stringify({ source: 'local-error' }), { status: 500, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/fred-data`); assert.equal(response.status, 500); const body = await response.json(); assert.equal(body.source, 'local-error'); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('falls back to cloud when cloudFallback is enabled and local handler returns 500', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({ 'fred-data.js': ` export default async function handler() { return new Response(JSON.stringify({ source: 'local-error' }), { status: 500, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, cloudFallback: 'true', logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/fred-data`); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.source, 'remote'); assert.equal(remote.hits.includes('/api/fred-data'), true); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('uses local handler response when local handler succeeds', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({ 'live.js': ` export default async function handler() { return new Response(JSON.stringify({ source: 'local-ok' }), { status: 200, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/live`); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.source, 'local-ok'); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('returns 404 when local route does not exist and cloudFallback is off', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/not-found`); assert.equal(response.status, 404); const body = await response.json(); assert.equal(body.error, 'No local handler for this endpoint'); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('strips browser origin headers before invoking local handlers', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({ 'origin-check.js': ` export default async function handler(req) { const origin = req.headers.get('origin'); return new Response(JSON.stringify({ source: 'local', originPresent: Boolean(origin), }), { status: 200, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/origin-check`, { headers: { Origin: 'https://tauri.localhost' }, }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.source, 'local'); assert.equal(body.originPresent, false); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('strips browser origin headers when proxying to cloud fallback (cloudFallback enabled)', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, cloudFallback: 'true', logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/no-local-handler`, { headers: { Origin: 'https://tauri.localhost' }, }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.source, 'remote'); assert.equal(body.origin, null); assert.equal(remote.origins[0], null); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('responds to OPTIONS preflight with CORS headers', async () => { const localApi = await setupApiDir({ 'data.js': ` export default async function handler() { return new Response('{}', { status: 200, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/data`, { method: 'OPTIONS' }); assert.equal(response.status, 204); assert.equal(response.headers.get('access-control-allow-methods'), 'GET, POST, PUT, DELETE, OPTIONS'); } finally { await app.close(); await localApi.cleanup(); } }); test('resolves packaged tauri resource layout under _up_/api', async () => { const remote = await setupRemoteServer(); const localResource = await setupResourceDirWithUpApi({ 'live.js': ` export default async function handler() { return new Response(JSON.stringify({ source: 'local-up' }), { status: 200, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, resourceDir: localResource.resourceDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { assert.equal(app.context.apiDir, localResource.apiDir); assert.equal(app.routes.length, 1); const response = await fetch(`http://127.0.0.1:${port}/api/live`); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.source, 'local-up'); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localResource.cleanup(); await remote.close(); } }); // ── Ollama env key allowlist + validation tests ── test('accepts OLLAMA_API_URL via /api/local-env-update', async () => { const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-env-update`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: 'http://127.0.0.1:11434' }), }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.ok, true); assert.equal(body.key, 'OLLAMA_API_URL'); assert.equal(process.env.OLLAMA_API_URL, 'http://127.0.0.1:11434'); } finally { delete process.env.OLLAMA_API_URL; await app.close(); await localApi.cleanup(); } }); test('accepts OLLAMA_MODEL via /api/local-env-update', async () => { const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-env-update`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_MODEL', value: 'llama3.1:8b' }), }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.ok, true); assert.equal(body.key, 'OLLAMA_MODEL'); assert.equal(process.env.OLLAMA_MODEL, 'llama3.1:8b'); } finally { delete process.env.OLLAMA_MODEL; await app.close(); await localApi.cleanup(); } }); test('rejects unknown key via /api/local-env-update', async () => { const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-env-update`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'NOT_ALLOWED_KEY', value: 'some-value' }), }); assert.equal(response.status, 403); const body = await response.json(); assert.equal(body.error, 'key not in allowlist'); } finally { await app.close(); await localApi.cleanup(); } }); test('validates OLLAMA_API_URL via /api/local-validate-secret (reachable endpoint)', async () => { // Stand up a mock Ollama server that responds to /v1/models const mockOllama = createServer((req, res) => { if (req.url === '/v1/models') { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ data: [{ id: 'llama3.1:8b' }] })); } else { res.writeHead(404); res.end('not found'); } }); const ollamaPort = await listen(mockOllama); const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-validate-secret`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: `http://127.0.0.1:${ollamaPort}` }), }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.valid, true); assert.equal(body.message, 'Ollama endpoint verified'); } finally { await app.close(); await localApi.cleanup(); await new Promise((resolve, reject) => { mockOllama.close((err) => (err ? reject(err) : resolve())); }); } }); test('validates LM Studio style /v1 base URL via /api/local-validate-secret', async () => { const mockOpenAiCompatible = createServer((req, res) => { if (req.url === '/v1/models') { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ data: [{ id: 'qwen2.5-7b-instruct' }] })); } else { res.writeHead(404); res.end('not found'); } }); const providerPort = await listen(mockOpenAiCompatible); const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-validate-secret`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: `http://127.0.0.1:${providerPort}/v1` }), }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.valid, true); assert.equal(body.message, 'Ollama endpoint verified'); } finally { await app.close(); await localApi.cleanup(); await new Promise((resolve, reject) => { mockOpenAiCompatible.close((err) => (err ? reject(err) : resolve())); }); } }); test('validates OLLAMA_API_URL via native /api/tags fallback', async () => { // Mock server that only responds to /api/tags (not /v1/models) const mockOllama = createServer((req, res) => { if (req.url === '/api/tags') { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ models: [{ name: 'llama3.1:8b' }] })); } else { res.writeHead(404); res.end('not found'); } }); const ollamaPort = await listen(mockOllama); const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-validate-secret`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: `http://127.0.0.1:${ollamaPort}` }), }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.valid, true); assert.equal(body.message, 'Ollama endpoint verified (native API)'); } finally { await app.close(); await localApi.cleanup(); await new Promise((resolve, reject) => { mockOllama.close((err) => (err ? reject(err) : resolve())); }); } }); test('validates OLLAMA_MODEL stores model name', async () => { const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-validate-secret`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_MODEL', value: 'mistral:7b' }), }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.valid, true); assert.equal(body.message, 'Model name stored'); } finally { await app.close(); await localApi.cleanup(); } }); test('rejects OLLAMA_API_URL with non-http protocol', async () => { const localApi = await setupApiDir({}); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/local-validate-secret`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: 'ftp://127.0.0.1:11434' }), }); assert.equal(response.status, 422); const body = await response.json(); assert.equal(body.valid, false); assert.equal(body.message, 'Must be an http(s) URL'); } finally { await app.close(); await localApi.cleanup(); } }); test('auth-required behavior unchanged — rejects unauthenticated requests when token is set', async () => { const localApi = await setupApiDir({}); const originalToken = process.env.LOCAL_API_TOKEN; process.env.LOCAL_API_TOKEN = 'secret-token-123'; const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { // Request without auth header should be rejected const response = await fetch(`http://127.0.0.1:${port}/api/local-env-update`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: 'http://127.0.0.1:11434' }), }); assert.equal(response.status, 401); const body = await response.json(); assert.equal(body.error, 'Unauthorized'); // Request with correct auth header should succeed const authedResponse = await fetch(`http://127.0.0.1:${port}/api/local-env-update`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer secret-token-123', }, body: JSON.stringify({ key: 'OLLAMA_API_URL', value: 'http://127.0.0.1:11434' }), }); assert.equal(authedResponse.status, 200); } finally { if (originalToken !== undefined) { process.env.LOCAL_API_TOKEN = originalToken; } else { delete process.env.LOCAL_API_TOKEN; } delete process.env.OLLAMA_API_URL; await app.close(); await localApi.cleanup(); } }); test('prefers Brotli compression for payloads larger than 1KB when supported by the client', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({ 'compression-check.js': ` export default async function handler() { const payload = { value: 'x'.repeat(3000) }; return new Response(JSON.stringify(payload), { status: 200, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/compression-check`, { headers: { 'Accept-Encoding': 'gzip, br' }, }); assert.equal(response.status, 200); assert.equal(response.headers.get('content-encoding'), 'br'); const compressed = Buffer.from(await response.arrayBuffer()); const decompressed = brotliDecompressSync(compressed).toString('utf8'); const body = JSON.parse(decompressed); assert.equal(body.value.length, 3000); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } }); test('uses gzip compression when Brotli is unavailable but gzip is accepted', async () => { const remote = await setupRemoteServer(); const localApi = await setupApiDir({ 'compression-check.js': ` export default async function handler() { const payload = { value: 'x'.repeat(3000) }; return new Response(JSON.stringify(payload), { status: 200, headers: { 'content-type': 'application/json' } }); } `, }); const app = await createLocalApiServer({ port: 0, apiDir: localApi.apiDir, remoteBase: remote.remoteBase, logger: { log() {}, warn() {}, error() {} }, }); const { port } = await app.start(); try { const response = await fetch(`http://127.0.0.1:${port}/api/compression-check`, { headers: { 'Accept-Encoding': 'gzip' }, }); assert.equal(response.status, 200); assert.equal(response.headers.get('content-encoding'), 'gzip'); const compressed = Buffer.from(await response.arrayBuffer()); const decompressed = gunzipSync(compressed).toString('utf8'); const body = JSON.parse(decompressed); assert.equal(body.value.length, 3000); assert.equal(remote.hits.length, 0); } finally { await app.close(); await localApi.cleanup(); await remote.close(); } });