Files
zpan/server/usecases/object.cf-test.ts
T
Jasper Van c85e60f200 fix(analytics): enforce trustworthy offline statistics (#510)
* 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.
2026-07-20 10:23:22 -04:00

266 lines
9.1 KiB
TypeScript

import { env } from 'cloudflare:workers'
import { nanoid } from 'nanoid'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { DirType } from '../../shared/constants'
import type { CreateShareInput } from '../../shared/schemas/share'
import { S3Service } from '../adapters/gateways/s3'
import { createActivityRepo } from '../adapters/repos/activity'
import { createMatterRepo } from '../adapters/repos/matter'
import { createQuotaRepo } from '../adapters/repos/quota'
import { createShareRepo } from '../adapters/repos/share'
import { createStorageRepo } from '../adapters/repos/storage'
import { createStorageUsageRepo } from '../adapters/repos/storage-usage'
import { matters, orgQuotaEntitlements, orgQuotas } from '../db/schema'
import { createCloudflarePlatform } from '../platform/cloudflare'
import type { Database } from '../platform/interface'
import { type SaveShareInput, type SaveToDriveDeps, saveShareToDrive as saveShareToDriveUseCase } from './object'
function buildDb() {
return createCloudflarePlatform(env).db
}
const createShare = (db: Database, input: CreateShareInput) => createShareRepo(db).create(input)
const resolveShareByToken = (db: Database, token: string) => createShareRepo(db).resolveByToken(token)
const revokeShareByToken = (db: Database, token: string, creatorId: string) =>
createShareRepo(db).revokeByToken(token, creatorId)
function saveToDriveDeps(db: Database): SaveToDriveDeps {
return {
s3: new S3Service(),
storages: createStorageRepo(db),
storageUsage: createStorageUsageRepo(db),
quota: createQuotaRepo(db),
activity: createActivityRepo(db),
share: createShareRepo(db),
matter: createMatterRepo(db),
}
}
const saveShareToDrive = (db: Database, input: SaveShareInput) => saveShareToDriveUseCase(saveToDriveDeps(db), input)
async function seedStorage(db: ReturnType<typeof buildDb>, id: string) {
await db.run(
`INSERT OR IGNORE INTO storages (id, bucket, endpoint, region, access_key, secret_key, file_path, custom_host, capacity, used, status, created_at, updated_at)
VALUES ('${id}', 'cf-bucket', 'https://s3.amazonaws.com', 'us-east-1', 'AKIA...', 'secret...', '', '', 0, 0, 'active', ${Date.now()}, ${Date.now()})`,
)
}
async function seedStorageQuota(db: ReturnType<typeof buildDb>, orgId: string, bytes = 10_000_000) {
const now = new Date()
await db.insert(orgQuotas).values({ id: nanoid(), orgId, quota: bytes })
await db.insert(orgQuotaEntitlements).values({
id: nanoid(),
orgId,
resourceType: 'storage',
entitlementType: 'plan',
source: 'free_plan',
sourceId: `free_plan:${orgId}`,
bytes,
startsAt: now,
expiresAt: null,
status: 'active',
metadata: JSON.stringify({ packageName: 'Free', source: 'free_plan' }),
createdAt: now,
updatedAt: now,
})
}
async function seedMatter(db: ReturnType<typeof buildDb>, orgId: string, dirtype = DirType.FILE) {
const now = new Date()
const matter = {
id: nanoid(),
orgId,
alias: nanoid(10),
name: `cf-file-${nanoid(6)}.pdf`,
type: dirtype !== DirType.FILE ? 'folder' : 'application/pdf',
size: 1024,
dirtype,
parent: '',
object: dirtype !== DirType.FILE ? '' : `objects/${nanoid()}.pdf`,
storageId: 'cf-storage-1',
status: 'active',
trashedAt: null,
createdAt: now,
updatedAt: now,
}
await db.insert(matters).values(matter)
return matter
}
// ─── resolveShareByToken on D1 ────────────────────────────────────────────────
describe('[CF] resolveShareByToken', () => {
it('returns ok for an active landing share', async () => {
const db = buildDb()
const orgId = `org-${nanoid(6)}`
await seedStorage(db, 'cf-storage-1')
const matter = await seedMatter(db, orgId)
const share = await createShare(db, {
matterId: matter.id,
orgId,
creatorId: 'cf-user-1',
kind: 'landing',
})
const result = await resolveShareByToken(db, share.token)
expect(result.status).toBe('ok')
if (result.status !== 'ok') return
expect(result.share.id).toBe(share.id)
})
it('returns revoked when share is revoked', async () => {
const db = buildDb()
const orgId = `org-${nanoid(6)}`
await seedStorage(db, 'cf-storage-1')
const matter = await seedMatter(db, orgId)
const share = await createShare(db, { matterId: matter.id, orgId, creatorId: 'cf-user-2', kind: 'landing' })
await revokeShareByToken(db, share.token, 'cf-user-2')
const result = await resolveShareByToken(db, share.token)
expect(result.status).toBe('revoked')
})
it('returns matter_trashed when matter is trashed', async () => {
const db = buildDb()
const orgId = `org-${nanoid(6)}`
await seedStorage(db, 'cf-storage-1')
const matter = await seedMatter(db, orgId)
const share = await createShare(db, { matterId: matter.id, orgId, creatorId: 'cf-user-3', kind: 'landing' })
// Trash = active row with trashedAt set (no 'trashed' status).
await db.run(`UPDATE matters SET trashed_at = ${Date.now()} WHERE id = '${matter.id}'`)
const result = await resolveShareByToken(db, share.token)
expect(result.status).toBe('matter_trashed')
})
})
// ─── saveShareToDrive on D1 ───────────────────────────────────────────────────
describe('[CF] saveShareToDrive — stream copy via D1', () => {
beforeEach(() => {
vi.spyOn(S3Service.prototype, 'copyObject').mockResolvedValue(undefined)
vi.spyOn(S3Service.prototype, 'streamCopy').mockResolvedValue(undefined)
})
it('saves a single file to the target org on D1', async () => {
const db = buildDb()
const srcOrgId = `src-${nanoid(6)}`
const dstOrgId = `dst-${nanoid(6)}`
await seedStorage(db, 'cf-storage-1')
await seedStorageQuota(db, dstOrgId)
const matter = await seedMatter(db, srcOrgId)
const share = await createShare(db, { matterId: matter.id, orgId: srcOrgId, creatorId: 'cf-u1', kind: 'landing' })
if (share.status === 'revoked') throw new Error('test setup failed')
const result = await saveShareToDrive(db, {
share,
matter,
currentUserId: 'cf-u2',
targetOrgId: dstOrgId,
targetParent: '',
})
expect(result.saved).toHaveLength(1)
expect(result.skipped).toHaveLength(0)
expect(result.saved[0].orgId).toBe(dstOrgId)
expect(result.saved[0].status).toBe('active')
})
it('does not increment downloads counter after save', async () => {
const db = buildDb()
const srcOrgId = `src-${nanoid(6)}`
const dstOrgId = `dst-${nanoid(6)}`
await seedStorage(db, 'cf-storage-1')
await seedStorageQuota(db, dstOrgId)
const matter = await seedMatter(db, srcOrgId)
const share = await createShare(db, { matterId: matter.id, orgId: srcOrgId, creatorId: 'cf-u3', kind: 'landing' })
if (share.status === 'revoked') throw new Error('test setup failed')
const downloadsBefore = share.downloads
await saveShareToDrive(db, {
share,
matter,
currentUserId: 'cf-u4',
targetOrgId: dstOrgId,
targetParent: '',
})
const _rows = await db.select().from(matters).where(
// Check that the original share downloads didn't change
)
// Verify by re-querying the share
const updatedShareRows = await db.all<{ downloads: number }>(
`SELECT downloads FROM shares WHERE id = '${share.id}'`,
)
expect(updatedShareRows[0]?.downloads).toBe(downloadsBefore)
})
it('recursively saves a folder tree on D1', async () => {
const db = buildDb()
const srcOrgId = `src-${nanoid(6)}`
const dstOrgId = `dst-${nanoid(6)}`
await seedStorage(db, 'cf-storage-1')
await seedStorageQuota(db, dstOrgId)
// Create folder structure
const now = new Date()
const folder = {
id: nanoid(),
orgId: srcOrgId,
alias: nanoid(10),
name: 'cf-album',
type: 'folder',
size: 0,
dirtype: DirType.USER_FOLDER,
parent: '',
object: '',
storageId: 'cf-storage-1',
status: 'active',
trashedAt: null,
createdAt: now,
updatedAt: now,
}
await db.insert(matters).values(folder)
const file1 = {
id: nanoid(),
orgId: srcOrgId,
alias: nanoid(10),
name: 'photo1.jpg',
type: 'image/jpeg',
size: 500,
dirtype: DirType.FILE,
parent: 'cf-album',
object: `objects/${nanoid()}.jpg`,
storageId: 'cf-storage-1',
status: 'active',
trashedAt: null,
createdAt: now,
updatedAt: now,
}
await db.insert(matters).values(file1)
const share = await createShare(db, { matterId: folder.id, orgId: srcOrgId, creatorId: 'cf-u5', kind: 'landing' })
if (share.status === 'revoked') throw new Error('test setup failed')
const result = await saveShareToDrive(db, {
share,
matter: folder,
currentUserId: 'cf-u6',
targetOrgId: dstOrgId,
targetParent: '',
})
// 1 root folder + 1 file = 2
expect(result.saved).toHaveLength(2)
expect(result.skipped).toHaveLength(0)
})
})