mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-21 04:59:47 +08:00
* fix(analytics): enforce trustworthy offline statistics Separate immutable counters from point-in-time snapshots, expose incomplete coverage instead of synthetic zeroes, and keep browser analytics result-only. Restore finite Free quota baselines, fail closed for invalid storage quota, reconcile traffic reports fairly, and add production-safe backfill and data-quality diagnostics. * fix(analytics): preserve global backfill totals Group generated hourly backfill rows by their projected values so SQLite cannot resolve output aliases to source organization columns and overwrite cross-organization totals.
327 lines
11 KiB
TypeScript
327 lines
11 KiB
TypeScript
import { sql } from 'drizzle-orm'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { createTestApp } from '../../test/setup.js'
|
|
import { createStorageRepo } from './storage.js'
|
|
|
|
describe('createStorage', () => {
|
|
it('sets filePath to empty string regardless of input', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
capacity: 0,
|
|
})
|
|
expect(result.filePath).toBe('')
|
|
})
|
|
|
|
it('sets customHost to empty string when not provided', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
capacity: 0,
|
|
})
|
|
expect(result.customHost).toBe('')
|
|
})
|
|
|
|
it('uses provided customHost when given', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
customHost: 'https://cdn.example.com',
|
|
capacity: 0,
|
|
})
|
|
expect(result.customHost).toBe('https://cdn.example.com')
|
|
})
|
|
|
|
it('sets capacity to 0 when not provided', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
capacity: 0,
|
|
})
|
|
expect(result.capacity).toBe(0)
|
|
})
|
|
|
|
it('uses provided capacity when given', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
capacity: 1073741824,
|
|
})
|
|
expect(result.capacity).toBe(1073741824)
|
|
})
|
|
|
|
it('initialises used to 0 and status to active', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'auto',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
capacity: 0,
|
|
})
|
|
expect(result.used).toBe(0)
|
|
expect(result.status).toBe('active')
|
|
})
|
|
|
|
it('persists the created row to the database', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await createStorageRepo(db).create({
|
|
bucket: 'my-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
capacity: 0,
|
|
})
|
|
const fetched = await createStorageRepo(db).get(created.id)
|
|
expect(fetched?.id).toBe(created.id)
|
|
expect(fetched?.bucket).toBe('my-bucket')
|
|
})
|
|
})
|
|
|
|
describe('updateStorage', () => {
|
|
async function seed(db: Awaited<ReturnType<typeof createTestApp>>['db']) {
|
|
return createStorageRepo(db).create({
|
|
bucket: 'original-bucket',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'AKID',
|
|
secretKey: 'SECRET',
|
|
customHost: 'https://cdn.original.com',
|
|
capacity: 500,
|
|
})
|
|
}
|
|
|
|
it('returns null when storage does not exist', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).update('nonexistent', { bucket: 'new-bucket' })
|
|
expect(result).toBeNull()
|
|
})
|
|
|
|
it('keeps existing values for fields not included in update', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seed(db)
|
|
const updated = await createStorageRepo(db).update(created.id, {})
|
|
expect(updated?.bucket).toBe('original-bucket')
|
|
expect(updated?.region).toBe('us-east-1')
|
|
expect(updated?.accessKey).toBe('AKID')
|
|
expect(updated?.secretKey).toBe('SECRET')
|
|
expect(updated?.customHost).toBe('https://cdn.original.com')
|
|
expect(updated?.capacity).toBe(500)
|
|
})
|
|
|
|
it('applies all provided optional fields', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seed(db)
|
|
const updated = await createStorageRepo(db).update(created.id, {
|
|
bucket: 'new-bucket',
|
|
endpoint: 'https://r2.example.com',
|
|
region: 'auto',
|
|
accessKey: 'NEW_AKID',
|
|
secretKey: 'NEW_SECRET',
|
|
customHost: 'https://cdn.new.com',
|
|
capacity: 1000,
|
|
status: 'disabled',
|
|
})
|
|
expect(updated?.bucket).toBe('new-bucket')
|
|
expect(updated?.endpoint).toBe('https://r2.example.com')
|
|
expect(updated?.region).toBe('auto')
|
|
expect(updated?.accessKey).toBe('NEW_AKID')
|
|
expect(updated?.secretKey).toBe('NEW_SECRET')
|
|
expect(updated?.customHost).toBe('https://cdn.new.com')
|
|
expect(updated?.capacity).toBe(1000)
|
|
expect(updated?.status).toBe('disabled')
|
|
})
|
|
|
|
it('updates only status leaving all other fields intact', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seed(db)
|
|
const updated = await createStorageRepo(db).update(created.id, { status: 'disabled' })
|
|
expect(updated?.status).toBe('disabled')
|
|
expect(updated?.bucket).toBe('original-bucket')
|
|
})
|
|
|
|
it('updates the updatedAt timestamp', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seed(db)
|
|
const before = created.updatedAt.getTime()
|
|
await new Promise((r) => setTimeout(r, 10))
|
|
const updated = await createStorageRepo(db).update(created.id, { bucket: 'new-bucket' })
|
|
expect(updated?.updatedAt.getTime()).toBeGreaterThanOrEqual(before)
|
|
})
|
|
})
|
|
|
|
describe('listStorages', () => {
|
|
it('returns empty items and zero total when no storages exist', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).list()
|
|
expect(result).toEqual({ items: [], total: 0 })
|
|
})
|
|
|
|
it('returns all storages ordered by createdAt ascending', async () => {
|
|
const { db } = await createTestApp()
|
|
await createStorageRepo(db).create({
|
|
bucket: 'b1',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'K1',
|
|
secretKey: 'S1',
|
|
capacity: 0,
|
|
})
|
|
await createStorageRepo(db).create({
|
|
bucket: 'b2',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'K2',
|
|
secretKey: 'S2',
|
|
capacity: 0,
|
|
})
|
|
const result = await createStorageRepo(db).list()
|
|
expect(result.total).toBe(2)
|
|
expect(result.items).toHaveLength(2)
|
|
})
|
|
})
|
|
|
|
describe('getStorage', () => {
|
|
it('returns null when storage does not exist', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).get('nonexistent')
|
|
expect(result).toBeNull()
|
|
})
|
|
|
|
it('returns the storage when it exists', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await createStorageRepo(db).create({
|
|
bucket: 'b',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'K',
|
|
secretKey: 'S',
|
|
capacity: 0,
|
|
})
|
|
const found = await createStorageRepo(db).get(created.id)
|
|
expect(found?.id).toBe(created.id)
|
|
})
|
|
})
|
|
|
|
describe('selectStorage', () => {
|
|
async function seedActive(
|
|
db: Awaited<ReturnType<typeof createTestApp>>['db'],
|
|
opts: { capacity?: number; used?: number; status?: string; bucket?: string } = {},
|
|
) {
|
|
const created = await createStorageRepo(db).create({
|
|
bucket: opts.bucket ?? 'b',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'K',
|
|
secretKey: 'S',
|
|
capacity: opts.capacity ?? 0,
|
|
})
|
|
if (opts.used !== undefined || opts.status !== undefined) {
|
|
await db.run(
|
|
sql`UPDATE storages SET used = ${opts.used ?? created.used}, status = ${opts.status ?? created.status} WHERE id = ${created.id}`,
|
|
)
|
|
}
|
|
return createStorageRepo(db).get(created.id)
|
|
}
|
|
|
|
it('returns an active storage whose capacity is not reported', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seedActive(db)
|
|
const found = await createStorageRepo(db).select()
|
|
expect(found.id).toBe(created?.id)
|
|
})
|
|
|
|
it('returns the requested active storage with capacity even when it is not oldest', async () => {
|
|
const { db } = await createTestApp()
|
|
const first = await seedActive(db, { bucket: 'first' })
|
|
const second = await seedActive(db, { bucket: 'second' })
|
|
|
|
const auto = await createStorageRepo(db).select()
|
|
const targeted = await createStorageRepo(db).select(second?.id)
|
|
|
|
expect(auto.id).toBe(first?.id)
|
|
expect(targeted.id).toBe(second?.id)
|
|
})
|
|
|
|
it('rejects a requested inactive storage', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seedActive(db, { status: 'disabled' })
|
|
await expect(createStorageRepo(db).select(created?.id)).rejects.toThrow('No available storage')
|
|
})
|
|
|
|
it('rejects a requested full storage', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await seedActive(db, { capacity: 10, used: 10 })
|
|
await expect(createStorageRepo(db).select(created?.id)).rejects.toThrow('No available storage')
|
|
})
|
|
|
|
it('throws when no active storage exists', async () => {
|
|
const { db } = await createTestApp()
|
|
await expect(createStorageRepo(db).select()).rejects.toThrow('No available storage')
|
|
})
|
|
})
|
|
|
|
describe('deleteStorage', () => {
|
|
it('returns not_found when storage does not exist', async () => {
|
|
const { db } = await createTestApp()
|
|
const result = await createStorageRepo(db).delete('nonexistent')
|
|
expect(result).toBe('not_found')
|
|
})
|
|
|
|
it('deletes a storage that is not referenced by any matter', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await createStorageRepo(db).create({
|
|
bucket: 'b',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'K',
|
|
secretKey: 'S',
|
|
capacity: 0,
|
|
})
|
|
const result = await createStorageRepo(db).delete(created.id)
|
|
expect(result).toBe('ok')
|
|
expect(await createStorageRepo(db).get(created.id)).toBeNull()
|
|
})
|
|
|
|
it('returns in_use when matters reference the storage', async () => {
|
|
const { db } = await createTestApp()
|
|
const created = await createStorageRepo(db).create({
|
|
bucket: 'b',
|
|
endpoint: 'https://s3.example.com',
|
|
region: 'us-east-1',
|
|
accessKey: 'K',
|
|
secretKey: 'S',
|
|
capacity: 0,
|
|
})
|
|
const now = Date.now()
|
|
await db.run(sql`
|
|
INSERT INTO matters (id, org_id, alias, name, type, storage_id, created_at, updated_at)
|
|
VALUES ('m-ref', 'org-1', 'alias-ref', 'test.txt', 'text/plain', ${created.id}, ${now}, ${now})
|
|
`)
|
|
const result = await createStorageRepo(db).delete(created.id)
|
|
expect(result).toBe('in_use')
|
|
})
|
|
})
|