From 270726d4464dc9cee547d0e18b7837861b955ac6 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 13 Apr 2026 13:38:35 +0200 Subject: [PATCH] Add tests --- src/analytics/PosthogEvents.test.ts | 162 ++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/analytics/PosthogEvents.test.ts diff --git a/src/analytics/PosthogEvents.test.ts b/src/analytics/PosthogEvents.test.ts new file mode 100644 index 00000000..35b86f5d --- /dev/null +++ b/src/analytics/PosthogEvents.test.ts @@ -0,0 +1,162 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { + expect, + describe, + it, + vi, + beforeEach, + beforeAll, + afterAll, +} from "vitest"; +import { logger } from "matrix-js-sdk/lib/logger"; +import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc"; + +import { PosthogAnalytics } from "./PosthogAnalytics"; +import { CallEndedTracker } from "./PosthogEvents"; +import { mockConfig } from "../utils/test"; + +const defaultCounters = { + roomEventEncryptionKeysSent: 10, + roomEventEncryptionKeysReceived: 5, +}; + +const defaultTotals = { + roomEventEncryptionKeysReceivedTotalAge: 500, +}; + +function createMockRtcSession(overrides?: { + counters?: Partial; + totals?: Partial; +}): MatrixRTCSession { + return { + statistics: { + counters: { ...defaultCounters, ...overrides?.counters }, + totals: { ...defaultTotals, ...overrides?.totals }, + }, + } as unknown as MatrixRTCSession; +} + +describe("CallEnded", () => { + beforeAll(() => { + mockConfig(); + }); + + beforeEach(() => { + vi.restoreAllMocks(); + vi.spyOn(PosthogAnalytics.instance, "trackEvent").mockImplementation( + () => {}, + ); + }); + + afterAll(() => { + PosthogAnalytics.resetInstance(); + }); + + it("warns if startTime is missing when track is called", () => { + const warnSpy = vi.spyOn(logger, "warn"); + const tracker = new CallEndedTracker(); + const mockSession = createMockRtcSession(); + + tracker.track("test-call-id", 2, false, mockSession); + + expect(warnSpy).toHaveBeenCalledWith( + "[PosthogEvents] Failed to send posthog callEnded event due to missing startTime", + ); + expect(PosthogAnalytics.instance.trackEvent).not.toHaveBeenCalled(); + }); + + it("tracks event with correct properties when startTime is set", () => { + const tracker = new CallEndedTracker(); + const mockSession = createMockRtcSession(); + + tracker.cacheStartCall(new Date(Date.now() - 60000)); + tracker.cacheParticipantCountChanged(5); + tracker.track("test-call-id", 3, true, mockSession); + + expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith( + { + eventName: "CallEnded", + callId: "test-call-id", + callParticipantsMax: 5, + callParticipantsOnLeave: 3, + callDuration: expect.closeTo(60, 1), + roomEventEncryptionKeysSent: 10, + roomEventEncryptionKeysReceived: 5, + roomEventEncryptionKeysReceivedAverageAge: 100, + }, + { send_instantly: true }, + ); + }); + + it("tracks maxParticipantsCount correctly across multiple changes", () => { + const tracker = new CallEndedTracker(); + const mockSession = createMockRtcSession(); + + tracker.cacheStartCall(new Date()); + tracker.cacheParticipantCountChanged(3); + tracker.cacheParticipantCountChanged(7); + tracker.cacheParticipantCountChanged(2); + tracker.track("test-call-id", 1, false, mockSession); + + expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith( + expect.objectContaining({ + callParticipantsMax: 7, + }), + expect.anything(), + ); + }); + + it("computes roomEventEncryptionKeysReceivedAverageAge as 0 when no keys received", () => { + const tracker = new CallEndedTracker(); + const mockSession = createMockRtcSession({ + counters: { roomEventEncryptionKeysReceived: 0 }, + }); + + tracker.cacheStartCall(new Date()); + tracker.track("test-call-id", 1, false, mockSession); + + expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith( + expect.objectContaining({ + roomEventEncryptionKeysReceivedAverageAge: 0, + }), + expect.anything(), + ); + }); + + it("computes roomEventEncryptionKeysReceivedAverageAge correctly when keys are received", () => { + const tracker = new CallEndedTracker(); + const mockSession = createMockRtcSession({ + counters: { roomEventEncryptionKeysReceived: 4 }, + totals: { roomEventEncryptionKeysReceivedTotalAge: 200 }, + }); + + tracker.cacheStartCall(new Date()); + tracker.track("test-call-id", 1, false, mockSession); + + expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith( + expect.objectContaining({ + roomEventEncryptionKeysReceivedAverageAge: 50, + }), + expect.anything(), + ); + }); + + it("passes send_instantly option correctly", () => { + const tracker = new CallEndedTracker(); + const mockSession = createMockRtcSession(); + + tracker.cacheStartCall(new Date()); + tracker.track("test-call-id", 1, false, mockSession); + + expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith( + expect.anything(), + { send_instantly: false }, + ); + }); +});