113 lines
3.3 KiB
TypeScript
113 lines
3.3 KiB
TypeScript
import { mkdtempSync, rmSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { DatabaseSync } from 'node:sqlite'
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
|
|
const tempDirs: string[] = []
|
|
|
|
afterEach(() => {
|
|
while (tempDirs.length) {
|
|
const dir = tempDirs.pop()
|
|
if (dir) rmSync(dir, { recursive: true, force: true })
|
|
}
|
|
process.env.DATA_DIR = undefined
|
|
})
|
|
|
|
function useTempDataDir(): string {
|
|
const dir = mkdtempSync(join(tmpdir(), 'pichost-smoke-'))
|
|
tempDirs.push(dir)
|
|
process.env.DATA_DIR = dir
|
|
return dir
|
|
}
|
|
|
|
async function loadDbModule() {
|
|
const mod = await import('./db')
|
|
mod.closeDb()
|
|
return mod
|
|
}
|
|
|
|
describe('activity log smoke', () => {
|
|
it('migrates legacy activity_logs constraints and accepts new action types', async () => {
|
|
const dir = useTempDataDir()
|
|
const dbPath = join(dir, 'pichost.db')
|
|
const legacy = new DatabaseSync(dbPath)
|
|
legacy.exec(`
|
|
CREATE TABLE activity_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
action TEXT NOT NULL CHECK(action IN ('upload', 'delete')),
|
|
key TEXT NOT NULL,
|
|
original_name TEXT NOT NULL,
|
|
size INTEGER NOT NULL DEFAULT 0,
|
|
content_type TEXT NOT NULL DEFAULT '',
|
|
source TEXT NOT NULL CHECK(source IN ('web', 'api')),
|
|
created_at TEXT NOT NULL,
|
|
user_id INTEGER,
|
|
backend_id TEXT,
|
|
ip_address TEXT,
|
|
status TEXT NOT NULL DEFAULT 'success'
|
|
);
|
|
INSERT INTO activity_logs (action, key, original_name, size, content_type, source, created_at)
|
|
VALUES ('upload', 'images/a.webp', 'a.webp', 100, 'image/webp', 'web', '2026-01-01T00:00:00.000Z');
|
|
CREATE TABLE settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
`)
|
|
legacy.close()
|
|
|
|
const { getDb, insertActivityLog, listActivityLogs, closeDb } = await loadDbModule()
|
|
getDb()
|
|
const { ensureStorageSchema } = await import('./storage-backends')
|
|
ensureStorageSchema()
|
|
|
|
insertActivityLog({
|
|
action: 'login',
|
|
key: 'auth/login',
|
|
originalName: 'admin',
|
|
size: 0,
|
|
contentType: '',
|
|
source: 'web',
|
|
userId: 1,
|
|
ipAddress: '127.0.0.1'
|
|
})
|
|
insertActivityLog({
|
|
action: 'tags',
|
|
key: 'tags/1',
|
|
originalName: '生活',
|
|
size: 0,
|
|
contentType: '',
|
|
source: 'admin',
|
|
userId: 1
|
|
})
|
|
|
|
const result = listActivityLogs({ limit: 10, page: 1 })
|
|
expect(result.total).toBe(3)
|
|
expect(result.items.map(item => item.action).sort()).toEqual(['login', 'tags', 'upload'])
|
|
|
|
const migrated = getDb().prepare(`
|
|
SELECT value FROM settings WHERE key = 'activity_logs_schema_v2'
|
|
`).get() as { value: string } | undefined
|
|
expect(migrated?.value).toBe('1')
|
|
|
|
closeDb()
|
|
await new Promise(resolve => setTimeout(resolve, 50))
|
|
})
|
|
|
|
it('lists activity logs on a fresh database without throwing', async () => {
|
|
useTempDataDir()
|
|
|
|
const { getDb, listActivityLogs, closeDb } = await loadDbModule()
|
|
getDb()
|
|
const { ensureStorageSchema } = await import('./storage-backends')
|
|
ensureStorageSchema()
|
|
|
|
const result = listActivityLogs({ limit: 5, page: 1 })
|
|
expect(result.page).toBe(1)
|
|
expect(result.items).toEqual([])
|
|
closeDb()
|
|
await new Promise(resolve => setTimeout(resolve, 50))
|
|
})
|
|
})
|