Skip to content

Commit 36188bd

Browse files
authored
Test/notification error resilience (JhaSourav07#3118)
## Description Fixes JhaSourav07#2898 Added isolated unit/integration tests for `models/Notification.ts` focused on hydration stability, exception safety, and error recovery behavior. ### Changes made: * Added `models/Notification.error-resilience.test.ts` * Tested hydration stability by reusing existing Notification model instances * Tested exception safety when `mongoose.model()` throws unexpected runtime/database errors * Verified safe default values for recovery stability * Ensured invalid `frequency` values fail validation safely without crashing * Verified required validation failures remain localized and recover gracefully ## Pillar * [ ] 🎨 Pillar 1 — New Theme Design * [ ] 📐 Pillar 2 — Geometric SVG Improvement * [ ] 🕐 Pillar 3 — Timezone Logic Optimization * [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A (test-only change) ## Checklist before requesting a review: * [x] I have read the `CONTRIBUTING.md` file. * [x] I have tested these changes locally (`vitest run` passes). * [ ] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). * [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). * [ ] I have updated `README.md` if I added a new theme or URL parameter. * [x] I have started the repo. * [x] I have made sure that i have only one commit to merge in this PR. * [ ] The SVG output matches the CommitPulse "premium quality" aesthetic standard (not applicable for test-only changes). * [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents 726b17b + 78f1fdd commit 36188bd

2 files changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { describe, expect, it } from 'vitest';
2+
import {
3+
SVG_WIDTH,
4+
SVG_HEIGHT,
5+
GHOST_HEIGHT_PX,
6+
LOG_SCALE_MULTIPLIER,
7+
LINEAR_SCALE_MULTIPLIER,
8+
MAX_LOG_HEIGHT,
9+
MAX_LINEAR_HEIGHT,
10+
FONT_MAP,
11+
CONTRIBUTION_MILESTONES,
12+
STREAK_MILESTONES,
13+
} from './constants';
14+
15+
describe('lib/svg/constants', () => {
16+
it('should expose expected SVG dimensions', () => {
17+
expect(SVG_WIDTH).toBe(600);
18+
expect(SVG_HEIGHT).toBe(420);
19+
});
20+
21+
it('should expose expected rendering scale constants', () => {
22+
expect(GHOST_HEIGHT_PX).toBe(4);
23+
expect(LOG_SCALE_MULTIPLIER).toBe(12);
24+
expect(LINEAR_SCALE_MULTIPLIER).toBe(5);
25+
expect(MAX_LOG_HEIGHT).toBe(80);
26+
expect(MAX_LINEAR_HEIGHT).toBe(50);
27+
});
28+
29+
it('should expose correct font mappings', () => {
30+
expect(FONT_MAP).toEqual({
31+
jetbrains: '"JetBrains Mono", monospace',
32+
fira: '"Fira Code", monospace',
33+
roboto: '"Roboto", sans-serif',
34+
});
35+
});
36+
37+
it('should expose expected contribution milestones', () => {
38+
expect(CONTRIBUTION_MILESTONES).toEqual([1, 10, 100, 250, 500, 1000]);
39+
});
40+
41+
it('should expose expected streak milestones', () => {
42+
expect(STREAK_MILESTONES).toEqual([3, 7, 30, 100]);
43+
});
44+
});
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import mongoose from 'mongoose';
2+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3+
4+
describe('Notification error resilience', () => {
5+
beforeEach(() => {
6+
vi.resetModules();
7+
vi.restoreAllMocks();
8+
});
9+
10+
afterEach(() => {
11+
vi.restoreAllMocks();
12+
});
13+
14+
it('should safely reuse existing Notification model during hydration', async () => {
15+
vi.resetModules();
16+
17+
const existingModel = { mocked: true };
18+
19+
mongoose.models.Notification = existingModel as never;
20+
21+
const { Notification } = await import('./Notification');
22+
23+
expect(Notification).toBe(existingModel);
24+
});
25+
26+
it('should recover cleanly when mongoose.model throws unexpectedly', async () => {
27+
vi.resetModules();
28+
29+
// Force recreation instead of reusing cached model
30+
delete mongoose.models.Notification;
31+
32+
vi.spyOn(mongoose, 'model').mockImplementation(() => {
33+
throw new Error('Database connectivity error');
34+
});
35+
36+
let caughtError: Error | null = null;
37+
38+
try {
39+
await import('./Notification');
40+
} catch (error) {
41+
caughtError = error as Error;
42+
}
43+
44+
expect(caughtError).toBeTruthy();
45+
expect(caughtError?.message).toContain('Database connectivity error');
46+
});
47+
48+
it('should expose safe default values for recovery stability', async () => {
49+
vi.resetModules();
50+
51+
delete mongoose.models.Notification;
52+
53+
const { Notification } = await import('./Notification');
54+
55+
const doc = new Notification({
56+
username: 'pari',
57+
email: 'pari@example.com',
58+
});
59+
60+
expect(doc.frequency).toBe('daily');
61+
expect(doc.notifyOnCommit).toBe(true);
62+
expect(doc.notifyOnStreak).toBe(true);
63+
expect(doc.notifyOnMilestone).toBe(true);
64+
expect(doc.isActive).toBe(true);
65+
});
66+
67+
it('should prevent invalid frequency values from crashing validation', async () => {
68+
vi.resetModules();
69+
70+
delete mongoose.models.Notification;
71+
72+
const { Notification } = await import('./Notification');
73+
74+
const doc = new Notification({
75+
username: 'pari',
76+
email: 'pari@example.com',
77+
frequency: 'invalid-value',
78+
});
79+
80+
const validationError = doc.validateSync();
81+
82+
expect(validationError).toBeTruthy();
83+
expect(validationError?.errors.frequency).toBeDefined();
84+
});
85+
86+
it('should keep required validation failures localized', async () => {
87+
vi.resetModules();
88+
89+
delete mongoose.models.Notification;
90+
91+
const { Notification } = await import('./Notification');
92+
93+
const doc = new Notification({});
94+
95+
const validationError = doc.validateSync();
96+
97+
expect(validationError).toBeTruthy();
98+
expect(validationError?.errors.username).toBeDefined();
99+
expect(validationError?.errors.email).toBeDefined();
100+
});
101+
});

0 commit comments

Comments
 (0)