Skip to content

test(analytics): comprehensive integration and edge-case tests#3308

Merged
PierreBrisorgueil merged 3 commits intomasterfrom
test/analytics-comprehensive-v2
Mar 25, 2026
Merged

test(analytics): comprehensive integration and edge-case tests#3308
PierreBrisorgueil merged 3 commits intomasterfrom
test/analytics-comprehensive-v2

Conversation

@PierreBrisorgueil
Copy link
Contributor

@PierreBrisorgueil PierreBrisorgueil commented Mar 25, 2026

Summary

  • 22 integration tests: Express supertest, concurrent load, query string capture, error resilience
  • 17 resilience tests: SDK errors, post-shutdown safety, double-shutdown idempotency
  • Edge cases for requireFeatureFlag: falsy IDs, concurrent flags, 403 shape

Replaces closed PR #3303. Closes #3298.

Summary by CodeRabbit

  • Documentation

    • Updated testing requirements to specify separate unit and integration test files
    • Clarified PR monitoring grace period timing and branch protection handling for review decisions
  • Tests

    • Added comprehensive test coverage for analytics middleware behavior and service resilience scenarios

Copilot AI review requested due to automatic review settings March 25, 2026 11:49
@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

Updated skill documentation to clarify test requirements and timing thresholds. Added two new comprehensive Jest test suites for the analytics module covering middleware behavior, service initialization, event-driven workflows, feature flag integration, and failure resilience scenarios.

Changes

Cohort / File(s) Summary
Skill Documentation
.claude/skills/feature/SKILL.md, .claude/skills/pull-request/SKILL.md
Updated feature skill's Definition of Done checklist to require separate unit and integration test files. Clarified pull-request skill's monitor loop stop-condition timing (~9 minutes) and expanded branch protection handling to treat CHANGES_REQUESTED review decision like REVIEW_REQUIRED.
Analytics Test Suite: Comprehensive
modules/analytics/tests/analytics.comprehensive.unit.tests.js
New Jest test suite validating Express middleware behavior (tracking on completed requests, concurrent request independence, query-string stripping), module initialization wiring (AnalyticsService and middleware registration), event-driven behavior (plan.changed event triggering groupIdentify), and requireFeatureFlag middleware edge cases (user ID validation, concurrent evaluations, organization context).
Analytics Test Suite: Resilience
modules/analytics/tests/analytics.service.resilience.unit.tests.js
New Jest unit test suite covering analytics service failure scenarios and shutdown safety—verifying SDK method exceptions propagate, post-shutdown calls are silent no-ops, shutdown is idempotent, and feature-flag APIs return undefined after shutdown.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested labels

Chore

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is concise but somewhat sparse; it summarizes test counts and related issues but lacks the structured sections (Scope, Validation, Guardrails) specified in the template. Expand the description to include Scope (modules impacted, risk level), Validation (lint/test checklist), and Guardrails sections from the template for clarity.
Linked Issues check ❓ Inconclusive The PR partially addresses issue #3298: it delivers unit/integration tests for analytics.service and requireFeatureFlag middleware with mocked posthog-node, but does not explicitly cover all acceptance criteria (featureFlags.service unit tests, identify/groupIdentify on auth/org flows, E2E tests). Clarify whether the omitted acceptance criteria (featureFlags.service tests, auth/org flow integration tests, E2E tests) are intentionally out of scope or deferred to follow-up work.
Out of Scope Changes check ❓ Inconclusive The PR includes ancillary changes to unrelated Vue conventions and test async/query-stripping tweaks alongside the core analytics test suite, which may introduce scope creep. Confirm that all non-test-file changes are necessary for the analytics test implementation or consider splitting them into a separate PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding comprehensive integration and edge-case tests for the analytics module.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/analytics-comprehensive-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds expanded Jest coverage for the modules/analytics module, focusing on resilience behavior (SDK failures/shutdown) and broader “comprehensive” scenarios (Express middleware + init wiring + feature-flag edge cases).

Changes:

  • Add resilience-focused unit tests for analytics.service (sync/async SDK failures, shutdown safety, optional args).
  • Add a large “comprehensive” test suite intended to cover Express middleware behavior, module init wiring, and requireFeatureFlag edge cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
modules/analytics/tests/analytics.service.resilience.unit.tests.js New resilience/unit tests for AnalyticsService error propagation, shutdown safety, and argument forwarding.
modules/analytics/tests/analytics.comprehensive.unit.tests.js New broad test suite covering intended middleware integration, init wiring, and feature-flag middleware edge cases.

Comment on lines +101 to +102
// Second call — client is null, returns undefined synchronously
AnalyticsService.shutdown();
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second shutdown() call is invoked without await/assertion. Since shutdown is an async function, this can leave an unresolved Promise (and can surface as an unhandled rejection if implementation changes). Await the second call (or await expect(...).resolves...).

Suggested change
// Second call — client is null, returns undefined synchronously
AnalyticsService.shutdown();
// Second call — client is null; still await in case implementation remains async
await AnalyticsService.shutdown();

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +55
jest.unstable_mockModule('../services/analytics.service.js', () => ({
default: {
track: mockTrack,
init: jest.fn(),
identify: jest.fn(),
groupIdentify: jest.fn(),
shutdown: jest.fn(),
},
}));

const mod = await import('../middlewares/analytics.middleware.js');
analyticsMiddleware = mod.default;

app = express();
app.use((req, _res, next) => {
req.user = { _id: 'user-int-1' };
req.organization = { _id: 'org-int-1' };
next();
});
app.use(analyticsMiddleware);

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests import ../middlewares/analytics.middleware.js, but modules/analytics currently has no middlewares/ directory or analytics.middleware.js file, so this suite will fail at import time. Update the import path to the actual middleware location or add the missing middleware implementation in this PR/dependency chain.

Copilot uses AI. Check for mistakes.
Comment on lines +222 to +229
test('should call AnalyticsService.init() and register middleware', async () => {
const { default: initAnalytics } = await import('../analytics.init.js');
initAnalytics(mockApp);

expect(mockInit).toHaveBeenCalledTimes(1);
expect(mockApp.use).toHaveBeenCalledTimes(1);
expect(mockApp.use).toHaveBeenCalledWith(expect.any(Function));
});
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expectations here don’t match the current modules/analytics/analytics.init.js implementation: initAnalytics only calls AnalyticsService.init() and does not register any middleware (app.use) or billing plan.changed listener. Either adjust the tests to reflect the current init behavior or update the init module to actually wire middleware/event listeners (and include those code changes in this PR).

Copilot uses AI. Check for mistakes.
Comment on lines +304 to +310
jest.unstable_mockModule('../services/analytics.featureFlags.service.js', () => ({
default: mockFeatureFlagsService,
}));

const mod = await import('../middlewares/analytics.requireFeatureFlag.js');
requireFeatureFlag = mod.default;

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suite mocks/imports ../services/analytics.featureFlags.service.js and ../middlewares/analytics.requireFeatureFlag.js, but neither file currently exists under modules/analytics/. As written, these tests will fail at module resolution; either update to the actual file locations or include the missing feature-flag service + middleware implementations as part of the PR series.

Suggested change
jest.unstable_mockModule('../services/analytics.featureFlags.service.js', () => ({
default: mockFeatureFlagsService,
}));
const mod = await import('../middlewares/analytics.requireFeatureFlag.js');
requireFeatureFlag = mod.default;
/**
* Test-only stub implementation of the requireFeatureFlag middleware factory.
*
* @param {string} featureKey - The feature flag key to evaluate.
* @returns {Function} Express middleware that enforces the feature flag.
*/
requireFeatureFlag = (featureKey) => {
return async (reqInner, resInner, nextInner) => {
try {
const enabled = await mockFeatureFlagsService.isEnabled(featureKey, {
userId: reqInner && reqInner.user ? reqInner.user._id : undefined,
organizationId:
reqInner && reqInner.organization ? reqInner.organization._id : undefined,
});
if (!enabled) {
resInner.status(403).json({ error: 'Feature disabled' });
return;
}
nextInner();
} catch (err) {
resInner.status(500).json({ error: 'Feature flag check failed' });
}
};
};

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +173
test('should not prevent response from completing when track throws', async () => {
// Build a fresh app that swallows finish-handler errors
const safeApp = express();
safeApp.use((req, _res, next) => {
req.user = { _id: 'u1' };
next();
});
safeApp.use(analyticsMiddleware);
safeApp.get('/api/items', (_req, res) => res.status(200).json({ items: [] }));

// Verify normal operation first
const res1 = await request(safeApp).get('/api/items');
expect(res1.status).toBe(200);
expect(mockTrack).toHaveBeenCalledTimes(1);

// Now verify track was called, meaning the middleware did not skip it
expect(mockTrack).toHaveBeenCalledWith(
'u1',
'api_request',
expect.objectContaining({ endpoint: '/api/items', method: 'GET' }),
undefined,
);
});
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name says it verifies behavior “when track throws”, but mockTrack is never configured to throw in this test, so it doesn’t exercise the intended error-resilience path. Either make mockTrack throw for this test and assert the HTTP response still completes, or rename the test to match what it actually verifies.

Copilot uses AI. Check for mistakes.

const mod = await import('../services/analytics.service.js');
AnalyticsService = mod.default;
AnalyticsService.init();
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AnalyticsService.init() is async and must be awaited here; otherwise client may still be null when the tests run, making the “SDK throws” assertions flaky or false (track/identify/groupIdentify become no-ops when uninitialised).

Suggested change
AnalyticsService.init();
await AnalyticsService.init();

Copilot uses AI. Check for mistakes.
Express integration tests (supertest): middleware tracks real requests,
handles concurrent load, skips health routes, captures query strings.

Service resilience tests: SDK error propagation (sync + async),
post-shutdown no-op safety, double-shutdown idempotency, optional args.

Init wiring tests: module init calls AnalyticsService.init(), registers
middleware, sets up billing plan.changed listener with error isolation.

Feature-flag middleware edge cases: falsy _id variants (null, 0, empty),
ObjectId-like coercion, concurrent flag evaluations, 403 response shape.

Closes #3298
@PierreBrisorgueil PierreBrisorgueil force-pushed the test/analytics-comprehensive-v2 branch from 6abc474 to b737355 Compare March 25, 2026 12:02
@PierreBrisorgueil PierreBrisorgueil merged commit 793cff2 into master Mar 25, 2026
2 of 3 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the test/analytics-comprehensive-v2 branch March 25, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(analytics): comprehensive tests for analytics module

2 participants