frontend-analytics-model.test.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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. record('ptr', 'ok')
  31. ]),
  32. domain(false, [
  33. record('verification', 'ok'),
  34. record('dkim', 'warn'),
  35. record('spf', 'missing')
  36. ])
  37. ],
  38. events: [{ createdAt: '2026-07-08T12:30:00.000Z' }],
  39. config: { submission: { enabled: true } },
  40. smtpCredential: { passwordSet: true }
  41. });
  42. assert.equal(summary.verifiedDomains, 1);
  43. assert.equal(summary.today, 8);
  44. assert.equal(summary.successRate, 90);
  45. assert.equal(summary.bounceRate, 10);
  46. assert.equal(summary.complaintRate, 0);
  47. assert.equal(summary.dnsIssues, 5);
  48. assert.equal(summary.smtpReady, true);
  49. assert.equal(summary.lastSentAt, '2026-07-08T12:30:00.000Z');
  50. });
  51. test('normalizes chart datasets for trend, status, ranking, and hourly views', () => {
  52. const analytics = {
  53. byDay: [
  54. { day: '2026-07-07', total: 6, queued: 5, failed: 1, recipients: 8 },
  55. { day: '2026-07-08', total: 9, queued: 9, failed: 0, recipients: 10 }
  56. ],
  57. byStatus: [
  58. { status: 'queued', total: 14 },
  59. { status: 'failed', total: 1 }
  60. ],
  61. byDomain: [
  62. { domain: 'b.example.com', total: 2, queued: 2, failed: 0, recipients: 2 },
  63. { domain: 'a.example.com', total: 8, queued: 7, failed: 1, recipients: 12 }
  64. ],
  65. hourly: [
  66. { hour: 0, total: 0, queued: 0, failed: 0 },
  67. { hour: 9, total: 4, queued: 3, failed: 1 }
  68. ]
  69. };
  70. assert.deepEqual(buildTrendSeries(analytics), [
  71. { date: '2026-07-07', total: 6, accepted: 5, failed: 1, recipients: 8 },
  72. { date: '2026-07-08', total: 9, accepted: 9, failed: 0, recipients: 10 }
  73. ]);
  74. assert.deepEqual(buildStatusDistribution(analytics), [
  75. { status: 'queued', label: 'queued', value: 14 },
  76. { status: 'failed', label: 'failed', value: 1 }
  77. ]);
  78. assert.deepEqual(buildDomainRanking(analytics).map((item) => item.domain), ['a.example.com', 'b.example.com']);
  79. assert.deepEqual(buildHourlyHeatmap(analytics).find((item) => item.hour === '09:00'), {
  80. hour: '09:00',
  81. total: 4,
  82. accepted: 3,
  83. failed: 1
  84. });
  85. });
  86. function domain(verified, records) {
  87. return {
  88. status: {
  89. verified,
  90. records
  91. }
  92. };
  93. }
  94. function record(key, status) {
  95. return { key, status };
  96. }