d3ro-voice/apps/desktop/tests/main/db-open-atomic-redteam-r2-1.test.ts

52 lines
1.8 KiB
TypeScript

// tests/main/db-open-atomic-redteam-r2-1.test.ts
// 계정 DB 가 손상돼 열리지 않아도 앱이 DB 없이 남지 않는다 — openForUser 는 새 DB 를 연 뒤에야
// 이전 DB 를 닫는다(원자적 교체).
import { afterEach, describe, expect, it } from 'vitest'
import fs from 'fs'
import path from 'path'
import { app } from 'electron'
import { ErrorCode } from '@d3ro/core/errors'
import { sql } from 'drizzle-orm'
import {
closeCurrent,
getCurrentUserId,
getDatabase,
isDatabaseOpen,
openForUser,
openLocal,
} from '../../src/main/db'
const CORRUPT_USER = `corrupt-${process.pid}-${Date.now()}`
const GOOD_USER = `good-${process.pid}-${Date.now()}`
function userDir(userId: string): string {
return path.join(app.getPath('userData'), 'users', userId)
}
afterEach(() => {
closeCurrent()
for (const id of [CORRUPT_USER, GOOD_USER]) fs.rmSync(userDir(id), { recursive: true, force: true })
})
describe('openForUser 원자적 교체', () => {
it('손상된 계정 DB 를 열지 못하면 이전(로컬) DB 가 그대로 열려 있다', () => {
openLocal()
fs.mkdirSync(userDir(CORRUPT_USER), { recursive: true })
fs.writeFileSync(path.join(userDir(CORRUPT_USER), 'd3ro.db'), 'this is not a sqlite database'.repeat(64))
expect(() => openForUser(CORRUPT_USER)).toThrow(expect.objectContaining({ code: ErrorCode.DBOpenFailed }))
expect(isDatabaseOpen()).toBe(true)
expect(getCurrentUserId()).toBe('_local')
expect(() => getDatabase().run(sql`SELECT 1`)).not.toThrow()
})
it('정상 계정 DB 는 열고 이전 DB 와 교체한다', () => {
openLocal()
const result = openForUser(GOOD_USER)
expect(result.dbPath).toContain(GOOD_USER)
expect(getCurrentUserId()).toBe(GOOD_USER)
expect(() => getDatabase().run(sql`SELECT 1`)).not.toThrow()
})
})