import-importers-golden.test.mjs
sha256:6a102aafafdfe7e70a24f4e59740200f0ee713ce7915f1b53e9d4ba5ee8b4410
Initial Muse snapshot
Human
48 days ago
| 1 | /** |
| 2 | * Golden import tests: synthetic fixtures → vault notes with expected SPEC-aligned frontmatter. |
| 3 | * Skips notion (live API), audio/video (Whisper + OPENAI_API_KEY). |
| 4 | */ |
| 5 | import { describe, it, before, after } from 'node:test'; |
| 6 | import assert from 'node:assert'; |
| 7 | import path from 'path'; |
| 8 | import fs from 'fs'; |
| 9 | import { fileURLToPath } from 'url'; |
| 10 | import { runImport } from '../lib/import.mjs'; |
| 11 | import { readNote } from '../lib/vault.mjs'; |
| 12 | |
| 13 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
| 14 | const fixturesRoot = path.join(__dirname, 'fixtures', 'import'); |
| 15 | const fixtureMarkdown = path.join(__dirname, 'fixtures', 'markdown-import', 'simple.md'); |
| 16 | const fixturePdf = path.join(__dirname, 'fixtures', 'pdf-import', 'hello.pdf'); |
| 17 | const fixtureDocx = path.join(__dirname, 'fixtures', 'docx-import', 'hello.docx'); |
| 18 | const fixtureGenericCsv = path.join(__dirname, 'fixtures', 'generic-csv-import', 'sample.csv'); |
| 19 | const fixtureJsonRows = path.join(__dirname, 'fixtures', 'json-rows-import', 'sample.json'); |
| 20 | const fixtureVcf = path.join(__dirname, 'fixtures', 'vcf-import', 'sample.vcf'); |
| 21 | const testVault = path.join(__dirname, 'fixtures', 'tmp-import-golden-vault'); |
| 22 | |
| 23 | function assertIsoDate(value) { |
| 24 | assert(typeof value === 'string' && value.length >= 10, `expected date-like string, got ${value}`); |
| 25 | } |
| 26 | |
| 27 | describe('import golden fixtures', () => { |
| 28 | before(() => { |
| 29 | if (fs.existsSync(testVault)) fs.rmSync(testVault, { recursive: true }); |
| 30 | fs.mkdirSync(testVault, { recursive: true }); |
| 31 | }); |
| 32 | |
| 33 | after(() => { |
| 34 | if (fs.existsSync(testVault)) { |
| 35 | try { |
| 36 | fs.rmSync(testVault, { recursive: true }); |
| 37 | } catch (_) {} |
| 38 | } |
| 39 | }); |
| 40 | |
| 41 | it('markdown', async () => { |
| 42 | const result = await runImport('markdown', fixtureMarkdown, { |
| 43 | vaultPath: testVault, |
| 44 | outputDir: 'inbox/golden-md', |
| 45 | dryRun: false, |
| 46 | }); |
| 47 | assert.strictEqual(result.count, 1); |
| 48 | const note = readNote(testVault, result.imported[0].path); |
| 49 | assert.strictEqual(note.frontmatter.source, 'markdown'); |
| 50 | assertIsoDate(String(note.frontmatter.date || '')); |
| 51 | }); |
| 52 | |
| 53 | it('pdf', async () => { |
| 54 | const result = await runImport('pdf', fixturePdf, { |
| 55 | vaultPath: testVault, |
| 56 | outputDir: 'inbox/golden-pdf', |
| 57 | dryRun: false, |
| 58 | }); |
| 59 | assert.strictEqual(result.count, 1); |
| 60 | const note = readNote(testVault, result.imported[0].path); |
| 61 | assert.strictEqual(note.frontmatter.source, 'pdf-import'); |
| 62 | assert.strictEqual(note.frontmatter.pdf_file, 'hello.pdf'); |
| 63 | assert.equal(Number(note.frontmatter.pdf_pages), 1); |
| 64 | assert.ok(String(note.frontmatter.source_id || '').length >= 16); |
| 65 | assertIsoDate(String(note.frontmatter.date || '')); |
| 66 | assert(note.body.includes('Knowtation PDF fixture')); |
| 67 | }); |
| 68 | |
| 69 | it('docx', async () => { |
| 70 | const result = await runImport('docx', fixtureDocx, { |
| 71 | vaultPath: testVault, |
| 72 | outputDir: 'inbox/golden-docx', |
| 73 | dryRun: false, |
| 74 | }); |
| 75 | assert.strictEqual(result.count, 1); |
| 76 | const note = readNote(testVault, result.imported[0].path); |
| 77 | assert.strictEqual(note.frontmatter.source, 'docx-import'); |
| 78 | assert.strictEqual(note.frontmatter.docx_file, 'hello.docx'); |
| 79 | assert.ok(String(note.frontmatter.source_id || '').length >= 16); |
| 80 | assertIsoDate(String(note.frontmatter.date || '')); |
| 81 | assert(note.body.includes('Knowtation DOCX fixture')); |
| 82 | assert(note.body.includes('Second line for golden test')); |
| 83 | }); |
| 84 | |
| 85 | it('markdown empty file', async () => { |
| 86 | const input = path.join(fixturesRoot, 'empty.md'); |
| 87 | const result = await runImport('markdown', input, { |
| 88 | vaultPath: testVault, |
| 89 | outputDir: 'inbox/golden-empty-md', |
| 90 | dryRun: false, |
| 91 | }); |
| 92 | assert.strictEqual(result.count, 1); |
| 93 | const note = readNote(testVault, result.imported[0].path); |
| 94 | assert.strictEqual(note.frontmatter.source, 'markdown'); |
| 95 | assert.strictEqual(String(note.body || '').trim(), ''); |
| 96 | }); |
| 97 | |
| 98 | it('chatgpt-export', async () => { |
| 99 | const input = path.join(fixturesRoot, 'chatgpt-export'); |
| 100 | const result = await runImport('chatgpt-export', input, { |
| 101 | vaultPath: testVault, |
| 102 | outputDir: 'inbox/golden-chatgpt', |
| 103 | dryRun: false, |
| 104 | }); |
| 105 | assert.strictEqual(result.count, 1); |
| 106 | const note = readNote(testVault, result.imported[0].path); |
| 107 | assert.strictEqual(note.frontmatter.source, 'chatgpt'); |
| 108 | assert.strictEqual(note.frontmatter.title, 'Fixture conversation'); |
| 109 | assert.ok(String(note.frontmatter.source_id || '').startsWith('chatgpt_')); |
| 110 | assertIsoDate(String(note.frontmatter.date || '')); |
| 111 | assert(note.body.includes('Fixture user line')); |
| 112 | }); |
| 113 | |
| 114 | it('claude-export (JSON)', async () => { |
| 115 | const input = path.join(fixturesRoot, 'claude-export', 'sample.json'); |
| 116 | const result = await runImport('claude-export', input, { |
| 117 | vaultPath: testVault, |
| 118 | outputDir: 'inbox/golden-claude', |
| 119 | dryRun: false, |
| 120 | }); |
| 121 | assert.strictEqual(result.count, 1); |
| 122 | const note = readNote(testVault, result.imported[0].path); |
| 123 | assert.strictEqual(note.frontmatter.source, 'claude'); |
| 124 | assert.strictEqual(note.frontmatter.title, 'Fixture Claude note'); |
| 125 | assert.strictEqual(note.frontmatter.source_id, 'claude-fix-1'); |
| 126 | assert.strictEqual(note.frontmatter.date, '2024-01-15'); |
| 127 | assert(note.body.includes('Fixture body')); |
| 128 | }); |
| 129 | |
| 130 | it('mem0-export', async () => { |
| 131 | const input = path.join(fixturesRoot, 'mem0-export', 'sample.json'); |
| 132 | const result = await runImport('mem0-export', input, { |
| 133 | vaultPath: testVault, |
| 134 | outputDir: 'inbox/golden-mem0', |
| 135 | dryRun: false, |
| 136 | }); |
| 137 | assert.strictEqual(result.count, 1); |
| 138 | const note = readNote(testVault, result.imported[0].path); |
| 139 | assert.strictEqual(note.frontmatter.source, 'mem0'); |
| 140 | assert.strictEqual(note.frontmatter.source_id, 'mem0-fix-1'); |
| 141 | assert(note.body.includes('Synthetic Mem0')); |
| 142 | }); |
| 143 | |
| 144 | it('mif', async () => { |
| 145 | const input = path.join(fixturesRoot, 'mif', 'sample.memory.md'); |
| 146 | const result = await runImport('mif', input, { |
| 147 | vaultPath: testVault, |
| 148 | outputDir: 'inbox/golden-mif', |
| 149 | dryRun: false, |
| 150 | }); |
| 151 | assert.strictEqual(result.count, 1); |
| 152 | const note = readNote(testVault, result.imported[0].path); |
| 153 | assert.strictEqual(note.frontmatter.source, 'mif'); |
| 154 | assert.strictEqual(note.frontmatter.source_id, 'mif-fix-1'); |
| 155 | assert(note.body.includes('MIF import golden')); |
| 156 | }); |
| 157 | |
| 158 | it('jira-export', async () => { |
| 159 | const input = path.join(fixturesRoot, 'jira-export', 'issues.csv'); |
| 160 | const result = await runImport('jira-export', input, { |
| 161 | vaultPath: testVault, |
| 162 | outputDir: 'inbox/golden-jira', |
| 163 | dryRun: false, |
| 164 | }); |
| 165 | assert.strictEqual(result.count, 1); |
| 166 | const note = readNote(testVault, result.imported[0].path); |
| 167 | assert.strictEqual(note.frontmatter.source, 'jira'); |
| 168 | assert.strictEqual(note.frontmatter.source_id, 'PROJ-1'); |
| 169 | assert.strictEqual(note.frontmatter.title, 'Fixture Jira issue'); |
| 170 | assert.ok(note.body.includes('## All CSV fields (JSON)')); |
| 171 | assert.ok(note.body.includes('"Issue key": "PROJ-1"')); |
| 172 | assertIsoDate(String(note.frontmatter.date || '')); |
| 173 | }); |
| 174 | |
| 175 | it('linear-export', async () => { |
| 176 | const input = path.join(fixturesRoot, 'linear-export', 'issues.csv'); |
| 177 | const result = await runImport('linear-export', input, { |
| 178 | vaultPath: testVault, |
| 179 | outputDir: 'inbox/golden-linear', |
| 180 | dryRun: false, |
| 181 | }); |
| 182 | assert.strictEqual(result.count, 1); |
| 183 | const note = readNote(testVault, result.imported[0].path); |
| 184 | assert.strictEqual(note.frontmatter.source, 'linear'); |
| 185 | assert.strictEqual(note.frontmatter.source_id, 'LIN-1'); |
| 186 | assertIsoDate(String(note.frontmatter.date || '')); |
| 187 | assert(note.body.includes('## All CSV fields (JSON)')); |
| 188 | assert(note.body.includes('"id": "LIN-1"')); |
| 189 | assert(note.body.includes('Fixture Linear')); |
| 190 | }); |
| 191 | |
| 192 | it('notebooklm (JSON)', async () => { |
| 193 | const input = path.join(fixturesRoot, 'notebooklm', 'sample.json'); |
| 194 | const result = await runImport('notebooklm', input, { |
| 195 | vaultPath: testVault, |
| 196 | outputDir: 'inbox/golden-nblm', |
| 197 | dryRun: false, |
| 198 | }); |
| 199 | assert.strictEqual(result.count, 1); |
| 200 | const note = readNote(testVault, result.imported[0].path); |
| 201 | assert.strictEqual(note.frontmatter.source, 'notebooklm'); |
| 202 | assert.strictEqual(note.frontmatter.source_id, 'nb-fix-1'); |
| 203 | assert(note.body.includes('NotebookLM')); |
| 204 | }); |
| 205 | |
| 206 | it('gdrive (folder of md)', async () => { |
| 207 | const input = path.join(fixturesRoot, 'gdrive'); |
| 208 | const result = await runImport('gdrive', input, { |
| 209 | vaultPath: testVault, |
| 210 | outputDir: 'inbox/golden-gdrive', |
| 211 | dryRun: false, |
| 212 | }); |
| 213 | assert.strictEqual(result.count, 1); |
| 214 | const note = readNote(testVault, result.imported[0].path); |
| 215 | assert.strictEqual(note.frontmatter.source, 'gdrive'); |
| 216 | assert.strictEqual(note.frontmatter.source_id, 'doc1'); |
| 217 | assert(note.body.includes('Synthetic Google Drive')); |
| 218 | }); |
| 219 | |
| 220 | it('generic-csv', async () => { |
| 221 | const result = await runImport('generic-csv', fixtureGenericCsv, { |
| 222 | vaultPath: testVault, |
| 223 | outputDir: 'inbox/golden-generic-csv', |
| 224 | dryRun: false, |
| 225 | }); |
| 226 | assert.strictEqual(result.count, 2); |
| 227 | const a = readNote(testVault, result.imported[0].path); |
| 228 | const b = readNote(testVault, result.imported[1].path); |
| 229 | assert.strictEqual(a.frontmatter.source, 'csv-import'); |
| 230 | assert.strictEqual(a.frontmatter.csv_file, 'sample.csv'); |
| 231 | assert.strictEqual(a.frontmatter.title, 'sample.csv · Alice'); |
| 232 | assert.equal(Number(a.frontmatter.row_index), 1); |
| 233 | { |
| 234 | const h = a.frontmatter.import_column_headers; |
| 235 | const cols = typeof h === 'string' ? JSON.parse(h) : h; |
| 236 | assert.deepStrictEqual(cols, ['name', 'amount', 'note']); |
| 237 | } |
| 238 | assert(a.body.startsWith('# sample.csv · Alice')); |
| 239 | assert(a.body.includes('## Full row (JSON)')); |
| 240 | assert(a.body.includes('"name": "Alice"')); |
| 241 | assert(a.body.includes('Alice') && a.body.includes('10')); |
| 242 | assert.equal(Number(b.frontmatter.row_index), 2); |
| 243 | assert.strictEqual(b.frontmatter.title, 'sample.csv · Bob'); |
| 244 | assert(b.body.startsWith('# sample.csv · Bob')); |
| 245 | assert(b.body.includes('Bob')); |
| 246 | }); |
| 247 | |
| 248 | it('json-rows', async () => { |
| 249 | const result = await runImport('json-rows', fixtureJsonRows, { |
| 250 | vaultPath: testVault, |
| 251 | outputDir: 'inbox/golden-json-rows', |
| 252 | dryRun: false, |
| 253 | }); |
| 254 | assert.strictEqual(result.count, 2); |
| 255 | const n0 = readNote(testVault, result.imported[0].path); |
| 256 | const n1 = readNote(testVault, result.imported[1].path); |
| 257 | assert.strictEqual(n0.frontmatter.source, 'json-import'); |
| 258 | assert.strictEqual(n0.frontmatter.json_file, 'sample.json'); |
| 259 | assert.equal(Number(n0.frontmatter.item_index), 0); |
| 260 | assert.strictEqual(n0.frontmatter.source_id, 'row-a'); |
| 261 | assert(n0.body.includes('"name": "First"')); |
| 262 | assert.equal(Number(n1.frontmatter.item_index), 1); |
| 263 | assert(n1.body.includes('Second')); |
| 264 | }); |
| 265 | |
| 266 | it('excel-xlsx', async () => { |
| 267 | const ExcelJS = (await import('exceljs')).default; |
| 268 | const xlsxPath = path.join(testVault, 'golden-temp.xlsx'); |
| 269 | const wb = new ExcelJS.Workbook(); |
| 270 | const ws = wb.addWorksheet('First'); |
| 271 | ws.getRow(1).values = [null, 'name', 'n']; |
| 272 | ws.getRow(2).values = [null, 'Alpha', 10]; |
| 273 | ws.getRow(3).values = [null, 'Beta', 20]; |
| 274 | const buffer = await wb.xlsx.writeBuffer(); |
| 275 | fs.writeFileSync(xlsxPath, buffer); |
| 276 | const result = await runImport('excel-xlsx', xlsxPath, { |
| 277 | vaultPath: testVault, |
| 278 | outputDir: 'inbox/golden-excel', |
| 279 | dryRun: false, |
| 280 | }); |
| 281 | assert.strictEqual(result.count, 2); |
| 282 | const a = readNote(testVault, result.imported[0].path); |
| 283 | const b = readNote(testVault, result.imported[1].path); |
| 284 | assert.strictEqual(a.frontmatter.source, 'xlsx-import'); |
| 285 | assert.strictEqual(a.frontmatter.title, 'golden-temp.xlsx · Alpha'); |
| 286 | assert.equal(Number(a.frontmatter.row_index), 1); |
| 287 | assert(a.body.startsWith('# golden-temp.xlsx · Alpha')); |
| 288 | assert(a.body.includes('## Full row (JSON)')); |
| 289 | assert(a.body.includes('"name": "Alpha"')); |
| 290 | assert(a.body.includes('Alpha') && a.body.includes('10')); |
| 291 | assert.strictEqual(b.frontmatter.title, 'golden-temp.xlsx · Beta'); |
| 292 | assert(b.body.startsWith('# golden-temp.xlsx · Beta')); |
| 293 | assert(b.body.includes('Beta')); |
| 294 | }); |
| 295 | |
| 296 | it('vcf', async () => { |
| 297 | const result = await runImport('vcf', fixtureVcf, { |
| 298 | vaultPath: testVault, |
| 299 | outputDir: 'inbox/golden-vcf', |
| 300 | dryRun: false, |
| 301 | }); |
| 302 | assert.strictEqual(result.count, 2); |
| 303 | const n0 = readNote(testVault, result.imported[0].path); |
| 304 | const n1 = readNote(testVault, result.imported[1].path); |
| 305 | assert.strictEqual(n0.frontmatter.source, 'vcf-import'); |
| 306 | assert(n0.path.includes('contacts/') && n0.path.includes('vcf')); |
| 307 | assert(n0.body.includes('Alice') && n0.body.includes('[email protected]')); |
| 308 | assert(n1.body.includes('Bob') && n1.body.includes('[email protected]')); |
| 309 | }); |
| 310 | |
| 311 | it('google-sheets: requires service account in environment', async () => { |
| 312 | const a = process.env.GOOGLE_APPLICATION_CREDENTIALS; |
| 313 | const j = process.env.GOOGLE_SERVICE_ACCOUNT_JSON; |
| 314 | try { |
| 315 | delete process.env.GOOGLE_APPLICATION_CREDENTIALS; |
| 316 | delete process.env.GOOGLE_SERVICE_ACCOUNT_JSON; |
| 317 | await assert.rejects( |
| 318 | runImport('google-sheets', '1dummySpreadsheetId', { |
| 319 | vaultPath: testVault, |
| 320 | outputDir: 'inbox/golden-sheets', |
| 321 | dryRun: true, |
| 322 | }), |
| 323 | /google-sheets import: set GOOGLE_SERVICE_ACCOUNT_JSON/, |
| 324 | ); |
| 325 | } finally { |
| 326 | if (a === undefined) delete process.env.GOOGLE_APPLICATION_CREDENTIALS; |
| 327 | else process.env.GOOGLE_APPLICATION_CREDENTIALS = a; |
| 328 | if (j === undefined) delete process.env.GOOGLE_SERVICE_ACCOUNT_JSON; |
| 329 | else process.env.GOOGLE_SERVICE_ACCOUNT_JSON = j; |
| 330 | } |
| 331 | }); |
| 332 | }); |
File History
1 commit
sha256:6a102aafafdfe7e70a24f4e59740200f0ee713ce7915f1b53e9d4ba5ee8b4410
Initial Muse snapshot
Human
48 days ago