2026-08-29 16:57:34 +02:00
|
|
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
|
|
|
import { openDatabase, type DB } from '../src/db.js'
|
2026-09-03 21:25:47 +02:00
|
|
|
import { resolveRipStatus, findMatchedAlbum } from '../src/ripstatus.js'
|
2026-08-29 16:57:34 +02:00
|
|
|
|
|
|
|
|
describe('resolveRipStatus', () => {
|
|
|
|
|
let db: DB
|
|
|
|
|
let itemId: number
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
db = openDatabase(':memory:')
|
|
|
|
|
db.prepare("INSERT INTO users (id, username, password_hash, is_admin) VALUES (1, 'sam', 'x', 1)").run()
|
|
|
|
|
db.prepare(
|
|
|
|
|
"INSERT INTO collection_items (id, user_id, discogs_release_id, title, artist) VALUES (10, 1, 100, 'Motion', 'The Cinematic Orchestra')"
|
|
|
|
|
).run()
|
|
|
|
|
itemId = 10
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function addAlbum(id: number, title: string, artist: string) {
|
|
|
|
|
db.prepare('INSERT INTO digital_albums (id, user_id, subsonic_id, title, artist) VALUES (?, 1, ?, ?, ?)').run(
|
|
|
|
|
id,
|
|
|
|
|
`sub-${id}`,
|
|
|
|
|
title,
|
|
|
|
|
artist
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it('no album and no override → not_ripped', () => {
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('not_ripped')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('confident auto-match → ripped', () => {
|
|
|
|
|
addAlbum(1, 'Motion!', 'Cinematic Orchestra')
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('ripped')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('same title different artist → not_ripped', () => {
|
|
|
|
|
addAlbum(1, 'Motion', 'Massive Attack')
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('not_ripped')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('match link → ripped even without fuzzy match', () => {
|
|
|
|
|
addAlbum(1, 'Motion (Remastered)', 'The Cinematic Orchestra')
|
|
|
|
|
db.prepare('INSERT INTO match_links (user_id, item_id, album_id) VALUES (1, 10, 1)').run()
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('ripped')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('manual override wins over everything (both directions)', () => {
|
|
|
|
|
addAlbum(1, 'Motion', 'The Cinematic Orchestra')
|
|
|
|
|
db.prepare('UPDATE collection_items SET rip_override = 0 WHERE id = 10').run()
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('not_ripped')
|
|
|
|
|
|
|
|
|
|
db.prepare('UPDATE collection_items SET rip_override = 1 WHERE id = 10').run()
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('ripped')
|
|
|
|
|
|
|
|
|
|
db.prepare('UPDATE collection_items SET rip_override = NULL WHERE id = 10').run()
|
|
|
|
|
expect(resolveRipStatus(db, 1, itemId)).toBe('ripped')
|
|
|
|
|
})
|
|
|
|
|
})
|
2026-09-03 21:25:47 +02:00
|
|
|
|
|
|
|
|
describe('findMatchedAlbum', () => {
|
|
|
|
|
let db: DB
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
db = openDatabase(':memory:')
|
|
|
|
|
db.prepare("INSERT INTO users (id, username, password_hash, is_admin) VALUES (1, 'sam', 'x', 1)").run()
|
|
|
|
|
db.prepare(
|
|
|
|
|
"INSERT INTO collection_items (id, user_id, discogs_release_id, title, artist) VALUES (10, 1, 100, 'Motion', 'The Cinematic Orchestra')"
|
|
|
|
|
).run()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function addAlbum(id: number, title: string, artist: string) {
|
|
|
|
|
db.prepare('INSERT INTO digital_albums (id, user_id, subsonic_id, title, artist) VALUES (?, 1, ?, ?, ?)').run(
|
|
|
|
|
id,
|
|
|
|
|
`sub-${id}`,
|
|
|
|
|
title,
|
|
|
|
|
artist
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it('returns the match-linked album', () => {
|
|
|
|
|
addAlbum(1, 'Motion (Remastered)', 'The Cinematic Orchestra')
|
|
|
|
|
db.prepare('INSERT INTO match_links (user_id, item_id, album_id) VALUES (1, 10, 1)').run()
|
|
|
|
|
const m = findMatchedAlbum(db, 1, 10)
|
|
|
|
|
expect(m).toMatchObject({ subsonicId: 'sub-1', lastPlayedAt: null })
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('falls back to the confident fuzzy match', () => {
|
|
|
|
|
addAlbum(2, 'Motion!', 'Cinematic Orchestra')
|
|
|
|
|
const m = findMatchedAlbum(db, 1, 10)
|
|
|
|
|
expect(m).toMatchObject({ subsonicId: 'sub-2' })
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns null when nothing matches or the item is missing', () => {
|
|
|
|
|
expect(findMatchedAlbum(db, 1, 10)).toBeNull()
|
|
|
|
|
expect(findMatchedAlbum(db, 1, 9999)).toBeNull()
|
|
|
|
|
})
|
|
|
|
|
})
|