clients-summary.test.ts 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { describe, it, expect } from 'vitest';
  2. import { computeClientsSummary } from '@/hooks/useClients';
  3. import type { ClientTraffic } from '@/schemas/client';
  4. // Parity with web/service/client.go buildClientsSummary: the same client must
  5. // land in the same bucket whether the count comes from the server (list fetch)
  6. // or is recomputed live from the client_stats WS event. A mismatch would make
  7. // the summary card "jump" on refresh.
  8. type Row = ClientTraffic & { email?: string };
  9. const GB = 1024 * 1024 * 1024;
  10. const DAY = 86_400_000;
  11. function row(over: Partial<Row>): Row {
  12. return { email: 'x', enable: true, up: 0, down: 0, total: 0, expiryTime: 0, ...over } as Row;
  13. }
  14. describe('computeClientsSummary', () => {
  15. it('buckets each client the way the Go service does', () => {
  16. const now = Date.now();
  17. const stats: Row[] = [
  18. row({ email: 'online@x', enable: true }),
  19. row({ email: 'offline@x', enable: true }),
  20. row({ email: 'disabled@x', enable: false }),
  21. row({ email: 'exhausted@x', enable: true, total: 1 * GB, up: 1 * GB }),
  22. row({ email: 'expired@x', enable: true, expiryTime: now - DAY }),
  23. row({ email: 'nearexpiry@x', enable: true, expiryTime: now + DAY }),
  24. row({ email: 'nearlimit@x', enable: true, total: 10 * GB, up: 9.9 * GB }),
  25. ];
  26. const online = new Set(['online@x', 'disabled@x']); // disabled-but-online must NOT count as online
  27. const expireDiffMs = 3 * DAY;
  28. const trafficDiffBytes = 1 * GB;
  29. const s = computeClientsSummary(stats, online, expireDiffMs, trafficDiffBytes);
  30. expect(s.total).toBe(7);
  31. expect(s.online).toEqual(['online@x']);
  32. expect(s.depleted.sort()).toEqual(['exhausted@x', 'expired@x']);
  33. expect(s.deactive).toEqual(['disabled@x']);
  34. expect(s.expiring.sort()).toEqual(['nearexpiry@x', 'nearlimit@x']);
  35. expect(s.active).toBe(2); // online@x + offline@x
  36. });
  37. it('depleted wins over disabled and over online', () => {
  38. const stats: Row[] = [
  39. row({ email: 'a@x', enable: false, total: 1 * GB, up: 2 * GB }),
  40. ];
  41. const s = computeClientsSummary(stats, new Set(['a@x']), 0, 0);
  42. expect(s.depleted).toEqual(['a@x']);
  43. expect(s.deactive).toEqual([]);
  44. expect(s.online).toEqual([]); // disabled is never online
  45. });
  46. it('unlimited + no expiry is active', () => {
  47. const stats: Row[] = [row({ email: 'a@x', enable: true, total: 0, expiryTime: 0 })];
  48. const s = computeClientsSummary(stats, new Set(), 3 * DAY, 1 * GB);
  49. expect(s.active).toBe(1);
  50. expect(s.expiring).toEqual([]);
  51. expect(s.depleted).toEqual([]);
  52. });
  53. });