From c5d55f0ab0730a9ecf4b12366fac3dc46135cb74 Mon Sep 17 00:00:00 2001 From: Shinku <17696928+shinku-chen@users.noreply.github.com> Date: Fri, 20 Mar 2026 01:03:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0TG=E4=BB=A3=E7=90=86=E9=80=89?= =?UTF-8?q?=E9=A1=B9,=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/routes/api/settings.events.test.ts | 27 +++++++ src/server/services/notifyService.test.ts | 60 ++++++++++++++++ src/web/pages/NotificationSettings.test.tsx | 70 +++++++++++++++++++ 3 files changed, 157 insertions(+) diff --git a/src/server/routes/api/settings.events.test.ts b/src/server/routes/api/settings.events.test.ts index e0918e6..86ffe92 100644 --- a/src/server/routes/api/settings.events.test.ts +++ b/src/server/routes/api/settings.events.test.ts @@ -55,6 +55,7 @@ describe('settings and auth events', () => { (config as any).telegramApiBaseUrl = 'https://api.telegram.org'; (config as any).telegramBotToken = ''; (config as any).telegramChatId = ''; + (config as any).telegramUseSystemProxy = false; }); afterAll(async () => { @@ -219,6 +220,32 @@ describe('settings and auth events', () => { expect(body.message).toContain('Telegram API Base URL'); }); + it('persists and returns telegram use system proxy from runtime settings', async () => { + const updateResponse = await app.inject({ + method: 'PUT', + url: '/api/settings/runtime', + payload: { + telegramUseSystemProxy: true, + }, + }); + + expect(updateResponse.statusCode).toBe(200); + const updated = updateResponse.json() as { telegramUseSystemProxy?: boolean }; + expect(updated.telegramUseSystemProxy).toBe(true); + expect((config as any).telegramUseSystemProxy).toBe(true); + + const saved = await db.select().from(schema.settings).where(eq(schema.settings.key, 'telegram_use_system_proxy')).get(); + expect(saved?.value).toBe(JSON.stringify(true)); + + const getResponse = await app.inject({ + method: 'GET', + url: '/api/settings/runtime', + }); + expect(getResponse.statusCode).toBe(200); + const runtime = getResponse.json() as { telegramUseSystemProxy?: boolean }; + expect(runtime.telegramUseSystemProxy).toBe(true); + }); + it('persists and returns routing fallback unit cost from runtime settings', async () => { const updateResponse = await app.inject({ method: 'PUT', diff --git a/src/server/services/notifyService.test.ts b/src/server/services/notifyService.test.ts index feba21e..f96b913 100644 --- a/src/server/services/notifyService.test.ts +++ b/src/server/services/notifyService.test.ts @@ -17,12 +17,24 @@ vi.mock('undici', () => ({ fetch: (...args: unknown[]) => fetchMock(...args), })); +const withExplicitProxyRequestInitMock = vi.fn( + (_proxyUrl: unknown, options?: Record) => { + if (_proxyUrl) return { ...(options || {}), dispatcher: 'mock-proxy-dispatcher' }; + return options ?? {}; + }, +); + +vi.mock('./siteProxy.js', () => ({ + withExplicitProxyRequestInit: (...args: unknown[]) => withExplicitProxyRequestInitMock(...args), +})); + describe('notifyService', () => { beforeEach(async () => { vi.resetModules(); sendMailMock.mockReset(); createTransportMock.mockClear(); fetchMock.mockReset(); + withExplicitProxyRequestInitMock.mockClear(); const { config } = await import('../config.js'); config.notifyCooldownSec = 300; @@ -35,6 +47,8 @@ describe('notifyService', () => { (config as any).telegramEnabled = false; (config as any).telegramBotToken = ''; (config as any).telegramChatId = ''; + (config as any).telegramUseSystemProxy = false; + config.systemProxyUrl = ''; config.smtpEnabled = true; config.smtpHost = 'smtp.example.com'; config.smtpPort = 465; @@ -222,4 +236,50 @@ describe('notifyService', () => { }), ); }); + + it('applies system proxy dispatcher when telegramUseSystemProxy is enabled', async () => { + const { config } = await import('../config.js'); + (config as any).telegramEnabled = true; + (config as any).telegramBotToken = '123456:telegram-token'; + (config as any).telegramChatId = '-1001234567890'; + (config as any).telegramUseSystemProxy = true; + config.systemProxyUrl = 'http://127.0.0.1:7890'; + config.smtpEnabled = false; + + fetchMock.mockResolvedValue({ ok: true, status: 200 }); + + const { sendNotification } = await import('./notifyService.js'); + await sendNotification('测试通知', 'proxy-test', 'info', { bypassThrottle: true, throwOnFailure: true }); + + expect(withExplicitProxyRequestInitMock).toHaveBeenCalledWith( + 'http://127.0.0.1:7890', + expect.objectContaining({ method: 'POST' }), + ); + expect(fetchMock).toHaveBeenCalledWith( + expect.stringContaining('/sendMessage'), + expect.objectContaining({ dispatcher: 'mock-proxy-dispatcher' }), + ); + }); + + it('does not apply proxy dispatcher when telegramUseSystemProxy is disabled', async () => { + const { config } = await import('../config.js'); + (config as any).telegramEnabled = true; + (config as any).telegramBotToken = '123456:telegram-token'; + (config as any).telegramChatId = '-1001234567890'; + (config as any).telegramUseSystemProxy = false; + config.systemProxyUrl = 'http://127.0.0.1:7890'; + config.smtpEnabled = false; + + fetchMock.mockResolvedValue({ ok: true, status: 200 }); + + const { sendNotification } = await import('./notifyService.js'); + await sendNotification('测试通知', 'no-proxy-test', 'info', { bypassThrottle: true, throwOnFailure: true }); + + expect(withExplicitProxyRequestInitMock).toHaveBeenCalledWith( + null, + expect.objectContaining({ method: 'POST' }), + ); + const fetchOptions = fetchMock.mock.calls[0]?.[1] as Record; + expect(fetchOptions.dispatcher).toBeUndefined(); + }); }); diff --git a/src/web/pages/NotificationSettings.test.tsx b/src/web/pages/NotificationSettings.test.tsx index 92c5415..b12c8e0 100644 --- a/src/web/pages/NotificationSettings.test.tsx +++ b/src/web/pages/NotificationSettings.test.tsx @@ -43,6 +43,7 @@ describe('NotificationSettings', () => { telegramApiBaseUrl: 'https://tg-proxy.example.com', telegramChatId: '-1001234567890', telegramBotTokenMasked: '1234****token', + telegramUseSystemProxy: false, smtpEnabled: false, smtpHost: '', smtpPort: 587, @@ -105,4 +106,73 @@ describe('NotificationSettings', () => { root?.unmount(); } }); + + it('loads and saves telegram use system proxy toggle', async () => { + apiMock.getRuntimeSettings.mockResolvedValue({ + webhookUrl: '', + barkUrl: '', + webhookEnabled: true, + barkEnabled: true, + serverChanEnabled: false, + telegramEnabled: true, + telegramApiBaseUrl: 'https://api.telegram.org', + telegramChatId: '-1001234567890', + telegramBotTokenMasked: '1234****token', + telegramUseSystemProxy: false, + smtpEnabled: false, + smtpHost: '', + smtpPort: 587, + smtpSecure: false, + smtpUser: '', + smtpFrom: '', + smtpTo: '', + notifyCooldownSec: 300, + }); + + let root: ReturnType | null = null; + try { + await act(async () => { + root = create( + + + + + , + ); + }); + await flushMicrotasks(); + + const allCheckboxes = root.root.findAll((node) => ( + node.type === 'input' && node.props.type === 'checkbox' + )); + const proxyCheckbox = allCheckboxes.find((node) => { + const parent = node.parent; + if (!parent) return false; + const text = collectText(parent); + return text.includes('使用系统代理'); + }); + expect(proxyCheckbox).toBeTruthy(); + expect(proxyCheckbox!.props.checked).toBe(false); + + await act(async () => { + proxyCheckbox!.props.onChange({ target: { checked: true } }); + }); + + const saveButton = root.root.find((node) => ( + node.type === 'button' + && typeof node.props.onClick === 'function' + && collectText(node).includes('保存通知设置') + )); + await act(async () => { + saveButton.props.onClick(); + }); + await flushMicrotasks(); + + expect(apiMock.updateRuntimeSettings).toHaveBeenCalledWith(expect.objectContaining({ + telegramUseSystemProxy: true, + })); + } finally { + root?.unmount(); + } + }); });