import assert from 'node:assert/strict'; import test from 'node:test'; import { createPlazaEventService } from './plaza-events.mjs'; import { createPlazaRecommendService, recommendInternals } from './plaza-recommend.mjs'; test('plaza events: dwell tiers increase signal weight', () => { const { eventSignal } = createPlazaEventService({ query: async () => [[]] }).internals; const short = eventSignal({ event_type: 'dwell', dwell_ms: 1000 }); const medium = eventSignal({ event_type: 'dwell', dwell_ms: 5000 }); const long = eventSignal({ event_type: 'dwell', dwell_ms: 35000 }); assert.ok(medium > short); assert.ok(long > medium); }); test('plaza recommend: mmr rerank reduces same-category clustering', () => { const { mmrRerank, itemSimilarity } = recommendInternals; const items = [ { id: 'a', rank_score: 0.95, category_slug: 'travel', author_id: 'u1', tags: ['日本'], }, { id: 'b', rank_score: 0.93, category_slug: 'travel', author_id: 'u2', tags: ['日本'], }, { id: 'c', rank_score: 0.9, category_slug: 'study-notes', author_id: 'u3', tags: ['rust'], }, ]; const reranked = mmrRerank(items, { lambda: 0.75, limit: 3 }); assert.equal(reranked[0].id, 'a'); assert.notEqual(reranked[1].category_slug, reranked[0].category_slug); assert.equal(itemSimilarity(items[0], items[1]), 1); }); test('plaza recommend: cursor roundtrip preserves offset', () => { const { parseRecommendCursor, encodeRecommendCursor } = recommendInternals; const encoded = encodeRecommendCursor({ offset: 20, profileVersion: 42 }); const parsed = parseRecommendCursor(encoded); assert.equal(parsed.offset, 20); assert.equal(parsed.profileVersion, 42); }); test('plaza recommend: cold start interleaves categories', () => { const { coldStartInterleave } = recommendInternals; const items = [ { id: 'a1', category_slug: 'travel', hot_score: 90, published_at: 1 }, { id: 'a2', category_slug: 'travel', hot_score: 80, published_at: 2 }, { id: 'b1', category_slug: 'study-notes', hot_score: 85, published_at: 3 }, { id: 'c1', category_slug: 'creative', hot_score: 70, published_at: 4 }, ]; const ordered = coldStartInterleave(items, 'session-1'); assert.equal(ordered.length, 4); assert.notEqual(ordered[0].category_slug, ordered[1].category_slug); }); test('plaza recommend: recallSimilar binds seeds before viewer id', async () => { let similarParams = null; const pool = { async query(sql, params) { if (sql.includes('FROM plaza_reactions r1')) { similarParams = params; return [[]]; } if (sql.includes('SELECT tags FROM plaza_posts WHERE id IN')) { return [[{ tags: '[]' }]]; } if (sql.includes('FROM plaza_posts pp')) { return [[]]; } return [[]]; }, }; const recommend = createPlazaRecommendService(pool, { eventService: { async getProfile() { return { categoryWeights: {}, tagWeights: {}, authorWeights: {}, feedCategoryWeights: {}, likedPostIds: new Set(['seed-post']), dislikedPostIds: new Set(), dislikedCategorySlugs: new Set(), followedAuthorIds: new Set(), seenPostIds: new Set(), deepSeenPostIds: new Set(), eventCount: 1, profileVersion: 7, }; }, }, formatPostRow(row) { return { id: row.id }; }, loadViewerReactions: async () => new Map(), }); await recommend.listRecommendedFeed({ viewerId: 'viewer-1', sessionId: 'session-1', limit: 20, }); assert.deepEqual(similarParams, ['seed-post', 'viewer-1', 80]); }); test('plaza events: dislike marks category as disliked', () => { const { emptyProfile } = createPlazaEventService({ query: async () => [[]] }).internals; const profile = emptyProfile(); profile.dislikedCategorySlugs.add('travel'); profile.dislikedPostIds.add('post-1'); assert.ok(profile.dislikedCategorySlugs.has('travel')); assert.ok(profile.deepSeenPostIds instanceof Set); });