← back to Homesonspec
collectors/chafin-communities/selftest-live.mts
84 lines
/**
* Bounded live self-test — chafin-communities adapter.
* Fetches up to 3 community pages, extracts records, reports field coverage.
* Pass criterion: ≥6/10 core field coverage (price, beds, baths, lot, id, city,
* state, status) across all homes found.
*
* Run:
* CHAFIN_COMMUNITY_LIMIT=3 node_modules/.bin/tsx selftest-live.mts
*/
process.env["CHAFIN_COMMUNITY_LIMIT"] = "3";
process.env["CHAFIN_HOME_LIMIT"] = "20";
import { chafinCommunitiesAdapter } from "./src/index.js";
import type { SourceRegistryEntry } from "@homesonspec/collectors-common";
const registry: SourceRegistryEntry = {
key: "chafin-communities-site",
collectionMethod: "CRAWL",
rateLimitRpm: 10,
mediaRights: "NONE",
};
const ctx = { mode: "live" as const, registry };
let pages = 0;
let communityRecords = 0;
let homeRecords = 0;
let errors = 0;
const cov = {
price: 0, beds: 0, baths: 0, lot: 0, id: 0,
city: 0, state: 0, status: 0,
};
console.log("[chafin-live] starting bounded crawl (limit=3 communities)...");
for await (const page of chafinCommunitiesAdapter.fetch(ctx)) {
pages++;
const out = chafinCommunitiesAdapter.extract(page);
errors += out.errors.length;
for (const err of out.errors) console.warn(" error:", err);
for (const rec of out.records) {
if (rec.entityType === "community") {
communityRecords++;
} else if (rec.entityType === "inventory_home") {
homeRecords++;
const f = rec.fields as Record<string, { value: unknown }>;
if (f["price"]?.value != null) cov.price++;
if (f["beds"]?.value != null) cov.beds++;
if (f["bathsTotal"]?.value != null) cov.baths++;
if (f["lotNumber"]?.value != null) cov.lot++;
if (f["builderInventoryId"]?.value != null) cov.id++;
if (f["city"]?.value != null) cov.city++;
if (f["state"]?.value != null) cov.state++;
if (f["constructionStatus"]?.value != null) cov.status++;
}
}
console.log(
` page ${pages}: ${page.url} → ${out.records.filter((r) => r.entityType === "inventory_home").length} homes`,
);
}
const n = homeRecords;
function pct(v: number) { return n ? `${Math.round(100 * v / n)}%` : "n/a"; }
console.log(`\n[chafin-live] RESULTS`);
console.log(` pages=${pages} communities=${communityRecords} homes=${n} errors=${errors}`);
console.log(` price=${pct(cov.price)} beds=${pct(cov.beds)} baths=${pct(cov.baths)}`);
console.log(` lot=${pct(cov.lot)} id=${pct(cov.id)} city=${pct(cov.city)} state=${pct(cov.state)} status=${pct(cov.status)}`);
// Pass criterion: at least some homes found + 6/8 fields at >50%
const fieldPcts = Object.values(cov).map((v) => (n ? v / n : 0));
const above50 = fieldPcts.filter((p) => p > 0.5).length;
if (n === 0) {
console.error("\nFAIL: no homes found (site may be blocking; check manually)");
process.exit(1);
} else if (above50 < 6) {
console.error(`\nFAIL: only ${above50}/8 fields above 50% coverage (need 6)`);
process.exit(1);
} else {
console.log(`\nPASS: ${n} homes, ${above50}/8 fields >50% coverage`);
}