diff --git a/src/server/services/accountExtraConfig.test.ts b/src/server/services/accountExtraConfig.test.ts index a691796..2143dca 100644 --- a/src/server/services/accountExtraConfig.test.ts +++ b/src/server/services/accountExtraConfig.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'; import { getCredentialModeFromExtraConfig, getPlatformUserIdFromExtraConfig, + getProxyUrlFromExtraConfig, getSub2ApiAuthFromExtraConfig, guessPlatformUserIdFromUsername, mergeAccountExtraConfig, @@ -67,4 +68,18 @@ describe('accountExtraConfig', () => { sub2apiAuth: { refreshToken: ' ' }, }))).toBeNull(); }); + + it('reads proxyUrl from extra config', () => { + expect(getProxyUrlFromExtraConfig(JSON.stringify({ proxyUrl: 'http://127.0.0.1:7890' }))).toBe('http://127.0.0.1:7890'); + expect(getProxyUrlFromExtraConfig(JSON.stringify({ proxyUrl: ' socks5://proxy.local:1080 ' }))).toBe('socks5://proxy.local:1080'); + }); + + it('returns null for missing or empty proxyUrl', () => { + expect(getProxyUrlFromExtraConfig(JSON.stringify({}))).toBeNull(); + expect(getProxyUrlFromExtraConfig(JSON.stringify({ proxyUrl: '' }))).toBeNull(); + expect(getProxyUrlFromExtraConfig(JSON.stringify({ proxyUrl: ' ' }))).toBeNull(); + expect(getProxyUrlFromExtraConfig(null)).toBeNull(); + expect(getProxyUrlFromExtraConfig(undefined)).toBeNull(); + expect(getProxyUrlFromExtraConfig('invalid-json')).toBeNull(); + }); }); diff --git a/src/server/services/siteProxy.test.ts b/src/server/services/siteProxy.test.ts index 5cdb5f9..b1051e7 100644 --- a/src/server/services/siteProxy.test.ts +++ b/src/server/services/siteProxy.test.ts @@ -115,4 +115,77 @@ describe('siteProxy', () => { expect(headers.get('x-site-scope')).toBe('site-level'); expect(headers.get('x-request-id')).toBe('req-1'); }); + + it('resolveChannelProxyUrl prefers account proxy over site proxy', async () => { + const { resolveChannelProxyUrl } = await import('./siteProxy.js'); + + const accountConfig = JSON.stringify({ proxyUrl: 'http://account-proxy:8080' }); + const siteWithProxy = { useSystemProxy: true }; + + expect(resolveChannelProxyUrl(siteWithProxy, accountConfig)).toBe('http://account-proxy:8080'); + expect(resolveChannelProxyUrl(siteWithProxy, null)).toBeNull(); + expect(resolveChannelProxyUrl(siteWithProxy, JSON.stringify({}))).toBeNull(); + }); + + it('withSiteRecordProxyRequestInit uses account proxy when provided', async () => { + const { withSiteRecordProxyRequestInit } = await import('./siteProxy.js'); + const result = withSiteRecordProxyRequestInit( + { useSystemProxy: false }, + { method: 'POST' }, + 'http://account-proxy:8080', + ); + expect('dispatcher' in result).toBe(true); + }); + + it('withSiteRecordProxyRequestInit ignores invalid account proxy', async () => { + const { withSiteRecordProxyRequestInit } = await import('./siteProxy.js'); + const result = withSiteRecordProxyRequestInit( + { useSystemProxy: false }, + { method: 'POST' }, + 'not-a-url', + ); + expect('dispatcher' in result).toBe(false); + }); + + it('withAccountProxyOverride sets ALS context for nested proxy calls', async () => { + const { withAccountProxyOverride, withSiteProxyRequestInit } = await import('./siteProxy.js'); + + await db.insert(schema.sites).values({ + name: 'als-site', + url: 'https://als-site.example.com', + platform: 'new-api', + }).run(); + + const result = await withAccountProxyOverride( + 'http://account-als-proxy:9090', + async () => { + return withSiteProxyRequestInit('https://als-site.example.com/v1/models', { + method: 'GET', + }); + }, + ); + + expect('dispatcher' in result).toBe(true); + }); + + it('withAccountProxyOverride skips ALS when proxy is null', async () => { + const { withAccountProxyOverride, withSiteProxyRequestInit } = await import('./siteProxy.js'); + + await db.insert(schema.sites).values({ + name: 'als-null-site', + url: 'https://als-null-site.example.com', + platform: 'new-api', + }).run(); + + const result = await withAccountProxyOverride( + null, + async () => { + return withSiteProxyRequestInit('https://als-null-site.example.com/v1/models', { + method: 'GET', + }); + }, + ); + + expect('dispatcher' in result).toBe(false); + }); });