Files
MikrotikManager/backend/src/services/server-poll-storage-model.test.ts
T

140 lines
10 KiB
TypeScript

import assert from "node:assert/strict"
import { test } from "node:test"
import { SERVER_POLL_STORAGE_POLICY as policy, PollStorageRefused, pollStoragePolicy, pollStorageScope,
pollStorageQuotaFit, pollStorageUsageRemaining, pollStorageGcEligible, type PollStorageScope, type PollStorageGcCandidate } from "./server-poll-storage-model.js"
const DAY = 86400000
const scope: PollStorageScope = { receiptId: "5f4aaf7b-cc19-4b36-b693-5ce092a03852", executionId: "b332191d-bf58-4196-a629-f30367a48ef7",
kind: "rest", ownerId: "operator", bindingProof: "a".repeat(64), contextProof: "b".repeat(64), issuedAt: 1000, expiresAt: 2000 }
const empty = { attempts: 0, bytes: 0, rows: 0 }
const usage = (attempts: number) => ({ attempts, bytes: attempts * (policy.attemptBytes + policy.admissionBytes), rows: attempts * policy.attemptRows })
const compactUsage = (attempts: number, compactedAttempts:number, encodedPerArchive=59075) => ({ attempts,compactedAttempts,archiveBytes:compactedAttempts*encodedPerArchive,
bytes:attempts*(policy.attemptBytes+policy.admissionBytes)+compactedAttempts*(policy.admissionBytes+encodedPerArchive),rows:attempts*policy.attemptRows+compactedAttempts*2 })
const candidate = (patch: Partial<PollStorageGcCandidate> = {}): PollStorageGcCandidate => ({ scope, lastActivityAt: 1500,
gcNotBefore: 0, retentionDays: 14, safetyWindowMs: 60000, mcpTerminal: false, ...patch })
const boundary = 2000 + 14 * DAY + 60000
test("reservations cover all journal rows and metadata independently, with immutable cloned policy", () => {
assert.equal(policy.attemptRows, 1 + 2008 + 2008 + 1 + 1)
assert.equal(policy.attemptBytes, 8 * 1024 * 1024)
assert.equal(policy.admissionBytes, 32768)
assert.equal(policy.globalAttempts, 128); assert.equal(policy.ownerAttempts, 8)
assert.equal(policy.globalRows, 128 * 4019); assert.equal(policy.ownerRows, 8 * 4019)
assert.equal(policy.globalBytes, 128 * (8388608 + 32768)); assert.equal(policy.ownerBytes, 8 * (8388608 + 32768))
const input = { ...policy, retentionDays: 30 }, cloned = pollStoragePolicy(input)
input.retentionDays = 1
assert.equal(cloned.retentionDays, 30); assert.equal(Object.isFrozen(cloned), true)
for (const patch of [{ globalAttempts: 129 }, { attemptRows: 4018 }, { ownerBytes: policy.ownerBytes + 1 }, { retentionDays: 0 },
{ retentionDays: 366 }, { safetyWindowMs: 59999 }, { safetyWindowMs: DAY + 1 }, { retentionDays: 1.5 }]) {
assert.throws(() => pollStoragePolicy({ ...policy, ...patch }), PollStorageRefused)
}
assert.equal(pollStoragePolicy({ ...policy, retentionDays: 365, safetyWindowMs: DAY }).retentionDays, 365)
})
test("scope validates original identity, clones and freezes, and never invents a lifetime cap", () => {
const input = { ...scope }, parsed = pollStorageScope(input, { now: 1000 })
input.ownerId = "changed"
assert.equal(parsed.ownerId, "operator"); assert.equal(Object.isFrozen(parsed), true)
assert.equal(pollStorageScope({ ...scope, ownerId: "x".repeat(200), expiresAt: 2000 + 1000 * DAY }, { now: 1500 }).expiresAt, 2000 + 1000 * DAY)
for (const patch of [{ receiptId: "invalid" }, { executionId: "invalid" }, { ownerId: " operator" }, { ownerId: "operator " },
{ ownerId: "x".repeat(201) }, { ownerId: "x\n" }, { ownerId: "x\u0085" }, { ownerId: "" }, { bindingProof: "A".repeat(64) },
{ contextProof: "b".repeat(63) }, { kind: "unknown" }, { issuedAt: -1 }, { issuedAt: 1.5 }, { issuedAt: NaN },
{ expiresAt: Infinity }, { expiresAt: 8640000000000001 }, { expiresAt: 1000 }, { extra: true }]) {
assert.throws(() => pollStorageScope({ ...scope, ...patch }, { now: 1500 }), PollStorageRefused)
}
assert.throws(() => pollStorageScope(scope, { now: 999 }), PollStorageRefused)
assert.throws(() => pollStorageScope(scope, { now: 2000 }), PollStorageRefused)
assert.equal(pollStorageScope(scope, { now: 2000, allowExpired: true }).expiresAt, 2000)
})
test("quota checks exact capacity and cannot refund admission allowance to fit an extra attempt", () => {
assert.equal(pollStorageQuotaFit(usage(127), usage(7)), true)
assert.equal(pollStorageQuotaFit(usage(128), empty), false)
assert.equal(pollStorageQuotaFit(usage(8), usage(8)), false)
for (const dimension of ["bytes", "rows", "attempts"] as const) {
assert.equal(pollStorageQuotaFit({ ...usage(127), [dimension]: usage(127)[dimension] + 1 }, empty), false)
assert.equal(pollStorageQuotaFit(usage(127), { ...usage(7), [dimension]: usage(7)[dimension] + 1 }), false)
for (const bad of [-1, 0.5, NaN, Infinity, Number.MAX_SAFE_INTEGER + 1, "0", undefined]) {
assert.equal(pollStorageQuotaFit({ ...empty, [dimension]: bad } as typeof empty, empty), false)
}
}
assert.equal(pollStorageQuotaFit({ ...usage(127), bytes: policy.globalBytes - policy.attemptBytes }, empty), false)
assert.equal(pollStorageQuotaFit(empty, usage(1)), false)
assert.equal(pollStorageQuotaFit({ ...empty, bytes: Number.MAX_SAFE_INTEGER }, empty), false)
})
test("quota refuses partial, undercharged and mismatched reservation counters", () => {
const malformed = [{ attempts: 1 }, { bytes: policy.attemptBytes, rows: policy.attemptRows },
{ ...usage(1), bytes: policy.attemptBytes }, { ...usage(1), bytes: usage(1).bytes - 1 },
{ ...usage(1), rows: policy.attemptRows - 1 }, { ...usage(1), rows: policy.attemptRows + 1 },
{ ...usage(1), attempts: 2 }, { ...empty, bytes: 1 }, { ...empty, rows: 1 },
{ ...usage(1), bytes: usage(1).bytes + 1 }, { ...usage(1), extra: true }]
for (const value of malformed) {
assert.equal(pollStorageQuotaFit(value as typeof empty, empty), false)
assert.equal(pollStorageQuotaFit(usage(7), value as typeof empty), false)
}
assert.equal(pollStorageQuotaFit(usage(7), usage(7)), true)
assert.equal(pollStorageQuotaFit(empty, empty), true)
})
test("GC is strictly after expiry plus retention/safety and persisted gcNotBefore", () => {
assert.equal(pollStorageGcEligible(candidate(), policy, boundary), false)
assert.equal(pollStorageGcEligible(candidate(), policy, boundary + 1), true)
assert.equal(pollStorageGcEligible(candidate({ gcNotBefore: boundary + DAY }), policy, boundary + DAY), false)
assert.equal(pollStorageGcEligible(candidate({ gcNotBefore: boundary + DAY }), policy, boundary + DAY + 1), true)
assert.equal(pollStorageGcEligible(candidate({ scope: { ...scope, kind: "mcp" } }), policy, boundary + 1), false)
assert.equal(pollStorageGcEligible(candidate({ scope: { ...scope, kind: "mcp" }, mcpTerminal: true }), policy, boundary + 1), true)
})
test("336 paid hourly histories and one heavy poll fit unchanged owner bytes and rows",()=>{
const retained=compactUsage(1,336)
assert(retained.bytes<policy.ownerBytes);assert(retained.rows<policy.ownerRows)
assert.equal(pollStorageQuotaFit(retained,retained),true)
assert.equal(pollStorageUsageRemaining(retained,"owner"),3)
assert.equal(pollStorageUsageRemaining(usage(8),"owner"),0,"Unresolved full reservations are not compacted or refunded by arithmetic")
assert.equal(pollStorageQuotaFit(usage(128),retained),false,"Owner history cannot exceed the global compacted ledger")
})
test("compacted accounting validates the exact optional pair, encoded bounds and opaque extras",()=>{
const good=compactUsage(0,1,40)
assert.equal(pollStorageQuotaFit(good,good),true)
assert.equal(pollStorageQuotaFit(compactUsage(0,1,131072),compactUsage(0,1,131072)),true)
const {archiveBytes:_bytes,...missingArchive}=good,{compactedAttempts:_count,...missingCount}=good
for(const value of [missingArchive,missingCount,{...good,archiveBytes:undefined},{...good,compactedAttempts:undefined},
compactUsage(0,1,39),compactUsage(0,1,131073),{...good,bytes:good.bytes-1},{...good,rows:good.rows+1},
{...good,compactedAttempts:-0},{...good,archiveBytes:NaN},{...good,opaque:true},
{...usage(0),compactedAttempts:0,archiveBytes:1},compactUsage(0,Number.MAX_SAFE_INTEGER,40)]){
assert.equal(pollStorageQuotaFit(value as typeof good,value as typeof good),false)
assert.equal(pollStorageUsageRemaining(value as typeof good,"owner"),undefined)
}
assert.equal(pollStorageQuotaFit({...empty,compactedAttempts:0,archiveBytes:0},empty),true)
assert.equal(pollStorageQuotaFit(compactUsage(0,1,41),compactUsage(0,1,40)),false,"No other compact admission can own the unexplained byte")
})
test("free heavy slots do not imply another full byte/row reservation fits",()=>{
const owner=compactUsage(0,2000,40),global=compactUsage(0,32600,40)
assert(owner.bytes<=policy.ownerBytes);assert(global.bytes<=policy.globalBytes)
assert.equal(pollStorageUsageRemaining(owner,"owner"),0)
assert.equal(pollStorageUsageRemaining(global,"global"),0)
assert.equal(pollStorageQuotaFit(global,owner),false)
assert.equal(pollStorageUsageRemaining(compactUsage(0,2056,40),"owner"),undefined)
})
test("retention increases, persisted retention/safety, recent stages and long original authority all delay GC", () => {
assert.equal(pollStorageGcEligible(candidate(), { ...policy, retentionDays: 30 }, boundary + 1), false)
assert.equal(pollStorageGcEligible(candidate({ retentionDays: 30 }), policy, boundary + 1), false)
assert.equal(pollStorageGcEligible(candidate({ safetyWindowMs: DAY }), policy, boundary + 1), false)
assert.equal(pollStorageGcEligible(candidate({ lastActivityAt: boundary }), policy, boundary + 1), false)
assert.equal(pollStorageGcEligible(candidate({ scope: { ...scope, expiresAt: 2000 + 1000 * DAY } }), policy, boundary + 1), false)
assert.equal(pollStorageGcEligible(candidate(), { ...policy, safetyWindowMs: DAY }, boundary + 1), false)
})
test("GC fails closed for malformed metadata, unsafe arithmetic and Date overflow", () => {
for (const patch of [{ retentionDays: 0 }, { retentionDays: 366 }, { lastActivityAt: -1 }, { lastActivityAt: 999 },
{ lastActivityAt: boundary + 2 }, { gcNotBefore: NaN }, { gcNotBefore: Infinity }, { safetyWindowMs: 59999 }, { safetyWindowMs: DAY + 1 }]) {
assert.equal(pollStorageGcEligible(candidate(patch), policy, boundary + 1), false)
}
assert.equal(pollStorageGcEligible(candidate(), policy, NaN), false)
assert.equal(pollStorageGcEligible(candidate({ scope: { ...scope, expiresAt: 8640000000000000 } }), policy, 8640000000000000), false)
})