eletrotupi / tcc/ api/tests/exports/format.unit.test.tsc901c35af9c84b6a29e5185545ced1129f33b8f4
2.5 KB | 72 lines Raw
1
import { describe, it, expect } from 'vitest'
2
import { serializeCsv } from '@app/services/export/csv.serializer'
3
import { buildManifest } from '@app/services/export/manifest.builder'
4
import { profileTable } from '@app/services/export/table.descriptors'
5
import { CsvTable } from '@app/services/export/types'
6

7
describe('serializeCsv', () => {
8
  it('keeps the header row and a BOM when the table has no rows', () => {
9
    const file = serializeCsv(profileTable, [])
10
    const text = file.content.toString('utf8')
11

12
    expect(text.startsWith('\ufeff')).toBe(true)
13
    expect(file.rows).toBe(0)
14

15
    const lines = text.slice(1).split('\r\n').filter(Boolean)
16
    expect(lines).toHaveLength(1)
17
    expect(lines[0]).toContain('Email')
18
  })
19

20
  it('round-trips commas, double quotes, and newlines through escaped fields', () => {
21
    const table: CsvTable<{ text: string }> = {
22
      filename: 'notes.csv',
23
      columns: [{ header: 'Texto', value: (row) => row.text }],
24
    }
25

26
    const values = ['a,b', 'he said "hi"', 'line one\nline two']
27
    const file = serializeCsv(
28
      table,
29
      values.map((text) => ({ text })),
30
    )
31
    const body = file.content.toString('utf8').slice(1)
32

33
    // RFC 4180: every string field is quoted and any inner quote doubled, so
34
    // reading the quoted fields back and undoubling the quotes recovers the
35
    // original values byte-for-byte.
36
    const fields = [...body.matchAll(/"((?:[^"]|"")*)"/g)].map((match) =>
37
      match[1].replaceAll('""', '"'),
38
    )
39

40
    expect(fields).toEqual(['Texto', ...values])
41
    expect(file.rows).toBe(3)
42
  })
43
})
44

45
describe('buildManifest', () => {
46
  it('states the generation time and lists each file with its row count', () => {
47
    const manifest = buildManifest(
48
      [{ filename: 'profile.csv', content: Buffer.from(''), rows: 0 }],
49
      new Date('2026-10-04T12:00:00.000Z'),
50
    )
51
    const text = manifest.content.toString('utf8')
52

53
    expect(manifest.filename).toBe('leia-me.md')
54
    expect(text).toContain('2026-10-04T12:00:00.000Z')
55
    expect(text).toContain('`profile.csv` — 0 registros')
56
    expect(text).toContain('_id')
57
  })
58

59
  it('lists every file and singularizes a count of one', () => {
60
    const manifest = buildManifest(
61
      [
62
        { filename: 'moods.csv', content: Buffer.from(''), rows: 1 },
63
        { filename: 'triggers.csv', content: Buffer.from(''), rows: 3 },
64
      ],
65
      new Date('2026-10-04T12:00:00.000Z'),
66
    )
67
    const text = manifest.content.toString('utf8')
68

69
    expect(text).toContain('`moods.csv` — 1 registro')
70
    expect(text).toContain('`triggers.csv` — 3 registros')
71
  })
72
})