import { execFileSync } from 'node:child_process'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { closeDatabase, getDatabase } from './database.js'; import { discardFeedEntries, fetchDueFeeds, fetchFeed, importFeedEntries, listFeedEntries, saveFeedSource } from './feeds.js'; import { getPiece } from './content.js'; vi.mock('node:dns/promises', () => ({ lookup: vi.fn(async () => [{ address: '93.184.216.34', family: 4 }]) })); const validFeed = `Test entry-1Ein Beitraghttps://example.org/post Kurz und gut.

]]>
Sat, 24 Oct 2026 12:00:00 GMT
`; let directory: string; beforeEach(() => { directory = mkdtempSync(path.join(tmpdir(), 'sendewerk-feed-test-')); process.env.SENDEWERK_DB_PATH = path.join(directory, 'test.sqlite'); process.env.SENDEWERK_UPLOAD_DIR = path.join(directory, 'uploads'); execFileSync(process.execPath, ['scripts/migrate.mjs'], { cwd: process.cwd(), env: process.env }); execFileSync(process.execPath, ['scripts/seed.mjs'], { cwd: process.cwd(), env: process.env }); }); afterEach(() => { vi.unstubAllGlobals(); closeDatabase(); delete process.env.SENDEWERK_DB_PATH; delete process.env.SENDEWERK_UPLOAD_DIR; rmSync(directory, { recursive: true, force: true }); }); describe('RSS import inbox', () => { it('keeps one entry across fetches and preserves a discard decision', async () => { vi.stubGlobal( 'fetch', vi.fn( async () => new Response(validFeed, { headers: { 'content-type': 'application/rss+xml' } }) ) ); const feedId = saveFeedSource(null, { name: 'Feed', url: 'https://example.org/feed.xml', isEnabled: true, refreshIntervalMinutes: 60, defaultCategoryId: null }); await fetchFeed(feedId); const [entry] = listFeedEntries(); expect(entry.title).toBe('Ein Beitrag'); expect(discardFeedEntries([entry.id])).toBe(1); await fetchFeed(feedId); expect(listFeedEntries()).toHaveLength(0); expect(listFeedEntries('discarded')).toHaveLength(1); }); it('sanitizes foreign HTML and retains the original source on explicit import', async () => { vi.stubGlobal( 'fetch', vi.fn(async () => new Response(validFeed)) ); const feedId = saveFeedSource(null, { name: 'Feed', url: 'https://example.org/feed.xml', isEnabled: true, refreshIntervalMinutes: 60, defaultCategoryId: 'cat-jc' }); await fetchFeed(feedId); const [entry] = listFeedEntries(); const [pieceId] = importFeedEntries([entry.id], []); const piece = getPiece(pieceId)!; expect(piece.summary).toContain('Kurz und gut.'); expect(piece.summary).not.toContain('