feat: remove EXTERNAL_MARKET result type entirely
All external market properties now surface as VERIFIED_PORTFOLIO. Removes the type from enums, domain types, mock data, matching engine, components, hooks, and tests — zero user-visible distinction remains. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -38,20 +38,17 @@ describe('rankMatches', () => {
|
||||
expect(ranked[0].propertyId).toBe('high')
|
||||
})
|
||||
|
||||
it('breaks ties by result type: VERIFIED_PORTFOLIO before EXTERNAL_MARKET', () => {
|
||||
it('breaks ties by result type: VERIFIED_PORTFOLIO and MAISON_WORK ranked above FUTURE_AVAILABILITY', () => {
|
||||
// Build one of each type but force identical score by using same property body
|
||||
const prop = makeProperty()
|
||||
const verified = buildFullMatch(need, { ...prop, id: 'v', resultType: ResultType.VERIFIED_PORTFOLIO })
|
||||
const external = buildFullMatch(need, { ...prop, id: 'e', resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.85 })
|
||||
const future = buildFullMatch(need, { ...prop, id: 'f', resultType: ResultType.FUTURE_AVAILABILITY, confidenceScore: 0.85 })
|
||||
|
||||
// If scores differ, force them equal for a clean tiebreak test
|
||||
if (verified.matchScore !== external.matchScore) {
|
||||
const lower = Math.min(verified.matchScore, external.matchScore)
|
||||
verified.matchScore = lower
|
||||
external.matchScore = lower
|
||||
}
|
||||
// Force same score for a clean tiebreak test
|
||||
verified.matchScore = 75
|
||||
future.matchScore = 75
|
||||
|
||||
const ranked = rankMatches([external, verified])
|
||||
const ranked = rankMatches([future, verified])
|
||||
expect(ranked[0].resultType).toBe(ResultType.VERIFIED_PORTFOLIO)
|
||||
})
|
||||
|
||||
|
||||
@@ -56,11 +56,6 @@ describe('calcConfidenceModifier', () => {
|
||||
expect(calcConfidenceModifier(p)).toBe(0)
|
||||
})
|
||||
|
||||
it('returns -3 for external market results', () => {
|
||||
const p = makeProperty({ resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.80 })
|
||||
expect(calcConfidenceModifier(p)).toBe(-3)
|
||||
})
|
||||
|
||||
it('returns -2 for Maison Work results', () => {
|
||||
const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.80 })
|
||||
expect(calcConfidenceModifier(p)).toBe(-2)
|
||||
@@ -72,9 +67,9 @@ describe('calcConfidenceModifier', () => {
|
||||
})
|
||||
|
||||
it('stacks an additional -10 penalty when confidenceScore < 0.50', () => {
|
||||
// EXTERNAL_MARKET (-3) + LOW_CONFIDENCE (-10) = -13
|
||||
const p = makeProperty({ resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.45 })
|
||||
expect(calcConfidenceModifier(p)).toBe(-13)
|
||||
// MAISON_WORK (-2) + LOW_CONFIDENCE (-10) = -12
|
||||
const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.45 })
|
||||
expect(calcConfidenceModifier(p)).toBe(-12)
|
||||
})
|
||||
|
||||
it('stacks -10 on FUTURE_AVAILABILITY when confidence is also low', () => {
|
||||
@@ -157,12 +152,12 @@ describe('calculateScore', () => {
|
||||
expect(output.finalScore).toBeGreaterThanOrEqual(85)
|
||||
})
|
||||
|
||||
it('scores a weak match (wrong city, over budget, poor DQ, external market) below 50', () => {
|
||||
it('scores a weak match (wrong city, over budget, poor DQ, low confidence) below 50', () => {
|
||||
const need = makeNeed({ preferredLocations: ['Zürich'] })
|
||||
const prop = makeProperty({
|
||||
location: { city: 'Basel', country: 'CH' },
|
||||
rentPricePerSqm: 70, // 140% of max budget 50 — not excluded but severe penalty
|
||||
resultType: ResultType.EXTERNAL_MARKET,
|
||||
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||
confidenceScore: 0.45, // triggers LOW_CONFIDENCE -10 stacking
|
||||
dataQuality: {
|
||||
score: 0.30, // CRITICAL → -15
|
||||
|
||||
@@ -132,7 +132,7 @@ describe('softFactorEnrichmentService — score range invariant (all keys, 0–1
|
||||
describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => {
|
||||
it('returns identical estimate for same location regardless of resultType', () => {
|
||||
// The enrichment service is location-only — resultType is irrelevant.
|
||||
// FUTURE_AVAILABILITY (pre-market) and EXTERNAL_MARKET should produce the same soft factor score.
|
||||
// FUTURE_AVAILABILITY (pre-market) and VERIFIED_PORTFOLIO should produce the same soft factor score.
|
||||
const sharedLocation = { city: 'Zürich', district: 'Oerlikon', country: 'CH' }
|
||||
const sharedAddress = { street: 'Thurgauerstrasse', houseNumber: '1', postalCode: '8050', city: 'Zürich', country: 'CH' }
|
||||
|
||||
@@ -141,16 +141,16 @@ describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => {
|
||||
address: sharedAddress,
|
||||
resultType: ResultType.FUTURE_AVAILABILITY,
|
||||
})
|
||||
const marketResult = makeProperty({
|
||||
const portfolioResult = makeProperty({
|
||||
location: sharedLocation,
|
||||
address: sharedAddress,
|
||||
resultType: ResultType.EXTERNAL_MARKET,
|
||||
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||
})
|
||||
|
||||
const futureEst = softFactorEnrichmentService.estimate('accessibility', futureSignal)
|
||||
const marketEst = softFactorEnrichmentService.estimate('accessibility', marketResult)
|
||||
expect(futureEst?.score).toBe(marketEst?.score)
|
||||
expect(futureEst?.label).toBe(marketEst?.label)
|
||||
const portfolioEst = softFactorEnrichmentService.estimate('accessibility', portfolioResult)
|
||||
expect(futureEst?.score).toBe(portfolioEst?.score)
|
||||
expect(futureEst?.label).toBe(portfolioEst?.label)
|
||||
})
|
||||
|
||||
it('FUTURE_AVAILABILITY at Oerlikon gets meaningful accessibility score (> 0.70)', () => {
|
||||
|
||||
Reference in New Issue
Block a user