Download server/src/__tests__/lib/proxy-restore.test.ts from automindy/fokemal: direct link, hf CLI and curl.
- Browser
- Download file 9.7 kB
-
https://huggingface.co/spaces/automindy/fokemal/resolve/main/server/src/__tests__/lib/proxy-restore.test.ts
- Command line
-
hf download hf://spaces/automindy/fokemal/server/src/__tests__/lib/proxy-restore.test.ts
-
curl -L -o proxy-restore.test.ts https://huggingface.co/spaces/automindy/fokemal/resolve/main/server/src/__tests__/lib/proxy-restore.test.ts
9.7 kB
| import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest'; | |
| import { initDb, getDb, setSetting } from '../../db/index.js'; | |
| import { | |
| restoreProxySettings, | |
| getProxyUrl, | |
| getProxyMode, | |
| getFetchRelayToken, | |
| isProxyEnabled, | |
| getProxyBypassPlatforms, | |
| getNoProxyRules, | |
| applyProxyUrl, | |
| applyProxyMode, | |
| applyProxyEnabled, | |
| applyProxyBypass, | |
| applyFetchRelayToken, | |
| encodeFetchRelayToken, | |
| } from '../../lib/proxy.js'; | |
| // #838 reads the OS-wide proxy (scutil/reg/gsettings) as a last-resort | |
| // fallback when neither the env nor the DB configures one. A developer | |
| // machine can genuinely have that set (macOS System Settings → Proxies), | |
| // which fails every "no proxy configured" case below for the same reason | |
| // clearProxyEnv() exists — the ambient system, not the code under test. | |
| // '' parses as "no proxy" in every platform branch, so the suite sees a | |
| // clean machine regardless of the host's system settings. | |
| vi.mock('node:child_process', async (importOriginal) => ({ | |
| ...(await importOriginal<Record<string, unknown>>()), | |
| execFileSync: () => '', | |
| })); | |
| // #949: the desktop embedder builds the app without server/src/index.ts, so | |
| // the proxy state it starts with is whatever the module defaults are — an | |
| // empty URL. The URL the user saved through PUT /api/settings/proxy sits in | |
| // the settings table, ignored until the next re-save. restoreProxySettings() | |
| // is the single hydration step both entry points now call after initDb; this | |
| // test pins that the DB value actually reaches the process state. | |
| const PROXY_ENV_VARS = ['PROXY_URL', 'PROXY_MODE', 'FETCH_RELAY_TOKEN', 'ALL_PROXY', 'HTTPS_PROXY', 'HTTP_PROXY', 'NO_PROXY']; | |
| function clearProxyEnv(): void { | |
| for (const name of PROXY_ENV_VARS) { | |
| delete process.env[name]; | |
| delete process.env[name.toLowerCase()]; | |
| } | |
| } | |
| let closed = false; | |
| beforeEach(() => { | |
| clearProxyEnv(); | |
| // Reset to the module defaults so each case starts from "fresh process". | |
| applyProxyUrl(''); | |
| applyProxyMode('forward'); | |
| applyFetchRelayToken(''); | |
| applyProxyEnabled(true); | |
| applyProxyBypass(''); | |
| }); | |
| afterAll(() => { | |
| if (!closed) { | |
| getDb().close(); | |
| closed = true; | |
| } | |
| }); | |
| describe('restoreProxySettings (desktop embedder hydration, #949)', () => { | |
| it('loads a saved proxy URL, enabled flag and bypass list from the settings table', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| setSetting('proxy_url', 'socks5h://127.0.0.1:9050'); | |
| setSetting('proxy_enabled', '1'); | |
| setSetting('proxy_bypass', 'groq,openrouter'); | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('socks5h://127.0.0.1:9050'); | |
| expect(isProxyEnabled()).toBe(true); | |
| expect(getProxyBypassPlatforms()).toEqual(['groq', 'openrouter']); | |
| }); | |
| it('respects a disabled proxy saved across a restart', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| setSetting('proxy_url', 'http://127.0.0.1:3128'); | |
| setSetting('proxy_enabled', '0'); | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://127.0.0.1:3128'); | |
| expect(isProxyEnabled()).toBe(false); | |
| }); | |
| it('keeps the defaults when nothing was ever saved', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe(''); | |
| expect(getProxyMode()).toBe('forward'); | |
| expect(isProxyEnabled()).toBe(true); | |
| expect(getProxyBypassPlatforms()).toEqual([]); | |
| }); | |
| it('lets the PROXY_URL env var outrank the saved value, as before', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| setSetting('proxy_url', 'http://saved:3128'); | |
| process.env.PROXY_URL = 'http://env:8080'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://env:8080'); | |
| expect(getProxyMode()).toBe('forward'); | |
| }); | |
| it('restores an explicitly saved Fetch Relay mode', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| setSetting('proxy_url', 'https://relay.example.test/secret'); | |
| setSetting('proxy_mode', 'fetch-relay'); | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('https://relay.example.test/secret'); | |
| expect(getProxyMode()).toBe('fetch-relay'); | |
| }); | |
| it('restores a saved Relay token and lets FETCH_RELAY_TOKEN override it', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| setSetting('fetch_relay_token', encodeFetchRelayToken('saved-token')); | |
| restoreProxySettings(); | |
| expect(getFetchRelayToken()).toBe('saved-token'); | |
| process.env.FETCH_RELAY_TOKEN = 'environment-token'; | |
| restoreProxySettings(); | |
| expect(getFetchRelayToken()).toBe('environment-token'); | |
| }); | |
| it('lets PROXY_MODE opt an environment PROXY_URL into Fetch Relay', () => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| process.env.PROXY_URL = 'https://relay.example.test/secret'; | |
| process.env.PROXY_MODE = 'fetch-relay'; | |
| restoreProxySettings(); | |
| expect(getProxyMode()).toBe('fetch-relay'); | |
| }); | |
| }); | |
| // The env tiers below are resolveProxySource()'s job, but restore time is the | |
| // only moment they are consulted on a desktop start: the embedder hydrates | |
| // once and then nothing re-reads the environment. A regression that skipped a | |
| // tier here would strand a user who has only ever exported HTTPS_PROXY. | |
| describe('restoreProxySettings + the standard env fallbacks (#353 x #949)', () => { | |
| function freshDb(): void { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| } | |
| it('falls back to ALL_PROXY when nothing was saved in the dashboard', () => { | |
| freshDb(); | |
| process.env.ALL_PROXY = 'socks5://all:1080'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('socks5://all:1080'); | |
| }); | |
| it('falls back to HTTPS_PROXY', () => { | |
| freshDb(); | |
| process.env.HTTPS_PROXY = 'http://https-tier:8443'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://https-tier:8443'); | |
| }); | |
| it('falls back to HTTP_PROXY', () => { | |
| freshDb(); | |
| process.env.HTTP_PROXY = 'http://http-tier:8080'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://http-tier:8080'); | |
| }); | |
| it('prefers ALL_PROXY, then HTTPS_PROXY, then HTTP_PROXY', () => { | |
| freshDb(); | |
| process.env.ALL_PROXY = 'http://all:1'; | |
| process.env.HTTPS_PROXY = 'http://https:2'; | |
| process.env.HTTP_PROXY = 'http://http:3'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://all:1'); | |
| }); | |
| it('reads the lower-case spelling too — the one curl/git users actually export', () => { | |
| freshDb(); | |
| process.env.https_proxy = 'http://lower:8443'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://lower:8443'); | |
| }); | |
| it('does NOT let the ambient env override a proxy the user typed into the dashboard', () => { | |
| freshDb(); | |
| setSetting('proxy_url', 'http://saved:3128'); | |
| process.env.ALL_PROXY = 'http://ambient:1080'; | |
| process.env.HTTPS_PROXY = 'http://ambient:8443'; | |
| process.env.HTTP_PROXY = 'http://ambient:8080'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://saved:3128'); | |
| }); | |
| it('still lets PROXY_URL outrank the ambient vars', () => { | |
| freshDb(); | |
| process.env.PROXY_URL = 'http://explicit:9090'; | |
| process.env.ALL_PROXY = 'http://ambient:1080'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://explicit:9090'); | |
| }); | |
| it('leaves the proxy empty when neither the DB nor any env var has one', () => { | |
| freshDb(); | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe(''); | |
| }); | |
| }); | |
| // NO_PROXY is read from the environment inside applyProxyUrl, so on the | |
| // desktop path it is parsed exactly once: during restore. If restore stopped | |
| // going through applyProxyUrl the saved URL might still land while the direct | |
| // list silently emptied, quietly proxying hosts the user excluded. | |
| describe('restoreProxySettings + NO_PROXY (#949)', () => { | |
| function freshDb(): void { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| } | |
| it('parses NO_PROXY at restore time, lower-cased and trimmed', () => { | |
| freshDb(); | |
| setSetting('proxy_url', 'http://127.0.0.1:3128'); | |
| process.env.NO_PROXY = 'localhost, .Internal.Corp ,, 10.0.0.1'; | |
| restoreProxySettings(); | |
| expect(getNoProxyRules()).toEqual(['localhost', '.internal.corp', '10.0.0.1']); | |
| }); | |
| it('normalises the `*.domain` spelling to the leading-dot form', () => { | |
| freshDb(); | |
| setSetting('proxy_url', 'http://127.0.0.1:3128'); | |
| process.env.NO_PROXY = '*.example.com'; | |
| restoreProxySettings(); | |
| expect(getNoProxyRules()).toEqual(['.example.com']); | |
| }); | |
| it('honours the lower-case no_proxy spelling', () => { | |
| freshDb(); | |
| setSetting('proxy_url', 'http://127.0.0.1:3128'); | |
| process.env.no_proxy = 'example.com'; | |
| restoreProxySettings(); | |
| expect(getNoProxyRules()).toEqual(['example.com']); | |
| }); | |
| it('applies NO_PROXY even when the proxy itself came from the env tier', () => { | |
| freshDb(); | |
| process.env.HTTPS_PROXY = 'http://ambient:8443'; | |
| process.env.NO_PROXY = 'internal.corp'; | |
| restoreProxySettings(); | |
| expect(getProxyUrl()).toBe('http://ambient:8443'); | |
| expect(getNoProxyRules()).toEqual(['internal.corp']); | |
| }); | |
| it('clears stale rules when the env no longer sets NO_PROXY', () => { | |
| freshDb(); | |
| setSetting('proxy_url', 'http://127.0.0.1:3128'); | |
| process.env.NO_PROXY = 'example.com'; | |
| restoreProxySettings(); | |
| expect(getNoProxyRules()).toEqual(['example.com']); | |
| delete process.env.NO_PROXY; | |
| restoreProxySettings(); | |
| expect(getNoProxyRules()).toEqual([]); | |
| }); | |
| }); | |