Fix audit generation and enrichment fallback

This commit is contained in:
2026-06-07 23:03:57 +02:00
parent e9463e8ef2
commit 470fb0f348
10 changed files with 2190 additions and 138 deletions

View File

@@ -195,7 +195,7 @@ test("queueLeadEnrichment uses lead-aware run index and does not use fixed-size
assert.equal(hasPattern(queueBody, /take\(50\)/), false, "No fixed-size .take(50) window in dedupe queries.");
});
test("website enrichment action uses Chromium desktop/mobile devices and runtime Playwright import", () => {
test("website enrichment action can still use Chromium desktop/mobile devices when configured", () => {
assert.equal(
hasPattern(
actionSource,
@@ -224,14 +224,6 @@ test("website enrichment action uses Chromium desktop/mobile devices and runtime
true,
"Action should reference TASK8_BROWSER_ASSET_URL when loading browser assets",
);
assert.equal(
hasPattern(
actionSource,
/TASK8_BROWSER_ASSET_URL[\s\S]{0,240}(throw|Error|required|missing|not configured|configured|konfiguriert|setze)/i,
),
true,
"Action should surface a clear error when the browser asset URL is not configured",
);
assert.equal(
hasPattern(actionSource, /import\("@sparticuz\/chromium"\)/),
false,
@@ -271,6 +263,84 @@ test("website enrichment action uses Chromium desktop/mobile devices and runtime
);
});
test("processLeadEnrichment uses browserless enrichment when Chromium source is missing", () => {
const processBody = extractExportSource(actionSource, "processLeadEnrichment");
const fallbackGuardIndex = processBody.indexOf("if (!getChromiumExecutableSource())");
const playwrightLoadIndex = processBody.indexOf("loadPlaywrightModules()");
assert.notEqual(
fallbackGuardIndex,
-1,
"processLeadEnrichment should branch before Playwright bootstrap when no Chromium source is configured.",
);
assert.equal(
fallbackGuardIndex < playwrightLoadIndex,
true,
"The missing-Chromium fallback should run before loadPlaywrightModules().",
);
assert.equal(
hasPattern(
processBody,
/if \(!getChromiumExecutableSource\(\)\)\s*\{[\s\S]*processLeadEnrichmentWithoutBrowser\(/,
),
true,
"Missing browser asset config should call the browserless enrichment path instead of throwing.",
);
assert.equal(
hasPattern(actionSource, /async function processLeadEnrichmentWithoutBrowser\(/),
true,
"Action should expose a dedicated browserless enrichment helper.",
);
assert.equal(
hasPattern(
actionSource,
/Chromium ist nicht konfiguriert; Website-Enrichment nutzt browserlosen Fetch-Fallback\./,
),
true,
"The fallback should make the degraded mode visible in run events.",
);
});
test("browserless website enrichment persists crawl evidence without screenshots", () => {
const fallbackSource = actionSource.slice(
actionSource.indexOf("async function processLeadEnrichmentWithoutBrowser("),
);
assert.equal(
hasPattern(fallbackSource, /crawlPageWithoutBrowser\(/),
true,
"Browserless enrichment should fetch pages directly instead of launching Playwright.",
);
assert.equal(
hasPattern(
actionSource,
/function crawlPageWithoutBrowser[\s\S]*extractContactSignalsFromHtmlLikeText\(/,
),
true,
"Browserless enrichment should still extract contact signals from fetched page content.",
);
assert.equal(
hasPattern(fallbackSource, /internal\.websiteEnrichment\.persistLeadEnrichmentResult/),
true,
"Browserless enrichment should persist pages, links, email candidates, and technical checks.",
);
assert.equal(
hasPattern(fallbackSource, /screenshots:\s*\[\]/),
true,
"Browserless enrichment should not pretend screenshots exist.",
);
assert.equal(
hasPattern(fallbackSource, /status:\s*"succeeded"/),
true,
"A successful browserless crawl should finish the enrichment run as succeeded.",
);
assert.equal(
hasPattern(fallbackSource, /internal\.pageSpeed\.queueLeadPageSpeedAudit/),
true,
"Browserless enrichment should keep the downstream PageSpeed handoff.",
);
});
test("website enrichment action invalidates stale @sparticuz/chromium-min cache when source changes", () => {
assert.equal(
hasPattern(actionSource, /CHROMIUM_SOURCE_MARKER_FILE/),
@@ -518,7 +588,7 @@ test("failure handling marks run as failed and writes lead-facing reason", () =>
assert.equal(
hasPattern(
actionSource,
/runMutation\(\s*api\.runs\.appendEvent[\s\S]*?level:\s*"error"[\s\S]*?message:\s*"Website-Enrichment fehlgeschlagen/,
/runMutation\(\s*internal\.runs\.appendEventInternal[\s\S]*?level:\s*"error"[\s\S]*?message:\s*"Website-Enrichment fehlgeschlagen/,
),
true,
"Action should append a visible error event on failure",
@@ -541,6 +611,66 @@ test("failure handling marks run as failed and writes lead-facing reason", () =>
);
});
test("website enrichment action treats Playwright close operations as best-effort cleanup", () => {
assert.equal(
hasPattern(actionSource, /function isPlaywrightTargetClosedError\(/),
true,
"Action should centralize recognition of Playwright target/page/context/browser closed errors.",
);
assert.equal(
hasPattern(actionSource, /async function closePlaywrightResourceSafely\(/),
true,
"Action should centralize best-effort Playwright resource cleanup.",
);
assert.equal(
hasPattern(
actionSource,
/isPlaywrightTargetClosedError[\s\S]*Target page, context or browser has been closed/,
),
true,
"Target page/context/browser closed errors should be recognized explicitly.",
);
assert.equal(
hasPattern(
actionSource,
/closePlaywrightResourceSafely[\s\S]*console\.warn\(/,
),
true,
"Unexpected Playwright close failures should be swallowed with a warning.",
);
const directUnsafeClosePatterns = [
/finally\s*\{\s*await page\.close\(\);?\s*\}/,
/finally\s*\{[\s\S]*await desktopContext\.close\(\);/,
/finally\s*\{[\s\S]*await mobileContext\.close\(\);/,
/finally\s*\{[\s\S]*await browser\.close\(\);/,
];
for (const pattern of directUnsafeClosePatterns) {
assert.equal(
hasPattern(actionSource, pattern),
false,
`Playwright cleanup should not await close() directly in finally: ${pattern}`,
);
}
const safeCloseCalls = [
/closePlaywrightResourceSafely\(\s*page,\s*"homepage screenshot page"/,
/closePlaywrightResourceSafely\(\s*page,\s*"crawl page"/,
/closePlaywrightResourceSafely\(\s*desktopContext,\s*"desktop browser context"/,
/closePlaywrightResourceSafely\(\s*mobileContext,\s*"mobile browser context"/,
/closePlaywrightResourceSafely\(\s*browser,\s*"browser"/,
];
for (const pattern of safeCloseCalls) {
assert.equal(
hasPattern(actionSource, pattern),
true,
`Expected Playwright cleanup to use safe close helper: ${pattern}`,
);
}
});
test("website enrichment enforces TASK-8 crawler limits and runtime timeboxes", () => {
assert.equal(
hasPattern(actionSource, /TASK8_CRAWL_TIMEOUT_MS/g),
@@ -666,7 +796,7 @@ test("processLeadEnrichment records warning on PageSpeed queue failure and conti
assert.equal(
hasPattern(
processBody,
/try\s*\{[\s\S]*internal\.pageSpeed\.queueLeadPageSpeedAudit[\s\S]*\}\s*catch\s*\([^)]*\)\s*\{[\s\S]*api\.runs\.appendEvent[\s\S]*level:\s*"warning"/,
/try\s*\{[\s\S]*internal\.pageSpeed\.queueLeadPageSpeedAudit[\s\S]*\}\s*catch\s*\([^)]*\)\s*\{[\s\S]*internal\.runs\.appendEventInternal[\s\S]*level:\s*"warning"/,
),
true,
"Queueing PageSpeed should be wrapped in warning-safe try/catch",