frontend-analytics-model.test.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import assert from 'node:assert/strict';
  2. import { test } from 'node:test';
  3. import {
  4. buildDashboardSummary,
  5. buildDomainRanking,
  6. buildHourlyHeatmap,
  7. buildStatusDistribution,
  8. buildTrendSeries
  9. } from '../src/frontend/analytics-model.js';
  10. test('builds dashboard summary from analytics, DNS health, and SMTP state', () => {
  11. const summary = buildDashboardSummary({
  12. analytics: {
  13. summary: {
  14. total: 20,
  15. queued: 18,
  16. failed: 2,
  17. recipients: 25,
  18. today: 8,
  19. successRate: 90,
  20. verifiedDomains: 1
  21. }
  22. },
  23. domains: [
  24. domain(true, [
  25. record('verification', 'ok'),
  26. record('dkim', 'ok'),
  27. record('spf', 'ok'),
  28. record('dmarc', 'ok'),
  29. record('sender-a', 'ok')
  30. ]),
  31. domain(false, [
  32. record('verification', 'ok'),
  33. record('dkim', 'warn'),
  34. record('spf', 'missing')
  35. ])
  36. ],
  37. events: [{ createdAt: '2026-07-08T12:30:00.000Z' }],
  38. config: { submission: { enabled: true } },
  39. smtpCredential: { passwordSet: true }
  40. });
  41. assert.equal(summary.verifiedDomains, 1);
  42. assert.equal(summary.today, 8);
  43. assert.equal(summary.successRate, 90);
  44. assert.equal(summary.bounceRate, 10);
  45. assert.equal(summary.complaintRate, 0);
  46. assert.equal(summary.dnsIssues, 4);
  47. assert.equal(summary.smtpReady, true);
  48. assert.equal(summary.lastSentAt, '2026-07-08T12:30:00.000Z');
  49. });
  50. test('normalizes chart datasets for trend, status, ranking, and hourly views', () => {
  51. const analytics = {
  52. byDay: [
  53. { day: '2026-07-07', total: 6, queued: 5, failed: 1, recipients: 8 },
  54. { day: '2026-07-08', total: 9, queued: 9, failed: 0, recipients: 10 }
  55. ],
  56. byStatus: [
  57. { status: 'queued', total: 14 },
  58. { status: 'failed', total: 1 }
  59. ],
  60. byDomain: [
  61. { domain: 'b.example.com', total: 2, queued: 2, failed: 0, recipients: 2 },
  62. { domain: 'a.example.com', total: 8, queued: 7, failed: 1, recipients: 12 }
  63. ],
  64. hourly: [
  65. { hour: 0, total: 0, queued: 0, failed: 0 },
  66. { hour: 9, total: 4, queued: 3, failed: 1 }
  67. ]
  68. };
  69. assert.deepEqual(buildTrendSeries(analytics), [
  70. { date: '2026-07-07', total: 6, accepted: 5, failed: 1, recipients: 8 },
  71. { date: '2026-07-08', total: 9, accepted: 9, failed: 0, recipients: 10 }
  72. ]);
  73. assert.deepEqual(buildStatusDistribution(analytics), [
  74. { status: 'queued', label: 'queued', value: 14 },
  75. { status: 'failed', label: 'failed', value: 1 }
  76. ]);
  77. assert.deepEqual(buildDomainRanking(analytics).map((item) => item.domain), ['a.example.com', 'b.example.com']);
  78. assert.deepEqual(buildHourlyHeatmap(analytics).find((item) => item.hour === '09:00'), {
  79. hour: '09:00',
  80. total: 4,
  81. accepted: 3,
  82. failed: 1
  83. });
  84. });
  85. function domain(verified, records) {
  86. return {
  87. status: {
  88. verified,
  89. records
  90. }
  91. };
  92. }
  93. function record(key, status) {
  94. return { key, status };
  95. }