import { describe, it, expect } from 'vitest' import { encrypt, decrypt } from '../src/crypto' import { getAccessToken, buildMime, TokenDeadError } from '../src/gmail' const KEY = '11'.repeat(32) describe('gmail plumbing', () => { it('crypto round-trips', () => { expect(decrypt(encrypt('refresh-token-x', KEY), KEY)).toBe('refresh-token-x') }) it('exchanges refresh token; surfaces invalid_grant as TokenDeadError', async () => { const okFetch = (async () => new Response(JSON.stringify({ access_token: 'at-1' }), { status: 200 })) as typeof fetch expect(await getAccessToken('rt', 'cid', 'sec', okFetch)).toBe('at-1') const deadFetch = (async () => new Response(JSON.stringify({ error: 'invalid_grant' }), { status: 400 })) as typeof fetch await expect(getAccessToken('rt', 'cid', 'sec', deadFetch)).rejects.toBeInstanceOf(TokenDeadError) }) it('builds a MIME message with PDF attachment', () => { const raw = buildMime({ from: 'a@b.c', to: 'x@y.z', subject: 'INV INV/26-27-0001', bodyText: 'Please find attached.', attachment: { filename: 'inv.pdf', contentType: 'application/pdf', data: Buffer.from('%PDF-') } }) const decoded = Buffer.from(raw.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString() expect(decoded).toContain('Subject: INV INV/26-27-0001') expect(decoded).toContain('application/pdf') }) })