feat: enhance discovery, vibe sessions, and library enrichment
Typecheck / typecheck (backend) (push) Has been cancelled
Typecheck / typecheck (workers) (push) Has been cancelled

This commit is contained in:
kami
2026-08-01 14:40:48 +04:00
parent a0c9f42a89
commit 4c48d11e9d
54 changed files with 4136 additions and 521 deletions
+85
View File
@@ -8,6 +8,22 @@ function makeService(): { service: DbService; mockQuery: ReturnType<typeof vi.fn
}
describe('DbService v2 methods', () => {
describe('createAlbum', () => {
it('persists the canonical release date instead of discarding it', async () => {
const { service, mockQuery } = makeService();
mockQuery.mockResolvedValue({ rows: [{ id: 'album-1' }] });
await service.createAlbum({
id: 'album-1', artist_id: 'artist-1', title: 'Album',
year: 2026, release_date: '2026-07-15', artwork_id: null,
});
const [sql, params] = mockQuery.mock.calls[0];
expect(sql).toContain('release_date');
expect(params[3]).toBe('2026-07-15');
});
});
describe('upsertClaim', () => {
it('calls INSERT ... ON CONFLICT with correct parameters', async () => {
const { service, mockQuery } = makeService();
@@ -168,6 +184,75 @@ describe('DbService v2 methods', () => {
});
});
describe('track evidence propagation', () => {
it('projects a favourite onto artist, genre, and present audio dimensions', async () => {
const { service, mockQuery } = makeService();
let evidenceNumber = 0;
mockQuery.mockImplementation((sql: string) => {
if (sql.includes('INSERT INTO evidence')) return Promise.resolve({ rows: [{ id: `ev-${++evidenceNumber}` }] });
if (sql.includes('WITH artist_ids')) {
return Promise.resolve({ rows: [
{ entity_type: 'artist', entity_id: 'artist-1' },
{ entity_type: 'genre', entity_id: 'genre-1' },
] });
}
if (sql.includes('SELECT energy, bpm, valence')) {
return Promise.resolve({ rows: [{ energy: 0.81, bpm: 128, valence: 0.22 }] });
}
return Promise.resolve({ rowCount: 1, rows: [] });
});
await service.recordTrackEvidence({
user_id: 'user-1', track_id: 'track-1', signal: 'add_to_favorites',
profile: 'longterm', weight: 0.60,
});
const evidenceWrites = mockQuery.mock.calls
.filter(([sql]) => (sql as string).includes('INSERT INTO evidence'))
.map(([, params]) => params as unknown[]);
expect(evidenceWrites.map(params => params[1])).toEqual([
'track', 'artist', 'genre', 'audio', 'audio', 'audio',
]);
const beliefWrites = mockQuery.mock.calls
.filter(([sql]) => (sql as string).includes('INSERT INTO listener_beliefs'))
.map(([, params]) => params as unknown[]);
const artistBelief = beliefWrites.find(params => params[2] === 'artist');
const genreBelief = beliefWrites.find(params => params[2] === 'genre');
expect(artistBelief?.[3]).toBe('artist-1');
expect(artistBelief?.[5]).toBeCloseTo(0.54); // one favourite is a usable comfort signal
expect(genreBelief?.[5]).toBeCloseTo(0.27);
expect(beliefWrites.filter(params => params[2] === 'audio')).toHaveLength(3);
});
it('rebuilds shared beliefs from local completed plays and feedback without appending evidence', async () => {
const clientQuery = vi.fn((sql: string) => {
if (sql === 'BEGIN' || sql === 'COMMIT' || sql === 'ROLLBACK') return Promise.resolve({ rows: [] });
if (sql.includes('DELETE FROM listener_beliefs')) return Promise.resolve({ rowCount: 0, rows: [] });
if (sql.includes('FROM play_history ph')) {
return Promise.resolve({ rows: [{
track_id: 'track-1', signal: 'playback_completed', profile: 'longterm', weight: 0.10,
}] });
}
if (sql.includes('WITH artist_ids')) return Promise.resolve({ rows: [{ entity_type: 'artist', entity_id: 'artist-1' }] });
if (sql.includes('SELECT energy, bpm, valence')) return Promise.resolve({ rows: [] });
return Promise.resolve({ rowCount: 1, rows: [] });
});
const connect = vi.fn().mockResolvedValue({ query: clientQuery, release: vi.fn() });
const service = new DbService({ query: vi.fn(), connect } as any);
const result = await service.rebuildDerivedListenerBeliefs('user-1');
expect(result).toEqual({ interactions: 1, beliefs: 1 });
expect(clientQuery.mock.calls.some(([sql]) => (sql as string).includes('FROM feedback f'))).toBe(true);
expect(clientQuery.mock.calls.some(([sql]) => (sql as string).includes('INSERT INTO evidence'))).toBe(false);
const beliefCall = (clientQuery.mock.calls as unknown as Array<[string, unknown[]]>).
find(([sql]) => sql.includes('INSERT INTO listener_beliefs'));
const beliefParams = beliefCall?.[1] ?? [];
expect(beliefParams.slice(1, 5)).toEqual(['longterm', 'artist', 'artist-1', 'affinity']);
});
});
describe('getFusedTrackArtists', () => {
it('reads from claim_fusion view', async () => {
const { service, mockQuery } = makeService();