diff --git a/e2e/tests/generation-live.spec.ts b/e2e/tests/generation-live.spec.ts index 7b47408..491da5e 100644 --- a/e2e/tests/generation-live.spec.ts +++ b/e2e/tests/generation-live.spec.ts @@ -120,6 +120,7 @@ test.describe('Live generation with OpenAI', () => { test('full generation pipeline produces valid synthesis', async ({ page, + request, }) => { test.setTimeout(180_000); @@ -141,6 +142,8 @@ test.describe('Live generation with OpenAI', () => { ai_provider: 'openai', ai_model: 'gpt-4o-mini', ai_model_writing: 'gpt-4o-mini', + use_llm_for_source_links: false, + use_llm_for_article_extraction: false, }); expect(settingsResp.status).toBe(200); @@ -222,5 +225,47 @@ test.describe('Live generation with OpenAI', () => { expect(item.summary.trim().length).toBeGreaterThan(50); } } + + // ═══════════════════════════════════════════════════════════════ + // Comprehensive synthesis validation + // ═══════════════════════════════════════════════════════════════ + const allUrls: string[] = []; + const domainCounts: Record = {}; + + for (const section of synthesis.sections) { + for (const item of section.items) { + allUrls.push(item.url); + try { + const domain = new URL(item.url).hostname; + domainCounts[domain] = (domainCounts[domain] || 0) + 1; + } catch { + // URL parse failure — already caught by earlier assertions + } + } + + // Category article count check (including Autre) + expect(section.items.length).toBeLessThanOrEqual(4); // max_items_per_category + } + + // No duplicate URLs across all sections + const uniqueUrls = new Set(allUrls); + expect(uniqueUrls.size).toBe(allUrls.length); + + // No domain exceeds max_articles_per_source (3) + for (const [domain, count] of Object.entries(domainCounts)) { + expect(count).toBeLessThanOrEqual(3); + } + + // Verify a sample of article links actually work (Playwright request API, no CORS issues) + const sampleUrls = allUrls.slice(0, 3); + for (const articleUrl of sampleUrls) { + try { + const resp = await request.head(articleUrl); + expect(resp.status()).toBeGreaterThanOrEqual(200); + expect(resp.status()).toBeLessThan(400); + } catch { + // Some sites block HEAD requests — skip rather than fail + } + } }); });