Add purpose-aware property assessor intake
This commit is contained in:
@@ -47,32 +47,120 @@ const samplePublicRecords: PublicRecordsResolution = {
|
||||
}
|
||||
};
|
||||
|
||||
test("assessProperty auto-enriches public-record data from address and asks for recipient email", async () => {
|
||||
test("assessProperty asks for assessment purpose before building a decision-grade assessment", async () => {
|
||||
const result = await assessProperty(
|
||||
{
|
||||
address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
|
||||
listingSourceUrl: "https://www.zillow.com/homedetails/example",
|
||||
listingGeoId: "233290",
|
||||
parcelId: "14069438"
|
||||
},
|
||||
{
|
||||
resolvePublicRecordsFn: async () => samplePublicRecords
|
||||
address: "4141 Whiteley Dr, Corpus Christi, TX 78418"
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.needsAssessmentPurpose, true);
|
||||
assert.equal(result.needsRecipientEmails, false);
|
||||
assert.equal(result.outputPath, null);
|
||||
assert.match(result.message, /assessment purpose/i);
|
||||
assert.equal(result.reportPayload, null);
|
||||
});
|
||||
|
||||
test("assessProperty auto-discovers listing sources, runs Zillow photos first, and asks for recipient email", async () => {
|
||||
const result = await assessProperty(
|
||||
{
|
||||
address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
|
||||
assessmentPurpose: "investment property",
|
||||
listingGeoId: "233290",
|
||||
parcelId: "14069438"
|
||||
},
|
||||
{
|
||||
resolvePublicRecordsFn: async () => samplePublicRecords,
|
||||
discoverListingSourcesFn: async () => ({
|
||||
attempts: [
|
||||
"Zillow discovery located a property page from the address.",
|
||||
"HAR discovery located a property page from the address."
|
||||
],
|
||||
zillowUrl:
|
||||
"https://www.zillow.com/homedetails/4141-Whiteley-Dr-Corpus-Christi-TX-78418/2103723704_zpid/",
|
||||
harUrl:
|
||||
"https://www.har.com/homedetail/4141-whiteley-dr-corpus-christi-tx-78418/14069438"
|
||||
}),
|
||||
extractPhotoDataFn: async (source, url) => ({
|
||||
source,
|
||||
requestedUrl: url,
|
||||
finalUrl: url,
|
||||
expectedPhotoCount: 29,
|
||||
complete: true,
|
||||
photoCount: 29,
|
||||
imageUrls: ["https://photos.example/1.jpg", "https://photos.example/2.jpg"],
|
||||
notes: [`${source} extractor succeeded.`]
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.needsAssessmentPurpose, false);
|
||||
assert.equal(result.needsRecipientEmails, true);
|
||||
assert.equal(result.outputPath, null);
|
||||
assert.match(result.message, /target email/i);
|
||||
assert.equal(result.reportPayload.subjectProperty?.address, samplePublicRecords.matchedAddress);
|
||||
assert.equal(result.reportPayload.publicRecords?.jurisdiction, "Nueces County Appraisal District");
|
||||
assert.equal(result.reportPayload.publicRecords?.accountNumber, "14069438");
|
||||
assert.equal(result.reportPayload.photoReview?.status, "not completed");
|
||||
assert.equal(result.reportPayload?.subjectProperty?.address, samplePublicRecords.matchedAddress);
|
||||
assert.equal(result.reportPayload?.publicRecords?.jurisdiction, "Nueces County Appraisal District");
|
||||
assert.equal(result.reportPayload?.publicRecords?.accountNumber, "14069438");
|
||||
assert.equal(result.reportPayload?.photoReview?.status, "completed");
|
||||
assert.equal(result.reportPayload?.photoReview?.source, "zillow");
|
||||
assert.equal(result.reportPayload?.photoReview?.photoCount, 29);
|
||||
assert.match(
|
||||
String(result.reportPayload.verdict?.offerGuidance),
|
||||
/listing, photo, comp, and carry analysis still required/i
|
||||
String(result.reportPayload?.verdict?.offerGuidance),
|
||||
/investment property/i
|
||||
);
|
||||
assert.match(
|
||||
String(result.reportPayload?.carryView?.[0]),
|
||||
/income property/i
|
||||
);
|
||||
assert.deepEqual(result.reportPayload?.recipientEmails, []);
|
||||
});
|
||||
|
||||
test("assessProperty falls back to HAR when Zillow photo extraction fails", async () => {
|
||||
const result = await assessProperty(
|
||||
{
|
||||
address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
|
||||
assessmentPurpose: "vacation home"
|
||||
},
|
||||
{
|
||||
resolvePublicRecordsFn: async () => samplePublicRecords,
|
||||
discoverListingSourcesFn: async () => ({
|
||||
attempts: ["Address-based discovery found Zillow and HAR candidates."],
|
||||
zillowUrl:
|
||||
"https://www.zillow.com/homedetails/4141-Whiteley-Dr-Corpus-Christi-TX-78418/2103723704_zpid/",
|
||||
harUrl:
|
||||
"https://www.har.com/homedetail/4141-whiteley-dr-corpus-christi-tx-78418/14069438"
|
||||
}),
|
||||
extractPhotoDataFn: async (source, url) => {
|
||||
if (source === "zillow") {
|
||||
throw new Error(`zillow failed for ${url}`);
|
||||
}
|
||||
return {
|
||||
source,
|
||||
requestedUrl: url,
|
||||
finalUrl: url,
|
||||
expectedPhotoCount: 29,
|
||||
complete: true,
|
||||
photoCount: 29,
|
||||
imageUrls: ["https://photos.har.example/1.jpg"],
|
||||
notes: ["HAR extractor succeeded after Zillow failed."]
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.reportPayload?.photoReview?.status, "completed");
|
||||
assert.equal(result.reportPayload?.photoReview?.source, "har");
|
||||
assert.match(
|
||||
String(result.reportPayload?.photoReview?.attempts?.join(" ")),
|
||||
/zillow/i
|
||||
);
|
||||
assert.match(
|
||||
String(result.reportPayload?.verdict?.offerGuidance),
|
||||
/vacation home/i
|
||||
);
|
||||
assert.deepEqual(result.reportPayload.recipientEmails, []);
|
||||
});
|
||||
|
||||
test("assessProperty renders a PDF when recipient email is present", async () => {
|
||||
@@ -80,15 +168,22 @@ test("assessProperty renders a PDF when recipient email is present", async () =>
|
||||
const result = await assessProperty(
|
||||
{
|
||||
address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
|
||||
assessmentPurpose: "rental for my daughter in college",
|
||||
recipientEmails: ["buyer@example.com"],
|
||||
output: outputPath
|
||||
},
|
||||
{
|
||||
resolvePublicRecordsFn: async () => samplePublicRecords
|
||||
resolvePublicRecordsFn: async () => samplePublicRecords,
|
||||
discoverListingSourcesFn: async () => ({
|
||||
attempts: ["No listing sources discovered from the address."],
|
||||
zillowUrl: null,
|
||||
harUrl: null
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.needsAssessmentPurpose, false);
|
||||
assert.equal(result.needsRecipientEmails, false);
|
||||
assert.equal(result.outputPath, outputPath);
|
||||
const stat = await fs.promises.stat(outputPath);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ReportValidationError, renderReportPdf } from "../src/report-pdf.js";
|
||||
|
||||
const samplePayload = {
|
||||
recipientEmails: ["buyer@example.com"],
|
||||
assessmentPurpose: "investment property",
|
||||
reportTitle: "Property Assessment Report",
|
||||
subjectProperty: {
|
||||
address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
|
||||
|
||||
Reference in New Issue
Block a user