← back to Homesonspec
collectors/meridian-homes/src/extract.test.ts
76 lines
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { sha256 } from "@homesonspec/collectors-common";
import { meridianHomesAdapter } from "./index";
const FIXTURE_DIR = join(import.meta.dirname, "../../../fixtures/raw-pages/meridian-homes");
const EXPECTED_PATH = join(import.meta.dirname, "../../../fixtures/expected-records/meridian-homes.json");
interface ExpectedRecord {
entityType: string;
canonicalHints?: Record<string, unknown>;
values: Record<string, unknown>;
}
interface ExpectedFile {
pages: Record<string, ExpectedRecord[]>;
}
async function extractPage(file: string) {
const body = await readFile(join(FIXTURE_DIR, file));
const firstLine = body.toString("utf8").split("\n", 1)[0] ?? "";
const url = firstLine.match(/source-url:\s*(\S+)/)?.[1] ?? `fixture://${file}`;
return meridianHomesAdapter.extract({
url,
retrievedAt: new Date().toISOString(),
contentType: "text/html",
body,
contentHash: sha256(body),
});
}
describe("meridian-homes extractor vs hand-authored expected records", async () => {
const expected: ExpectedFile = JSON.parse(await readFile(EXPECTED_PATH, "utf8"));
for (const [file, expectedRecords] of Object.entries(expected.pages)) {
it(`${file} extracts the contracted records exactly`, async () => {
const { records, errors } = await extractPage(file);
expect(errors).toEqual([]);
expect(records).toHaveLength(expectedRecords.length);
records.forEach((record, i) => {
const want = expectedRecords[i]!;
expect(record.entityType).toBe(want.entityType);
if (want.canonicalHints) {
for (const [key, value] of Object.entries(want.canonicalHints)) {
expect(record.canonicalHints[key as keyof typeof record.canonicalHints], `hint ${key}`).toBe(value);
}
}
for (const [field, value] of Object.entries(want.values)) {
expect(record.fields[field]?.value, `${file} field ${field}`).toEqual(value);
}
});
});
}
it("every extracted field carries a sourceUrl and confidence", async () => {
for (const file of Object.keys(expected.pages)) {
const { records } = await extractPage(file);
for (const record of records) {
for (const [field, envelope] of Object.entries(record.fields)) {
expect(envelope.sourceUrl, `${file}/${field} sourceUrl`).toMatch(/^https?:\/\/|^fixture:\/\//);
expect(envelope.confidence, `${file}/${field} confidence`).toBeGreaterThanOrEqual(0);
expect(envelope.confidence).toBeLessThanOrEqual(1);
}
}
}
});
it("null values (missing from source) always have confidence 0 — never guessed", async () => {
const { records } = await extractPage("03-home-124-silverleaf.html");
const home = records[0]!;
expect(home.fields.price?.value).toBeNull();
expect(home.fields.price?.confidence).toBe(0);
});
});