c0py: initial scaffolding for website registry tool
CI (SIAX Cloud) / security (push) Successful in 12s
CI (SIAX Cloud) / contracts (push) Failing after 12s
CI (SIAX Cloud) / quality (push) Failing after 8s
Deploy to Coolify / deploy (push) Failing after 2s

This commit is contained in:
2026-09-16 13:15:21 +02:00
commit e6cc719f37
58 changed files with 1131 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import { describe, it, expect } from "vitest";
import { analyzePages } from "../src/registry/analyzer";
import type { Page } from "@siax/c0py-types";
const pages: Page[] = [
{
id: "1",
registryId: "r1",
url: "https://example.com/",
path: "/",
title: "Home",
description: null,
statusCode: 200,
contentType: "text/html",
discoveredAt: new Date(),
analysis: null,
},
{
id: "2",
registryId: "r1",
url: "https://example.com/about",
path: "/about",
title: "About",
description: null,
statusCode: 200,
contentType: "text/html",
discoveredAt: new Date(),
analysis: null,
},
{
id: "3",
registryId: "r1",
url: "https://example.com/missing",
path: "/missing",
title: null,
description: null,
statusCode: 404,
contentType: "text/html",
discoveredAt: new Date(),
analysis: null,
},
];
describe("analyzePages", () => {
it("counts pages correctly", () => {
const result = analyzePages(pages);
expect(result.totalPages).toBe(3);
expect(result.brokenLinks).toBe(1);
expect(result.hasBrokenLinks).toBe(true);
expect(result.uniqueDomains).toBe(1);
});
it("aggregates status codes", () => {
const result = analyzePages(pages);
expect(result.statusCodes[200]).toBe(2);
expect(result.statusCodes[404]).toBe(1);
});
it("handles empty input", () => {
const result = analyzePages([]);
expect(result.totalPages).toBe(0);
expect(result.avgStatusCode).toBe(0);
expect(result.hasBrokenLinks).toBe(false);
});
});
+16
View File
@@ -0,0 +1,16 @@
import { describe, it, expect } from "vitest";
import { Crawler } from "../src/crawler/crawler";
describe("Crawler", () => {
it("instantiates with options", () => {
const c = new Crawler({ maxPages: 10, timeoutMs: 5000 });
expect(c).toBeDefined();
});
it("crawls a real page", async () => {
const c = new Crawler({ maxPages: 1, timeoutMs: 10000 });
const pages = await c.crawl("https://example.com");
expect(pages.length).toBeGreaterThanOrEqual(1);
expect(pages[0].statusCode).toBe(200);
}, 15000);
});
+24
View File
@@ -0,0 +1,24 @@
import { describe, it, expect } from "vitest";
import { assembleRegistry } from "../src/registry/registry";
describe("assembleRegistry", () => {
it("creates a registry with pages", () => {
const result = assembleRegistry("https://example.com", "Example", [
{
url: "https://example.com/",
path: "/",
title: "Home",
description: null,
statusCode: 200,
contentType: "text/html",
links: [],
},
]);
expect(result.registry.url).toBe("https://example.com");
expect(result.registry.name).toBe("Example");
expect(result.pages).toHaveLength(1);
expect(result.pages[0].id).toBeDefined();
expect(result.pages[0].registryId).toBe(result.registry.id);
});
});
+1
View File
@@ -0,0 +1 @@
testTimeout: 10000,