Files
property-match/src/features/matching/__tests__/matchCardAdapter.test.ts
T
Benjamin Sutter 36570c5bdc fix: resolve all TypeScript errors and refactor oversized components
- Fix MUI v9 API: PaperProps/InputLabelProps/inputProps → slotProps in 6 components
- Add ExternalMarketResult alias, PropertyUnit import, OPERATIONS workspace config
- Fix TradeOff.description → .concern, ScoreFactor.label → .criterion
- Make schattenmarktRelease.leadTimeMonths optional, fix mock-data enum values
- Fix useMatchDetailData query typing, weightingService missing WeightProfile keys
- Split Pipeline/Compare/MatchDetail/IntelligenceMatchCard into sub-components
- Fix all test fixtures (CreateNeedInput, CreatePropertyInput, TradeOffInput, etc.)
- Add vercel.json for deployment, zero tsc errors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 00:08:35 +02:00

120 lines
4.3 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { buildMatchCardViewModel } from '../matchCardAdapter'
import { buildFullMatch } from '../rankingEngine'
import { ResultType } from '../../../domain/enums'
import type { VerifiedPortfolioResult, FutureAvailabilityResult } from '../../../domain/unifiedResult'
import type { FutureSignal } from '../../../domain/futureSignal'
import { makeNeed, makeProperty } from './fixtures'
// ── buildMatchCardViewModel — verified portfolio ──────────────────────────────
describe('buildMatchCardViewModel — VERIFIED_PORTFOLIO', () => {
const need = makeNeed()
const property = makeProperty()
const match = buildFullMatch(need, property)
const result: VerifiedPortfolioResult = {
matchId: 'vm-test-1',
needId: need.id,
matchScore: match.matchScore,
resultType: 'VERIFIED_PORTFOLIO',
property,
match,
}
it('sets resultType correctly', () => {
const vm = buildMatchCardViewModel(result, [])
expect(vm.resultType).toBe('VERIFIED_PORTFOLIO')
})
it('uses property title as the card title', () => {
const vm = buildMatchCardViewModel(result, [])
expect(vm.title).toBe(property.title)
})
it('builds locationLabel from city (and district when present)', () => {
const vm = buildMatchCardViewModel(result, [])
expect(vm.locationLabel).toContain('Zürich')
})
it('propagates matchScore from the result', () => {
const vm = buildMatchCardViewModel(result, [])
expect(vm.matchScore).toBe(match.matchScore)
})
it('scoreBreakdown reflects hard and soft components from the engine', () => {
const vm = buildMatchCardViewModel(result, [])
expect(vm.scoreBreakdown!.hardMatchScore).toBe(match.scoreBreakdown.hardMatchScore)
expect(vm.scoreBreakdown!.softFactorScore).toBe(match.scoreBreakdown.softFactorScore)
expect(vm.scoreBreakdown!.totalScore).toBe(match.scoreBreakdown.totalScore)
})
it('does NOT set a disclaimer for verified portfolio results', () => {
const vm = buildMatchCardViewModel(result, [])
expect(vm.disclaimer).toBeUndefined()
})
it('returns reasons derived from top positive factors', () => {
const vm = buildMatchCardViewModel(result, [])
// A high-scoring match should have at least one reason
expect(vm.reasons.length).toBeGreaterThan(0)
vm.reasons.forEach(r => {
expect(r.type).toMatch(/HARD_FACT|SOFT_FACTOR/)
expect(r.score).toBeGreaterThanOrEqual(0)
})
})
it('passes through supplied actions unchanged', () => {
const actions = [{ id: 'shortlist', label: 'Shortlist', actionType: 'SAVE_SHORTLIST' as const, primary: true, onClick: () => {} }]
const vm = buildMatchCardViewModel(result, actions)
expect(vm.actions).toBe(actions)
})
})
// ── buildMatchCardViewModel — future availability ─────────────────────────────
describe('buildMatchCardViewModel — FUTURE_AVAILABILITY', () => {
const need = makeNeed()
const futureProperty = makeProperty({ resultType: ResultType.FUTURE_AVAILABILITY })
const match = buildFullMatch(need, futureProperty)
const minimalSignal = {
id: 'sig-1',
probability: 0.72,
locationHint: 'Zürich Oerlikon',
isVerified: false,
source: { type: 'AI_SIGNAL', credibility: 0.7 },
} as unknown as FutureSignal
const futureResult: FutureAvailabilityResult = {
matchId: 'future-test-1',
needId: need.id,
matchScore: match.matchScore,
resultType: 'FUTURE_AVAILABILITY',
signal: minimalSignal,
property: futureProperty,
match,
}
it('sets resultType to FUTURE_AVAILABILITY', () => {
const vm = buildMatchCardViewModel(futureResult, [])
expect(vm.resultType).toBe('FUTURE_AVAILABILITY')
})
it('always includes a disclaimer for future signals', () => {
const vm = buildMatchCardViewModel(futureResult, [])
expect(vm.disclaimer).toBeTruthy()
expect(typeof vm.disclaimer).toBe('string')
})
it('exposes signal probability in the view model', () => {
const vm = buildMatchCardViewModel(futureResult, [])
expect(vm.signalProbability).toBe(0.72)
})
it('classifies signal quality as HIGH when probability >= 0.70', () => {
const vm = buildMatchCardViewModel(futureResult, [])
expect(vm.signalQuality).toBe('HIGH')
})
})