arblant
panel-gateway / test / accounting.test.js
accounting.test.js78 lines · 3.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
/* Unit tests: Accounting DO with REAL SQLite (node:sqlite) behind a tiny
   adapter matching the Workers `state.storage.sql` API. Run: node --test test/accounting.test.js */
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Accounting } from '../src/accounting.js';
import { makeSqliteState } from './sqlite-adapter.mjs';

const fresh = () => new Accounting(makeSqliteState(), {});

test('create + validate ok', async () => {
  const a = fresh();
  const u = await a.createUser({ name: 'مامان', quotaGB: 10, expiryDays: 30 });
  assert.ok(u.uuid);
  const v = await a.validate({ uuid: u.uuid });
  assert.deepEqual(v, { ok: true });
});

test('unknown user rejected', async () => {
  const a = fresh();
  const v = await a.validate({ uuid: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' });
  assert.equal(v.ok, false); assert.equal(v.reason, 'unknown-user');
});

test('quota enforcement', async () => {
  const a = fresh();
  const u = await a.createUser({ name: 't', quotaGB: 0.001 }); // ~1MB
  assert.deepEqual(await a.validate({ uuid: u.uuid }), { ok: true });
  const r1 = await a.addUsage({ uuid: u.uuid, up: 600_000, down: 600_000 }); // 1.2MB > quota
  assert.equal(r1.overQuota, true);
  const v = await a.validate({ uuid: u.uuid });
  assert.equal(v.ok, false); assert.equal(v.reason, 'quota-exceeded');
});

test('usage accumulates', async () => {
  const a = fresh();
  const u = await a.createUser({ name: 't2', quotaGB: 10 });
  await a.addUsage({ uuid: u.uuid, up: 100, down: 200 });
  await a.addUsage({ uuid: u.uuid, up: 50, down: 50 });
  const [row] = await a.listUsers();
  assert.equal(row.usedTotal, 400);
});

test('expiry enforced', async () => {
  const state = makeSqliteState();
  const a = new Accounting(state, {});
  const u = await a.createUser({ name: 'tmp', expiryDays: 30 });
  assert.deepEqual(await a.validate({ uuid: u.uuid }), { ok: true });
  state.storage.sql.exec('UPDATE users SET expires_at = ? WHERE uuid = ?', Date.now() - 1000, u.uuid);
  const v = await a.validate({ uuid: u.uuid });
  assert.equal(v.ok, false); assert.equal(v.reason, 'expired');
});

test('revoke + reset', async () => {
  const a = fresh();
  const u = await a.createUser({ name: 'x', quotaGB: 10 });
  await a.addUsage({ uuid: u.uuid, up: 10, down: 10 });
  await a.revokeUser({ uuid: u.uuid });
  const v = await a.validate({ uuid: u.uuid });
  assert.equal(v.reason, 'revoked');
  await a.resetUsage({ uuid: u.uuid });
  const [row] = await a.listUsers();
  assert.equal(row.usedTotal, 0);
});

test('fetch RPC routing', async () => {
  const a = fresh();
  const mk = (method, body) => new Request('http://do/' + method,
    { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) });
  let r = await a.fetch(mk('createUser', { name: 'rpc', quotaGB: 5 }));
  let j = await r.json();
  assert.equal(j.ok, true);
  const uuid = j.result.uuid;
  r = await a.fetch(mk('validate', { uuid }));
  j = await r.json();
  assert.deepEqual(j.result, { ok: true });
  r = await a.fetch(mk('nope', {}));
  assert.equal(r.status, 404);
});