diff --git a/src/scan.ts b/src/scan.ts index 094d4ad..fa616ef 100644 --- a/src/scan.ts +++ b/src/scan.ts @@ -73,7 +73,7 @@ export async function scanSite(config: SentinelConfig, site: SiteConfig, options } const emailRequired = - !options.dryRun && (hasFatalIssues(issues) || blackout || (!baseline && changes.length > 0)); + !options.dryRun && (hasActiveIssues(issues) || blackout || (!baseline && changes.length > 0)); const result: ScanResult = { siteId: site.id, siteName: site.name, @@ -222,6 +222,10 @@ function hasFatalIssues(issues: ScanIssue[]): boolean { return issues.some((issue) => issue.fatal); } +function hasActiveIssues(issues: ScanIssue[]): boolean { + return issues.some((issue) => !issue.ignored); +} + /** * Vero quando un monitor che aveva già una baseline non raccoglie più nulla: * blocco lato sito, DNS o rete, mai un sito realmente svuotato. @@ -243,7 +247,7 @@ export function collectRemovals( issues: ScanIssue[], resources: FetchedResource[] ): ScanChange[] { - if (issues.length > 0 || isScanBlackout(siteState, resources)) return []; + if (hasActiveIssues(issues) || isScanBlackout(siteState, resources)) return []; const knownUrls = Object.keys(siteState.urls); const removals: ScanChange[] = []; diff --git a/test/removals.test.ts b/test/removals.test.ts index 3ea4f4c..023c1be 100644 --- a/test/removals.test.ts +++ b/test/removals.test.ts @@ -78,4 +78,15 @@ describe("collectRemovals", () => { expect(removals).toEqual([]); }); + + it("deduce le rimozioni quando restano solo avvisi ignorati", () => { + const removals = collectRemovals( + stateWith(["https://example.com/a", "https://example.com/vecchia"]), + new Set(["https://example.com/a"]), + [{ url: "https://example.com/legacy", message: "HTTP 404", fatal: false, ignored: true }], + [resource] + ); + + expect(removals.map((change) => change.url)).toEqual(["https://example.com/vecchia"]); + }); }); diff --git a/test/scan.test.ts b/test/scan.test.ts new file mode 100644 index 0000000..dd14c09 --- /dev/null +++ b/test/scan.test.ts @@ -0,0 +1,73 @@ +import { mkdtemp, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { scanSite } from "../src/scan.js"; +import type { SentinelConfig, SiteConfig } from "../src/types.js"; + +const { sendScanEmail } = vi.hoisted(() => ({ sendScanEmail: vi.fn() })); + +vi.mock("../src/email.js", () => ({ sendScanEmail })); + +const tempDirs: string[] = []; + +afterEach(async () => { + vi.unstubAllGlobals(); + sendScanEmail.mockReset(); + await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))); +}); + +describe("scanSite", () => { + it("richiede l'email per una sitemap malformata", async () => { + const rootDir = await mkdtemp(path.join(os.tmpdir(), "sentinel-scan-")); + tempDirs.push(rootDir); + + const site: SiteConfig = { + id: "test", + name: "Test", + enabled: true, + sitemapUrls: ["https://example.com/sitemap.xml"], + roots: ["https://example.com/"], + crawl: { maxDepth: 0, maxUrls: 10, timeoutMs: 1000, userAgent: "Sentinel test" }, + includeFileExtensions: [], + trackingParams: [], + ignoredIssues: [] + }; + const config: SentinelConfig = { + version: 1, + storage: { + dataDir: path.join(rootDir, "data"), + snapshotsDir: path.join(rootDir, "snapshots"), + reportsDir: path.join(rootDir, "reports") + }, + email: { + enabled: true, + defaultProfile: "test", + fromEnv: "FROM", + toEnv: "TO", + subjectPrefix: "[Sentinel]", + profiles: {} + }, + sites: [site] + }; + + vi.stubGlobal( + "fetch", + vi.fn(async (url: string | URL | Request) => { + const value = String(url); + if (value.endsWith("/robots.txt")) return new Response("", { status: 404 }); + if (value.endsWith("/sitemap.xml")) return new Response("<", { status: 200 }); + return new Response("Pagina valida", { + status: 200, + headers: { "content-type": "text/html" } + }); + }) + ); + + const result = await scanSite(config, site, { dryRun: false }); + + expect(result.issues).toMatchObject([{ url: "https://example.com/sitemap.xml", fatal: false }]); + expect(result.emailRequired).toBe(true); + expect(sendScanEmail).toHaveBeenCalledOnce(); + }); +});