import { test } from 'node:test'; import assert from 'node:assert/strict'; import { COLUMNS, toCsv, toJson } from '../src/lib/export.js'; const record = (over = {}) => ({ videoId: 'dQw4w9WgXcQ', title: 'Hello', url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', thumbnailUrl: 'https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg', channelId: 'UC1', channelTitle: 'Chan', channelUrl: 'https://www.youtube.com/@chan', subscribers: 1000, views: 50000, publishedAt: '2026-10-01T00:00:00Z', durationSec: 600, isShort: false, score: 50, vidiqScore: null, foundOn: 'home', firstSeenAt: '2026-10-25T10:00:00.000Z', updatedAt: '2026-10-25T10:00:00.000Z', ...over, }); test('toCsv writes a header and one row per record', () => { const csv = toCsv([record()]); assert.equal( csv, COLUMNS.join(',') + '\r\n' + 'dQw4w9WgXcQ,Hello,https://www.youtube.com/watch?v=dQw4w9WgXcQ,' + 'https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg,UC1,Chan,https://www.youtube.com/@chan,' + '1000,50000,2026-10-01T00:00:00Z,600,false,50,,home,' + '2026-10-25T10:00:00.000Z,2026-10-25T10:00:00.000Z\r\n', ); }); test('toCsv quotes commas, quotes and newlines', () => { const csv = toCsv([record({ title: 'He said "hi", then\nleft' })]); assert.ok(csv.includes('"He said ""hi"", then\nleft"')); }); test('toCsv writes null and undefined as empty cells and keeps booleans readable', () => { const csv = toCsv([record({ subscribers: null, vidiqScore: 7.5, isShort: true, channelUrl: undefined })]); const row = csv.split('\r\n')[1]; assert.ok(row.includes(',true,')); assert.ok(row.includes(',7.5,')); assert.ok(row.includes('Chan,,,50000')); // channelUrl and subscribers empty }); test('toCsv of no records is just the header', () => { assert.equal(toCsv([]), COLUMNS.join(',') + '\r\n'); }); test('toJson pretty-prints and round-trips', () => { const records = [record(), record({ videoId: 'abcdefghijk' })]; const text = toJson(records); assert.ok(text.includes('\n ')); assert.deepEqual(JSON.parse(text), records); });