← back to Govarbitrage
src/lib/places.test.ts
459 lines
import { describe, expect, it } from "vitest";
import {
isCommercial,
dedupeByContact,
normalizePhone,
normalizeHost,
relevanceScore,
type AgentResult,
type RawPlace,
} from "./places";
// isCommercial() is the finder's core value prop: precision-first
// commercial-vs-residential classification (the LA 57→28 verified drop).
// These offline tests lock that behavior in — zero live Google Places billing,
// per project rails. The apply order under test (see places.ts):
// 1. off-topic Google type → DROP
// 2. strong commercial name OR allowlisted brand → KEEP (beats residential)
// 3. residential/irrelevant name → DROP
// 4. residential-flavored type → DROP
// 5/6. no commercial evidence → DROP (precision > recall)
function place(name: string, types: string[] = ["real_estate_agency"]): RawPlace {
return { id: "x", displayName: { text: name }, primaryType: types[0], types };
}
describe("isCommercial — KEEP on strong commercial name signals", () => {
it.each([
"Downtown Commercial Real Estate Brokers",
"Metro CRE Advisors",
"Industrial Property Group",
"Prime Office Leasing",
"Retail & Industrial Brokerage",
"Apex Investment Sales",
"Tenant Representation Partners",
"Net Lease Advisors",
"Summit Capital Markets",
"Bay Area Multifamily Group",
"Harbor Income Properties",
])("keeps %s", (name) => {
expect(isCommercial(place(name))).toBe(true);
});
});
describe("isCommercial — KEEP on allowlisted national brokerage brands", () => {
it.each([
"CBRE",
"JLL Los Angeles",
"Cushman & Wakefield",
"Colliers International",
"Marcus & Millichap",
"Lee & Associates",
"Newmark Group",
"Avison Young",
"Hughes Marino",
"NAI Capital",
])("keeps brand %s", (name) => {
expect(isCommercial(place(name))).toBe(true);
});
it("is case-insensitive on brand match", () => {
expect(isCommercial(place("cbre commercial"))).toBe(true);
expect(isCommercial(place("MARCUS AND MILLICHAP"))).toBe(true);
});
});
describe("isCommercial — commercial evidence WINS over a residential word", () => {
it("keeps a name carrying both commercial and residential signals", () => {
// "commercial" (KEEP, step 2) is evaluated before "homes" (DROP, step 3).
expect(isCommercial(place("Commercial & Home Sales Group"))).toBe(true);
});
it("keeps an allowlisted brand even next to a residential word", () => {
expect(isCommercial(place("CBRE Residential Division"))).toBe(true);
});
});
describe("isCommercial — DROP on residential / irrelevant names", () => {
it.each([
"John Smith, Realtor",
"Dream Homes Realty",
"Sunset Home Sales",
"First-Time Buyer Specialists",
"The Home Team",
"Bluewater Property Management",
"City Apartments Locators",
"Acme Mortgage & Home Loans",
"Luxury Condos of Malibu",
"Single Family Realty Team",
])("drops %s", (name) => {
expect(isCommercial(place(name))).toBe(false);
});
});
describe("isCommercial — DROP on generic / no-evidence real-estate results", () => {
it("drops a plain real_estate_agency with no commercial signal", () => {
// Precision-first: Google's real_estate_agency type can't confirm commercial.
expect(isCommercial(place("Smith Real Estate"))).toBe(false);
expect(isCommercial(place("Downtown Realty"))).toBe(false);
});
it("drops a non-real-estate result with no signal either way", () => {
expect(isCommercial(place("Bob's Consulting", ["consultant"]))).toBe(false);
});
});
describe("isCommercial — DROP on off-topic Google place types (regardless of name)", () => {
it.each([
["The Maimon Group", "travel_agency"],
["Cozy Stays", "lodging"],
["Shield Insurance", "insurance_agency"],
["Two Guys Moving", "moving_company"],
["SecureBox Self Storage", "self_storage"],
["Mario's Restaurant", "restaurant"],
])("drops %s tagged %s", (name, type) => {
expect(isCommercial(place(name, [type]))).toBe(false);
});
it("off-topic type beats an otherwise-commercial name", () => {
// A travel_agency mis-tag on a 'commercial'-named place is still dropped.
expect(isCommercial(place("Commercial Getaways", ["travel_agency"]))).toBe(false);
});
// Regression: Google place types are underscore-joined ("furniture_store"), and
// "_" is a JS word char, so the old \bstore\b boundary NEVER fired inside them —
// every compound retail/mall type with a commercial-sounding name leaked through
// (an "Office Furniture Warehouse" tagged furniture_store was kept as a broker).
// Now matched by exact type token + a "*_store" suffix rule.
it.each([
["Office Furniture Warehouse", "furniture_store"],
["Commercial Hardware Supply", "hardware_store"],
["Downtown Office Depot", "office_supply_store"],
["Retail Plaza Shopping Mall", "shopping_mall"],
["Prime Electronics Outlet", "electronics_store"],
["Industrial Home Improvement Center", "home_improvement_store"],
["Commercial Auto Group", "car_dealer"],
["Office Legal Advisors", "lawyer"],
])("drops %s (compound off-topic type %s) despite a commercial-sounding name", (name, type) => {
expect(isCommercial(place(name, [type]))).toBe(false);
});
it("catches an off-topic type buried in the secondary types array", () => {
// primaryType is real_estate_agency but a furniture_store type is also present.
expect(
isCommercial({
id: "x",
displayName: { text: "Commercial Furnishings & Realty" },
primaryType: "real_estate_agency",
types: ["real_estate_agency", "furniture_store"],
}),
).toBe(false);
});
it("does NOT over-drop a legit commercial real_estate_agency (no *_store false hit)", () => {
expect(isCommercial(place("Commercial Real Estate Brokers", ["real_estate_agency"]))).toBe(true);
});
});
// Regression: the "broker" and "finance" verticals overlap, so a Places text
// search for "commercial real estate broker" surfaces WEALTH / FINANCIAL-
// ADVISORY / securities firms. Their names carry finance-ambiguous tokens
// ("investments", "advisors", "capital markets") that ALSO satisfy the generic
// COMMERCIAL_RE keep — so before the fix, "Sterling Financial Advisors" and
// "Vanguard Investment Advisors" were WRONGLY kept as CRE brokers. Now a
// financial-advisory name with NO real-estate anchor is dropped, while a legit
// CRE broker (which carries an RE anchor or an allowlisted brand) is preserved.
describe("isCommercial — DROP on financial-advisory / wealth firms with no RE anchor", () => {
it.each([
"Morgan Stanley Wealth Investments",
"Sterling Financial Advisors",
"Vanguard Investment Advisors",
"Edward Jones Investments",
"Pinnacle Wealth Advisors",
"Fidelity Asset Management",
"Prudential Insurance Advisors",
"Cornerstone Wealth Management",
"Cambridge Investment Management",
"Heritage Retirement Advisors",
])("drops finance firm %s (no real-estate anchor)", (name) => {
expect(isCommercial(place(name))).toBe(false);
});
it.each([
// A real-estate anchor rescues a finance-ambiguous name — these are real CRE.
"Net Lease Advisors", // "net lease" + "leasing" anchor
"Metro CRE Advisors", // "CRE" anchor
"Apex Investment Sales", // "investment sales" is the CRE-specific phrase
"Harbor Income Properties", // "properties" anchor
"Commercial Real Estate Investment Advisors", // "real estate" anchor
"Summit Capital Markets", // capital markets alone isn't a finance-drop token
])("keeps CRE broker %s despite a finance-ambiguous word", (name) => {
expect(isCommercial(place(name))).toBe(true);
});
it("keeps an allowlisted CRE brand even when the name carries a finance word", () => {
// Brand allowlist overrides the financial-advisory drop.
expect(isCommercial(place("CBRE Investment Management"))).toBe(true);
expect(isCommercial(place("JLL Capital Markets"))).toBe(true);
});
it("still keeps a plainly-commercial broker with no finance word at all", () => {
// Guard against the finance gate over-reaching into ordinary CRE names.
expect(isCommercial(place("Downtown Commercial Leasing"))).toBe(true);
});
});
// CRE-adjacent SERVICE / trade false-positive gate. A Places "commercial real
// estate broker" text search surfaces firms that WORK on commercial property but
// are NOT buyer/leasing brokers — appraisers, title/escrow, construction, mortgage
// lenders, inspectors, property/facilities managers. Their names carry the same
// "commercial"/"property" tokens that satisfy COMMERCIAL_RE, so before the fix
// "Commercial Property Appraisers" and "Sunbelt Commercial Construction" were
// WRONGLY kept as brokers. Now a CRE-service name with NO explicit broker anchor
// is dropped, while a real brokerage (broker anchor or allowlisted brand) — even
// one with a valuation / property-management arm — is preserved.
describe("isCommercial — DROP on CRE-adjacent service/trade firms with no broker anchor", () => {
it.each([
"Commercial Property Appraisers Inc",
"Titan Commercial Title & Escrow",
"Sunbelt Commercial Construction",
"Meridian Commercial Property Management",
"Apex Commercial Mortgage Capital",
"National Commercial Property Inspections",
"Green Commercial Environmental Services",
"Premier Commercial Interior Design Group",
"Statewide Commercial Escrow Services",
"Metro Commercial Facilities Management",
"Cornerstone Commercial Appraisal & Valuation",
"Summit Commercial General Contractors",
])("drops CRE-service firm %s (no broker anchor)", (name) => {
expect(isCommercial(place(name))).toBe(false);
});
it.each([
// A broker anchor rescues a name that also mentions a service word.
"Titan Commercial Real Estate Brokerage", // "brokerage" + "real estate"
"Landmark Commercial Realty & Property Management", // "commercial realty" broker + PM arm
"Apex Commercial Real Estate & Construction Advisors", // "real estate" anchor
"Downtown Commercial Leasing", // "leasing" anchor, no service word
"Net Lease Advisors", // pure broker, unaffected
"Harbor Investment Sales", // "investment sales" anchor
])("keeps CRE broker %s despite (or without) a service word", (name) => {
expect(isCommercial(place(name))).toBe(true);
});
it("keeps an allowlisted brand's valuation / management arm", () => {
// Brand allowlist overrides the CRE-service drop.
expect(isCommercial(place("Cushman & Wakefield Valuation Services"))).toBe(true);
expect(isCommercial(place("CBRE Property Management"))).toBe(true);
expect(isCommercial(place("Colliers Appraisal Group"))).toBe(true);
});
it("still keeps a plainly-commercial broker with no service word at all", () => {
// Guard against the service gate over-reaching into ordinary CRE names.
expect(isCommercial(place("Commercial Realty Advisors"))).toBe(true);
expect(isCommercial(place("Downtown Commercial Real Estate Brokers"))).toBe(true);
});
});
describe("isCommercial — DROP on residential-flavored place types", () => {
it.each(["apartment_complex", "home_goods_store"])("drops type %s with no commercial name", (type) => {
expect(isCommercial(place("Parkview", [type]))).toBe(false);
});
it("commercial name still wins over a residential-flavored type", () => {
expect(isCommercial(place("Commercial Leasing at Parkview", ["apartment_complex"]))).toBe(true);
});
});
describe("isCommercial — edge cases", () => {
it("drops a place with no name", () => {
expect(isCommercial({ id: "x", displayName: { text: "" }, types: ["real_estate_agency"] })).toBe(false);
});
it("drops a place with no types and no commercial name", () => {
expect(isCommercial({ id: "x", displayName: { text: "Generic Agency" } })).toBe(false);
});
});
// --- Contact-based dedupe -------------------------------------------------
// dedupeByContact() collapses the same office surfaced under multiple lenses as
// different place ids. Phone is authoritative; website only merges when phones
// don't contradict (national brands share one domain across many branches).
// Offline + deterministic — zero live Google Places billing.
describe("normalizePhone", () => {
it.each([
["(213) 555-0123", "2135550123"],
["213-555-0123", "2135550123"],
["+1 213 555 0123", "2135550123"],
["1 (213) 555-0123", "2135550123"],
])("normalizes %s → %s", (input, expected) => {
expect(normalizePhone(input)).toBe(expected);
});
it("returns null for null / partial / non-10-digit numbers", () => {
expect(normalizePhone(null)).toBeNull();
expect(normalizePhone("555-0123")).toBeNull(); // 7 digits — not keyable
expect(normalizePhone("")).toBeNull();
});
});
describe("normalizeHost", () => {
it.each([
["https://www.cbre.com/office/la", "cbre.com"],
["http://CBRE.com", "cbre.com"],
["https://la.cbre.com", "la.cbre.com"], // subdomains stay distinct (precise)
])("normalizes %s → %s", (input, expected) => {
expect(normalizeHost(input)).toBe(expected);
});
it("returns null for null / unparseable", () => {
expect(normalizeHost(null)).toBeNull();
expect(normalizeHost("not a url")).toBeNull();
});
});
function agent(over: Partial<AgentResult> & { id: string; name: string }): AgentResult {
return {
address: null,
rating: null,
reviews: 0,
phone: null,
website: null,
mapsUrl: null,
primaryType: null,
kinds: ["buyer"],
...over,
};
}
describe("dedupeByContact", () => {
it("collapses the same office surfaced under two lenses (same phone)", () => {
// The motivating case: "CBRE" + "CBRE Investment Sales", same office/phone,
// two different place ids from two different query lenses.
const out = dedupeByContact([
agent({ id: "1", name: "CBRE", phone: "(213) 555-0100", reviews: 40, rating: 4.5, kinds: ["buyer"] }),
agent({
id: "2",
name: "CBRE Investment Sales",
phone: "213-555-0100",
reviews: 3,
rating: 4.9,
kinds: ["leasing"],
}),
]);
expect(out).toHaveLength(1);
expect(out[0].id).toBe("1"); // first-seen kept canonical
expect(out[0].kinds.sort()).toEqual(["buyer", "leasing"]); // kinds unioned
// Richer rating/review pair adopted from the MORE-rated listing (id 1).
expect(out[0].reviews).toBe(40);
expect(out[0].rating).toBe(4.5);
});
it("keeps two DIFFERENT branch offices of one brand (same domain, different phones)", () => {
const out = dedupeByContact([
agent({ id: "1", name: "CBRE Downtown", phone: "213-555-0100", website: "https://www.cbre.com" }),
agent({ id: "2", name: "CBRE West LA", phone: "310-555-0200", website: "https://www.cbre.com" }),
]);
expect(out).toHaveLength(2); // distinct phones ⇒ distinct offices, never merged
});
it("merges on website only when phones don't contradict (one side phone-less)", () => {
const out = dedupeByContact([
agent({ id: "1", name: "JLL", phone: "213-555-0300", website: "https://jll.com" }),
agent({ id: "2", name: "JLL Capital Markets", phone: null, website: "https://jll.com" }),
]);
expect(out).toHaveLength(1);
expect(out[0].id).toBe("1");
});
it("enriches the canonical's missing contact fields from the duplicate", () => {
const out = dedupeByContact([
agent({ id: "1", name: "Colliers", phone: "213-555-0400", website: null, address: null }),
agent({
id: "2",
name: "Colliers Intl",
phone: "213-555-0400",
website: "https://colliers.com",
address: "1 Main St",
}),
]);
expect(out).toHaveLength(1);
expect(out[0].website).toBe("https://colliers.com"); // filled from dup
expect(out[0].address).toBe("1 Main St"); // filled from dup
});
it("leaves genuinely distinct agencies untouched (no shared phone or host)", () => {
const out = dedupeByContact([
agent({ id: "1", name: "Newmark", phone: "213-555-0500", website: "https://nmrk.com" }),
agent({ id: "2", name: "Kidder Mathews", phone: "206-555-0600", website: "https://kidder.com" }),
]);
expect(out).toHaveLength(2);
});
it("does not merge two phone-distinct entries that lack websites", () => {
const out = dedupeByContact([
agent({ id: "1", name: "Lee & Associates", phone: "213-555-0700" }),
agent({ id: "2", name: "Avison Young", phone: "213-555-0800" }),
]);
expect(out).toHaveLength(2);
});
});
// relevanceScore() weights the RANKED list so a stronger CRE-broker match
// (national brand / strong commercial name / surfaced by both lenses) surfaces
// ahead of a weakly-commercial one at comparable rating-weight — without a
// 0-review brand leapfrogging a genuinely reviewed office. Offline, no billing.
describe("relevanceScore", () => {
it("returns the 1.0 baseline for a plain commercial listing", () => {
// No allowlisted brand, no strong COMMERCIAL_RE hit in the name, one lens.
expect(relevanceScore(agent({ id: "x", name: "Downtown Advisory" }))).toBeCloseTo(1.0);
});
it("adds weight for an allowlisted national brokerage brand", () => {
const s = relevanceScore(agent({ id: "x", name: "CBRE", kinds: ["buyer"] }));
expect(s).toBeGreaterThan(1.0);
// CBRE hits the brand bonus (+0.3) AND "commercial"? no — name is bare "CBRE"
// so only the brand bonus applies.
expect(s).toBeCloseTo(1.3);
});
it("adds weight for a strong commercial name signal", () => {
// "Industrial Property Group" hits COMMERCIAL_RE but is not an allowlist brand.
expect(relevanceScore(agent({ id: "x", name: "Industrial Property Group" }))).toBeCloseTo(1.2);
});
it("adds weight when surfaced by BOTH the buyer and leasing lenses", () => {
const both = relevanceScore(agent({ id: "x", name: "Anon Realty", kinds: ["buyer", "leasing"] }));
const one = relevanceScore(agent({ id: "x", name: "Anon Realty", kinds: ["buyer"] }));
expect(both - one).toBeCloseTo(0.1);
});
it("stacks signals (brand + commercial name + both lenses) up to the bound", () => {
const s = relevanceScore(
agent({ id: "x", name: "CBRE Commercial", kinds: ["buyer", "leasing"] }),
);
// +0.3 brand +0.2 commercial-name +0.1 both-lenses = 1.6 ceiling.
expect(s).toBeCloseTo(1.6);
});
});
// The ranking must PROMOTE a stronger commercial match at comparable
// rating-weight, but a zero-review brand must NOT jump a genuinely reviewed
// office. dedupeByContact is a pass-through here (distinct contacts), so we can
// assert ordering behavior on the same sort comparator via a tiny local sort
// mirroring searchCommercialAgents (kept in sync with places.ts).
describe("relevance-weighted ranking behavior", () => {
const rankWeight = (r: AgentResult) =>
(r.rating ?? 0) * Math.log((r.reviews ?? 0) + 1) * relevanceScore(r);
it("promotes a stronger CRE match over a weakly-commercial one at equal rating/reviews", () => {
const brand = agent({ id: "1", name: "CBRE", rating: 4.5, reviews: 30, kinds: ["buyer", "leasing"] });
const weak = agent({ id: "2", name: "Anon Advisory", rating: 4.5, reviews: 30, kinds: ["buyer"] });
expect(rankWeight(brand)).toBeGreaterThan(rankWeight(weak));
});
it("does NOT let a zero-review brand leapfrog a well-reviewed office", () => {
const brandNoReviews = agent({ id: "1", name: "CBRE", rating: 5, reviews: 0, kinds: ["buyer", "leasing"] });
const reviewedOffice = agent({ id: "2", name: "Metro Advisory", rating: 4.2, reviews: 120, kinds: ["buyer"] });
// rating × log(1) = 0 for the brand regardless of relevance multiplier.
expect(rankWeight(brandNoReviews)).toBe(0);
expect(rankWeight(reviewedOffice)).toBeGreaterThan(rankWeight(brandNoReviews));
});
});