Files
RSSHub/lib/middleware/template.test.ts
T
DIYgod 81ff849337 feat(tests): add comprehensive unit tests for various utility functions and views (#20779)
* feat(tests): add comprehensive unit tests for various utility functions and views

- Implement tests for cache utility to ensure proper behavior with no cache and TTL keys.
- Add tests for common utilities including string manipulation and path handling.
- Introduce tests for directory import functionality to validate file imports and pattern matching.
- Create tests for git hash retrieval to handle fallback scenarios.
- Develop tests for deprecated got utility to verify response handling and retry logic.
- Enhance got utility tests to include request hooks and search parameter handling.
- Mock header generator tests to validate user agent handling.
- Expand helpers tests to cover current path retrieval and duration parsing.
- Implement tests for ofetch utility to ensure proper proxy handling and logging.
- Add OpenTelemetry metric tests to validate metric serialization.
- Create proxy tests to verify multi-proxy selection and failure handling.
- Introduce request rewriter tests to validate fetch and get wrapper functionality.
- Add timezone utility tests to handle various input types.
- Implement view tests for Atom and RSS rendering to ensure correct output.
- Create index view tests to validate debug information display based on configuration.

* refactor: remove deprecated got implementation and associated tests

* test: expand coverage for api, middleware, and utils
2025-12-29 17:34:48 +08:00

143 lines
4.7 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { config } from '@/config';
import template from '@/middleware/template';
const createCtx = (query: Record<string, string | undefined>, data: any, extra: Record<string, unknown> = {}) => {
const store = new Map<string, unknown>([['data', data], ...Object.entries(extra)]);
return {
req: {
query: (key: string) => query[key],
url: 'http://localhost/rss',
},
get: (key: string) => store.get(key),
set: (key: string, value: unknown) => store.set(key, value),
json: vi.fn((payload) => payload),
html: vi.fn((payload) => payload),
render: vi.fn((payload) => payload),
body: vi.fn((payload) => payload),
redirect: vi.fn((url: string, status: number) => ({ url, status })),
header: vi.fn(),
res: { headers: new Headers() },
};
};
describe('template middleware', () => {
it('returns debug json when requested', async () => {
const originalDebug = config.debugInfo;
config.debugInfo = true;
const ctx = createCtx({ format: 'debug.json' }, { item: [] }, { json: { ok: true } });
const result = await template(ctx as any, async () => {});
expect(result).toEqual({ ok: true });
expect(ctx.json).toHaveBeenCalled();
config.debugInfo = originalDebug;
});
it('returns api data without rendering', async () => {
const ctx = createCtx({}, null, { apiData: { ok: true } });
const result = await template(ctx as any, async () => {});
expect(result).toEqual({ ok: true });
expect(ctx.json).toHaveBeenCalledWith({ ok: true });
});
it('renders debug html snippet when requested', async () => {
const originalDebug = config.debugInfo;
config.debugInfo = true;
const ctx = createCtx({ format: '0.debug.html' }, { item: [{ description: 'Hello' }] });
const result = await template(ctx as any, async () => {});
expect(result).toBe('Hello');
expect(ctx.html).toHaveBeenCalled();
config.debugInfo = originalDebug;
});
it('trims long titles and normalizes authors', async () => {
const originalLimit = config.titleLengthLimit;
config.titleLengthLimit = 3;
const data = {
title: 'Feed',
item: [
{
title: 'ABCDE',
author: [{ name: ' Alice ' }, { name: 'Bob ' }],
itunes_duration: '65',
},
],
};
const ctx = createCtx({ format: 'rss' }, data);
await template(ctx as any, async () => {});
expect(data.item[0].title).toBe('ABC...');
expect(data.item[0].author).toBe('Alice, Bob');
expect(data.item[0].itunes_duration).toBe('0:01:05');
config.titleLengthLimit = originalLimit;
});
it('clears invalid dates for non-rss formats', async () => {
const data = {
title: 'Test',
item: [
{
title: 'Item',
pubDate: 'invalid-date',
updated: 'invalid-updated',
},
],
};
const ctx = createCtx({ format: 'json' }, data);
await template(ctx as any, async () => {});
expect(data.item[0].pubDate).toBe('');
expect(data.item[0].updated).toBe('');
});
it('returns redirect response when redirect is set', async () => {
const ctx = createCtx({}, { item: [] }, { redirect: 'https://example.com' });
const result = await template(ctx as any, async () => {});
expect(result).toEqual({ url: 'https://example.com', status: 301 });
expect(ctx.redirect).toHaveBeenCalledWith('https://example.com', 301);
});
it('renders rss3 output', async () => {
const data = {
title: 'Test',
item: [
{
title: 'Item',
link: 'https://example.com/item',
},
],
};
const ctx = createCtx({ format: 'rss3' }, data);
const result = await template(ctx as any, async () => {});
expect(ctx.json).toHaveBeenCalled();
expect(result).toHaveProperty('data');
});
it('renders atom output', async () => {
const data = {
title: 'Test',
item: [
{
title: 'Item',
link: 'https://example.com/item',
},
],
};
const ctx = createCtx({ format: 'atom' }, data);
await template(ctx as any, async () => {});
expect(ctx.render).toHaveBeenCalled();
});
});