- Restore CLAUDE.md with project conventions - ESLint config with vue3-recommended + typescript - Image upload endpoint (POST /api/admin/upload) with 5MB limit - Admin product form now supports image upload/preview/removal - Vitest config + 19 tests (crypto, validation, btcpay webhook, types) - Restore .claude/ security hooks (block-risky-bash, protect-files) - Logo splash now shows "EVERYTHING YOU LOVE IS A PSYOP" tagline - Add .vite/ to gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
1.6 KiB
TypeScript
56 lines
1.6 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
import crypto from 'node:crypto'
|
|
|
|
// Generate a test key and set it before importing the module
|
|
const testKey = crypto.randomBytes(32).toString('hex')
|
|
|
|
describe('server/lib/crypto', () => {
|
|
beforeAll(() => {
|
|
process.env.ENCRYPTION_KEY = testKey
|
|
})
|
|
|
|
afterAll(() => {
|
|
delete process.env.ENCRYPTION_KEY
|
|
})
|
|
|
|
it('encrypts and decrypts shipping address', async () => {
|
|
const { encrypt, decrypt } = await import('../../server/lib/crypto.js')
|
|
|
|
const address = JSON.stringify({
|
|
name: 'Satoshi Nakamoto',
|
|
line1: '1 Bitcoin Ave',
|
|
city: 'Cryptoville',
|
|
postalCode: '21000',
|
|
country: 'Decentraland',
|
|
})
|
|
|
|
const encrypted = encrypt(address)
|
|
expect(encrypted).not.toBe(address)
|
|
expect(encrypted).toMatch(/^[0-9a-f]+$/)
|
|
|
|
const decrypted = decrypt(encrypted)
|
|
expect(decrypted).toBe(address)
|
|
expect(JSON.parse(decrypted).name).toBe('Satoshi Nakamoto')
|
|
})
|
|
|
|
it('produces different ciphertext for same plaintext', async () => {
|
|
const { encrypt } = await import('../../server/lib/crypto.js')
|
|
|
|
const plaintext = 'same input'
|
|
const a = encrypt(plaintext)
|
|
const b = encrypt(plaintext)
|
|
|
|
expect(a).not.toBe(b) // Random nonce ensures different output
|
|
})
|
|
|
|
it('fails on tampered ciphertext', async () => {
|
|
const { encrypt, decrypt } = await import('../../server/lib/crypto.js')
|
|
|
|
const encrypted = encrypt('secret data')
|
|
// Flip a byte in the middle
|
|
const tampered = encrypted.slice(0, 40) + 'ff' + encrypted.slice(42)
|
|
|
|
expect(() => decrypt(tampered)).toThrow()
|
|
})
|
|
})
|