// Simple integration test for playlist public/private visibility // Run with: npm run test:playlists import fs from 'node:fs'; import path from 'node:path'; import os from 'node:os'; // Create isolated temp DB file const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'newtube-test-')); const dbPath = path.join(tmpDir, 'test.db'); process.env.NEWTUBE_DB_FILE = dbPath; // Dynamically import DB after setting env const dbMod = await import('../db.mjs'); const { insertUser, cryptoRandomUUID, createPlaylist, listPublicPlaylists, listPlaylists, getPlaylistWithItemsIfAllowed, } = dbMod; function expect(cond, msg) { if (!cond) { throw new Error(`Assertion failed: ${msg}`); } } function logOk(msg) { console.log(`✓ ${msg}`); } // Seed users const ownerId = cryptoRandomUUID(); const otherId = cryptoRandomUUID(); insertUser({ id: ownerId, username: 'owner', email: 'owner@example.com', passwordHash: 'x' }); insertUser({ id: otherId, username: 'other', email: 'other@example.com', passwordHash: 'y' }); // Seed playlists const pub1 = createPlaylist({ userId: ownerId, title: 'Pub A', description: 'public', thumbnail: null, isPrivate: false }); const pub2 = createPlaylist({ userId: ownerId, title: 'Pub B', description: 'public', thumbnail: null, isPrivate: 0 }); const priv1 = createPlaylist({ userId: ownerId, title: 'Priv A', description: 'private', thumbnail: null, isPrivate: true }); // 1) Anonymous lists public { const pubs = listPublicPlaylists({ limit: 50, offset: 0 }); expect(Array.isArray(pubs), 'listPublicPlaylists returns array'); const ids = new Set(pubs.map(p => p.id)); expect(ids.has(pub1.id) && ids.has(pub2.id), 'public playlists visible'); expect(!ids.has(priv1.id), 'private playlist not visible'); logOk('Anonymous sees public playlists only'); } // 2) Other user lists own (none) + sees public via listPublicPlaylists { const ownOther = listPlaylists({ userId: otherId, limit: 50, offset: 0 }); expect(ownOther.length === 0, 'other user has no own playlists'); const pubs = listPublicPlaylists({ limit: 50, offset: 0 }); const ids = new Set(pubs.map(p => p.id)); expect(ids.has(pub1.id) && ids.has(pub2.id), 'other user sees public playlists'); logOk('Other user sees public playlists'); } // 3) Owner lists own (public + private) { const ownOwner = listPlaylists({ userId: ownerId, limit: 50, offset: 0 }); const ids = new Set(ownOwner.map(p => p.id)); expect(ids.has(pub1.id) && ids.has(pub2.id) && ids.has(priv1.id), 'owner sees all own playlists, including private'); logOk('Owner sees own public + private playlists'); } // 4) Public view endpoint logic: getPlaylistWithItemsIfAllowed { // Anonymous can view public const viewPub = getPlaylistWithItemsIfAllowed({ viewerUserId: undefined, id: pub1.id, limit: 10, offset: 0 }); expect(viewPub && viewPub.id === pub1.id, 'anonymous can view public playlist details'); // Anonymous cannot view private const viewPriv = getPlaylistWithItemsIfAllowed({ viewerUserId: undefined, id: priv1.id, limit: 10, offset: 0 }); expect(viewPriv === 'forbidden', 'anonymous cannot view private playlist'); // Other user can view public const viewPubOther = getPlaylistWithItemsIfAllowed({ viewerUserId: otherId, id: pub2.id, limit: 10, offset: 0 }); expect(viewPubOther && viewPubOther.id === pub2.id, 'other user can view public playlist'); // Owner can view private const viewPrivOwner = getPlaylistWithItemsIfAllowed({ viewerUserId: ownerId, id: priv1.id, limit: 10, offset: 0 }); expect(viewPrivOwner && viewPrivOwner.id === priv1.id, 'owner can view own private playlist'); logOk('Public view behavior correct for anonymous, other, and owner'); } console.log('\nAll playlist visibility tests passed.');