import { http, HttpResponse } from 'msw'; import { afterEach, describe, expect, it, vi } from 'vitest'; import server from '@/setup.test'; afterEach(() => { vi.resetModules(); }); describe('config', () => { it('bilibilib cookie', async () => { process.env.BILIBILI_COOKIE_12 = 'cookie1'; process.env.BILIBILI_COOKIE_34 = 'cookie2'; const { config } = await import('./config'); expect(config.bilibili.cookies).toMatchObject({ 12: 'cookie1', 34: 'cookie2', }); delete process.env.BILIBILI_COOKIE_12; delete process.env.BILIBILI_COOKIE_34; }); it('unix socket', async () => { process.env.SOCKET = '/tmp/rsshub.sock'; const { config } = await import('./config'); expect(config.connect.socket).toBe('/tmp/rsshub.sock'); delete process.env.SOCKET; }); it('unix socket defaults to undefined', async () => { process.env.SOCKET = ''; const { config } = await import('./config'); expect(config.connect.socket).toBeUndefined(); delete process.env.SOCKET; }); it('email config', async () => { process.env['EMAIL_CONFIG_xx.qq.com'] = 'token1'; process.env['EMAIL_CONFIG_oo.qq.com'] = 'token2'; const { config } = await import('./config'); expect(config.email.config).toMatchObject({ 'xx.qq.com': 'token1', 'oo.qq.com': 'token2', }); delete process.env['EMAIL_CONFIG_xx.qq.com']; delete process.env['EMAIL_CONFIG_oo.qq.com']; }); it('discuz cookie', async () => { process.env.DISCUZ_COOKIE_12 = 'cookie1'; process.env.DISCUZ_COOKIE_34 = 'cookie2'; const { config } = await import('./config'); expect(config.discuz.cookies).toMatchObject({ 12: 'cookie1', 34: 'cookie2', }); delete process.env.DISCUZ_COOKIE_12; delete process.env.DISCUZ_COOKIE_34; }); it('medium cookie', async () => { process.env.MEDIUM_COOKIE_12 = 'cookie1'; process.env.MEDIUM_COOKIE_34 = 'cookie2'; const { config } = await import('./config'); expect(config.medium.cookies).toMatchObject({ 12: 'cookie1', 34: 'cookie2', }); delete process.env.MEDIUM_COOKIE_12; delete process.env.MEDIUM_COOKIE_34; }); it('discourse config', async () => { process.env.DISCOURSE_CONFIG_12 = JSON.stringify({ a: 1 }); process.env.DISCOURSE_CONFIG_34 = JSON.stringify({ b: 2 }); const { config } = await import('./config'); expect(config.discourse.config).toMatchObject({ 12: { a: 1 }, 34: { b: 2 }, }); delete process.env.DISCOURSE_CONFIG_12; delete process.env.DISCOURSE_CONFIG_34; }); it('no random ua', async () => { process.env.NO_RANDOM_UA = '1'; const { config } = await import('./config'); expect(config.ua).toBe('RSSHub/1.0 (+http://github.com/DIYgod/RSSHub; like FeedFetcher-Google)'); delete process.env.NO_RANDOM_UA; }); it('default ua from preset', async () => { const { config } = await import('./config'); expect(config.ua).toContain('Chrome'); expect(config.ua).toContain('Macintosh'); expect(config.isDefaultUA).toBe(true); }); it('http cache config', async () => { process.env.CACHE_HTTP_URL = 'https://cache.example.com'; process.env.CACHE_HTTP_TOKEN = 'token'; const { config } = await import('./config'); expect(config.httpCache).toMatchObject({ url: 'https://cache.example.com', token: 'token', }); delete process.env.CACHE_HTTP_URL; delete process.env.CACHE_HTTP_TOKEN; }); it('Kemono and Coomer root URLs', async () => { process.env.KEMONO_ROOT_URL = 'https://kemono.example.com/'; process.env.COOMER_ROOT_URL = 'https://coomer.example.com///'; const { config } = await import('./config'); expect(config.kemono.rootUrl).toBe('https://kemono.example.com'); expect(config.kemono.assetsUrl).toBe('https://img.kemono.example.com'); expect(config.coomer.rootUrl).toBe('https://coomer.example.com'); expect(config.coomer.assetsUrl).toBe('https://img.coomer.example.com'); delete process.env.KEMONO_ROOT_URL; delete process.env.COOMER_ROOT_URL; }); it('Kemono and Coomer inferred asset URLs preserve root paths', async () => { process.env.KEMONO_ROOT_URL = 'https://kemono.example.com/mirror/'; process.env.COOMER_ROOT_URL = 'https://coomer.example.com/mirror///'; const { config } = await import('./config'); expect(config.kemono.assetsUrl).toBe('https://img.kemono.example.com/mirror'); expect(config.coomer.assetsUrl).toBe('https://img.coomer.example.com/mirror'); delete process.env.KEMONO_ROOT_URL; delete process.env.COOMER_ROOT_URL; }); it('Kemono and Coomer asset URL subdomains', async () => { process.env.KEMONO_ASSETS_URL = 'https://assets.kemono.example.com/'; process.env.COOMER_ASSETS_URL = 'https://assets.coomer.example.com///'; const { config } = await import('./config'); expect(config.kemono.assetsUrl).toBe('https://assets.kemono.example.com'); expect(config.coomer.assetsUrl).toBe('https://assets.coomer.example.com'); delete process.env.KEMONO_ASSETS_URL; delete process.env.COOMER_ASSETS_URL; }); it('Kemono and Coomer asset URL path prefixes', async () => { process.env.KEMONO_ASSETS_URL = 'https://kemono.example.com/assets/'; process.env.COOMER_ASSETS_URL = 'https://coomer.example.com/assets///'; const { config } = await import('./config'); expect(config.kemono.assetsUrl).toBe('https://kemono.example.com/assets'); expect(config.coomer.assetsUrl).toBe('https://coomer.example.com/assets'); delete process.env.KEMONO_ASSETS_URL; delete process.env.COOMER_ASSETS_URL; }); it('remote config', async () => { process.env.REMOTE_CONFIG = 'http://rsshub.test/config'; const { config } = await import('./config'); await vi.waitFor(() => { expect(config.ua).toBe('test'); }); delete process.env.REMOTE_CONFIG; }); }); describe('config remote errors', () => { afterEach(() => { vi.restoreAllMocks(); }); it('logs when remote config returns empty', async () => { process.env.REMOTE_CONFIG = 'http://rsshub.test/empty'; server.use(http.get('http://rsshub.test/empty', () => HttpResponse.json(null))); vi.resetModules(); const { default: logger } = await import('@/utils/logger'); const errorSpy = vi.spyOn(logger, 'error').mockReturnValue(logger); await import('@/config'); await vi.waitFor(() => { expect(errorSpy).toHaveBeenCalledWith('Remote config load failed.'); }); delete process.env.REMOTE_CONFIG; }); it('logs when remote config throws', async () => { process.env.REMOTE_CONFIG = 'http://rsshub.test/fail'; server.use(http.get('http://rsshub.test/fail', () => HttpResponse.error())); vi.resetModules(); const { default: logger } = await import('@/utils/logger'); const errorSpy = vi.spyOn(logger, 'error').mockReturnValue(logger); await import('@/config'); await vi.waitFor(() => { expect(errorSpy).toHaveBeenCalledWith('Remote config load failed.', expect.any(Error)); }); delete process.env.REMOTE_CONFIG; }); });