import { describe, expect, it } from 'vitest'; import { UNRATED_BASELINE, newProBoost, proximityScore, recencyScore, score, smoothedRating, } from '../src/ranking'; const NOW = new Date('2026-06-01T12:00:00Z'); const daysAgo = (n: number) => new Date(NOW.getTime() - n * 86_400_000); describe('smoothedRating', () => { it('falls back to the baseline for an unrated pro', () => { expect(smoothedRating(null, 0)).toBe(UNRATED_BASELINE); }); it('does not let one 5-star review top the deck', () => { const oneReview = smoothedRating(5, 1); const manyReviews = smoothedRating(4.8, 50); expect(oneReview).toBeLessThan(manyReviews); }); it('converges on the true rating as reviews accumulate', () => { expect(smoothedRating(4.8, 500)).toBeCloseTo(4.8, 1); }); it('pulls a single bad review up toward the baseline too', () => { expect(smoothedRating(1, 1)).toBeGreaterThan(1); }); }); describe('proximityScore', () => { it('is 1 next door and 0 at the radius edge', () => { expect(proximityScore(0, 10_000)).toBe(1); expect(proximityScore(10_000, 10_000)).toBe(0); }); it('clamps beyond the radius rather than going negative', () => { expect(proximityScore(50_000, 10_000)).toBe(0); }); it('handles a zero radius without dividing by zero', () => { expect(proximityScore(100, 0)).toBe(0); }); }); describe('recencyScore', () => { it('rewards active pros and decays dormant ones to zero', () => { expect(recencyScore(NOW, NOW)).toBe(1); expect(recencyScore(daysAgo(7), NOW)).toBeCloseTo(0.5, 1); expect(recencyScore(daysAgo(30), NOW)).toBe(0); }); it('scores a pro who has never been active at zero', () => { expect(recencyScore(null, NOW)).toBe(0); }); }); describe('newProBoost', () => { it('gives fresh supply a decaying head start', () => { expect(newProBoost(NOW, NOW)).toBe(1); expect(newProBoost(daysAgo(15), NOW)).toBeCloseTo(0.5, 1); expect(newProBoost(daysAgo(60), NOW)).toBe(0); }); }); describe('score', () => { const base = { ratingAvg: 4.5, ratingCount: 20, responseRate: 0.9, distanceM: 2_000, serviceRadiusM: 10_000, lastActiveAt: NOW, createdAt: daysAgo(200), now: NOW, }; it('always lands in 0..1', () => { expect(score(base)).toBeGreaterThan(0); expect(score(base)).toBeLessThanOrEqual(1); }); it('ranks the closer of two identical pros higher', () => { const near = score({ ...base, distanceM: 500 }); const far = score({ ...base, distanceM: 9_000 }); expect(near).toBeGreaterThan(far); }); it('ranks the more responsive of two identical pros higher', () => { expect(score({ ...base, responseRate: 0.95 })).toBeGreaterThan( score({ ...base, responseRate: 0.2 }), ); }); it('does not bury a brand-new pro beneath an established one', () => { const newbie = score({ ...base, ratingAvg: null, ratingCount: 0, responseRate: null, createdAt: NOW }); expect(newbie).toBeGreaterThan(0.3); }); it('assumes an unknown response rate is average rather than terrible', () => { const unknown = score({ ...base, responseRate: null }); const terrible = score({ ...base, responseRate: 0 }); expect(unknown).toBeGreaterThan(terrible); }); });