Skip to content

Conversation

@HardMax71
Copy link
Owner

@HardMax71 HardMax71 commented Jan 30, 2026

Also fixing parts of backend if there's shitshow in codebase


Summary by cubic

Simplified the SSE notifications stream to emit plain NotificationResponse events and added frontend unit tests for interceptors, formatters, and the notification stream. Also updated OpenAPI and generated SDK types (including QueuePriority) and enabled ping keepalive for more stable connections.

  • Refactors

    • Notifications SSE now sends event="notification" with NotificationResponse; removed SSENotificationEventData and control/heartbeat events; added ping=30.
    • SSE service builds NotificationResponse from Redis and logs open/close; removed legacy formatters.
    • OpenAPI switches stream schema to NotificationResponse; added QueuePriority enum and updated affected request/response schemas; regenerated frontend types.
    • Frontend stream parses NotificationResponse and shows browser notifications when permitted.
    • Cleaned up UserOverviewModal typing for recent events.
  • New Features

    • Added unit tests for api-interceptors, formatters, and notifications stream (connection state, error handling, event parsing, disconnect behavior).

Written for commit f4ad102. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added QueuePriority enum (critical, high, normal, low, background) for execution priorities.
  • Tests

    • Added tests for API interceptor utilities and formatters.
    • Added comprehensive tests for the notification stream component (event handling, errors, browser notifications).
  • Refactor

    • Simplified notification streaming to emit consolidated NotificationResponse payloads and aligned public schemas/types to the new shape.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Replaces SSENotificationEvent/SSENotificationEventData with NotificationResponse across backend and frontend, simplifies backend notification SSE flow (removes handshake/heartbeat, uses auth dependency, adds ping=30), introduces QueuePriority enum, and updates generated types and frontend parsing/tests accordingly.

Changes

Cohort / File(s) Summary
Backend SSE route & service
backend/app/api/routes/sse.py, backend/app/services/sse/sse_service.py
Notification SSE endpoint now uses NotificationResponse, accepts an auth_service dependency to fetch the current user, wraps EventSourceResponse with ping=30, and streams serialized NotificationResponse directly; removed handshake/heartbeat and SSENotificationEventData formatting helpers.
Backend enums & schemas
backend/app/domain/enums/sse.py, backend/app/domain/enums/__init__.py, backend/app/schemas_pydantic/sse.py
Removed SSENotificationEvent enum and SSENotificationEventData pydantic model from public API exports and imports; SSEControlEvent and SSEHealthStatus remain exported.
API docs / generated types (backend & docs)
docs/reference/openapi.json, frontend/src/lib/api/types.gen.ts
Added QueuePriority string enum (critical, high, normal, low, background); changed priority fields on several events from integer to QueuePriority; removed SseNotificationEvent/SseNotificationEventData schemas and updated SSE notification stream schema to NotificationResponse.
Frontend API surface
frontend/src/lib/api/index.ts
Re-exported generated types (including new QueuePriority and NotificationResponse) from types.gen.ts to make them publicly available.
Frontend notification stream & tests
frontend/src/lib/notifications/stream.svelte.ts, frontend/src/lib/notifications/__tests__/stream.test.ts
Simplified SSE message handling to only accept events with event === 'notification', parse NotificationResponse from data, forward to callbacks, create browser Notification using subject/body, and added comprehensive tests for connection, parsing, errors, auth/401 handling, and browser Notification behavior.
Frontend type/UX cleanup
frontend/src/components/admin/events/UserOverviewModal.svelte
Removed unused EventType import and RecentEvent interface; simplified recentEvents derivation from overview data.
Frontend utilities tests
frontend/src/lib/__tests__/api-interceptors.test.ts, frontend/src/lib/__tests__/formatters.test.ts
Added unit test suites for API interceptor helpers (getErrorMessage, unwrap, unwrapOr) and various formatters (date/time, durations, bytes, numbers, truncate) with edge-case coverage.
CI tweak
.github/workflows/stack-tests.yml
Set OTEL_SDK_DISABLED: "true" for the backend unit test step to disable OpenTelemetry during tests.

Sequence Diagram(s)

sequenceDiagram
  participant Client as EventSource (browser)
  participant Route as /notifications/stream (FastAPI)
  participant SSE as SSEService
  participant Redis as Redis Pub/Sub
  participant Auth as AuthService

  Client->>Route: GET /notifications/stream (EventSource)
  Route->>Auth: resolve auth_service -> current_user
  Route->>SSE: start notification_stream(current_user)
  SSE->>Redis: subscribe to user's notification channel
  Redis-->>SSE: RedisNotificationMessage
  SSE->>Route: yield SSE event { event: "notification", data: NotificationResponse }
  Route-->>Client: EventSource stream ping=30 / message event "notification"
  Client->>Client: parse NotificationResponse and invoke callbacks (and optional Browser Notification)
  Note right of SSE: logs open/close and ignores missing messages
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • chore/test: add more tests for frontend #100 — Direct code-level overlap: replaces SSENotificationEvent/SSENotificationEventData with NotificationResponse across SSE route/service and frontend types.
  • chore: type fixes #74 — Related changes to SSE/notification stack and schemas; likely touches the same service and docs areas.
  • chore: frontend tests #42 — Frontend-focused: modifies SSE notification stream handling and adds tests that align with the new NotificationResponse payload.

Poem

🐰 I hopped into streams with a cheerful twitch,
I swapped old events for a single rich stitch —
No heartbeats, no handshakes, just NotificationResponse,
QueuePriority sorted, the whole flow moves at once.
Thump-thump hooray — the SSE meadow feels light!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'fix: frontend tests added' is vague and does not accurately represent the main changes. The PR makes substantial backend changes (SSE notifications refactoring, QueuePriority enum, OpenAPI updates) and frontend changes beyond just adding tests. Revise the title to reflect the primary change more accurately, such as 'refactor: simplify SSE notifications with NotificationResponse' or 'refactor: SSE notifications and add frontend tests' to better capture the scope of changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/frontend-tests

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

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 13 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="frontend/src/lib/__tests__/formatters.test.ts">

<violation number="1" location="frontend/src/lib/__tests__/formatters.test.ts:43">
P3: This assertion hard-codes a Latin-digit year, but formatTimestamp uses `toLocaleString` with the default locale. In locales with non-Latin numerals, the output won’t contain "2025", so the test can fail depending on environment locale. Consider comparing against Intl.DateTimeFormat output for the current locale to keep the test deterministic.</violation>

<violation number="2" location="frontend/src/lib/__tests__/formatters.test.ts:149">
P3: These expectations assume western digit grouping and Latin numerals, but formatNumber uses `toLocaleString()` with the environment’s locale. In locales with different digit groupings or numerals, the substrings won’t appear and the test becomes flaky. Consider comparing to `Intl.NumberFormat(undefined)` output instead.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


it('formats with locale separators', () => {
const result = formatNumber(1234567);
expect(result).toContain('1');
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 30, 2026

Choose a reason for hiding this comment

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

P3: These expectations assume western digit grouping and Latin numerals, but formatNumber uses toLocaleString() with the environment’s locale. In locales with different digit groupings or numerals, the substrings won’t appear and the test becomes flaky. Consider comparing to Intl.NumberFormat(undefined) output instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/lib/__tests__/formatters.test.ts, line 149:

<comment>These expectations assume western digit grouping and Latin numerals, but formatNumber uses `toLocaleString()` with the environment’s locale. In locales with different digit groupings or numerals, the substrings won’t appear and the test becomes flaky. Consider comparing to `Intl.NumberFormat(undefined)` output instead.</comment>

<file context>
@@ -0,0 +1,178 @@
+
+    it('formats with locale separators', () => {
+        const result = formatNumber(1234567);
+        expect(result).toContain('1');
+        expect(result).toContain('234');
+        expect(result).toContain('567');
</file context>
Fix with Cubic

});

it('accepts custom Intl options', () => {
expect(formatTimestamp(new Date(2025, 0, 15).toISOString(), { year: 'numeric' })).toContain('2025');
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 30, 2026

Choose a reason for hiding this comment

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

P3: This assertion hard-codes a Latin-digit year, but formatTimestamp uses toLocaleString with the default locale. In locales with non-Latin numerals, the output won’t contain "2025", so the test can fail depending on environment locale. Consider comparing against Intl.DateTimeFormat output for the current locale to keep the test deterministic.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/lib/__tests__/formatters.test.ts, line 43:

<comment>This assertion hard-codes a Latin-digit year, but formatTimestamp uses `toLocaleString` with the default locale. In locales with non-Latin numerals, the output won’t contain "2025", so the test can fail depending on environment locale. Consider comparing against Intl.DateTimeFormat output for the current locale to keep the test deterministic.</comment>

<file context>
@@ -0,0 +1,178 @@
+    });
+
+    it('accepts custom Intl options', () => {
+        expect(formatTimestamp(new Date(2025, 0, 15).toISOString(), { year: 'numeric' })).toContain('2025');
+    });
+
</file context>
Fix with Cubic

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@backend/app/services/sse/sse_service.py`:
- Around line 202-226: The notification loop in sse_service.py is vulnerable to
malformed Redis messages causing a validation/serialization exception to bubble
out and terminate the SSE stream; wrap the per-message processing that converts
RedisNotificationMessage into NotificationResponse (the block after await
subscription.get(RedisNotificationMessage) through yield {"event":
"notification", ...}) in a try/except that catches serialization/validation
errors (and general exceptions), logs the error with context (user_id and raw
redis_msg), and continues the loop so the subscription remains open; keep the
existing finally that calls asyncio.shield(subscription.close()) and logs
"Notification stream closed".

In `@frontend/src/lib/__tests__/formatters.test.ts`:
- Around line 33-48: Tests in formatters.test.ts are brittle because they assume
US-style output; update the assertions for formatTimestamp and formatNumber to
compare against the actual Intl output for the current runtime locale instead of
hard-coded substrings. For formatTimestamp tests, build an expected string via
new Intl.DateTimeFormat(undefined, <same options passed to formatTimestamp>)
formatted with the same Date/ISO input and assert equality (or strict
containment of that result); for formatNumber tests, use new
Intl.NumberFormat(undefined, <same options>) to produce the expected string for
the number and assert equality. Ensure you reference the same options used by
formatTimestamp and formatNumber so the tests are deterministic across locales.
🧹 Nitpick comments (2)
frontend/src/lib/notifications/__tests__/stream.test.ts (2)

129-139: Consider moving global stub cleanup to afterEach for robustness.

If an assertion fails before vi.unstubAllGlobals() on line 138, the global stub could theoretically leak to subsequent tests. While Vitest typically isolates tests well, moving cleanup to afterEach would make this test more resilient.

♻️ Suggested cleanup pattern
 describe('NotificationStream', () => {
     let callback: ReturnType<typeof vi.fn>;
 
     beforeEach(() => {
         callback = vi.fn();
         listenCallbacks = {};
         mockAbort.mockClear();
         notificationStream.disconnect();
     });
+
+    afterEach(() => {
+        vi.unstubAllGlobals();
+    });

     // ... other tests ...

     it('fires browser Notification when permission is granted', () => {
         const MockNotification = vi.fn();
         Object.defineProperty(MockNotification, 'permission', { value: 'granted' });
         vi.stubGlobal('Notification', MockNotification);
 
         notificationStream.connect(callback);
         listenCallbacks.onMessage!({ event: 'notification', data: JSON.stringify(validPayload()) });
 
         expect(MockNotification).toHaveBeenCalledWith('Test', { body: 'Hello', icon: '/favicon.png' });
-        vi.unstubAllGlobals();
     });
 });

113-119: Consider whether non-401 errors should set an error message for better UX.

The implementation intentionally skips setting an error message for non-401 response errors (e.g., 500)—only 401 errors are assigned error = 'Unauthorized'. This test correctly validates that behavior. However, the design choice to leave error null for server errors means users get no visible feedback when the connection fails due to a 500 or similar error, only a silent disconnection. If user feedback is important for these errors, consider setting an error message or log to help diagnose connection issues.

Comment on lines +33 to +48
describe('formatTimestamp', () => {
it.each([null, undefined, 'garbage'] as const)('returns N/A for %j', (input) => {
expect(formatTimestamp(input as string | null | undefined)).toBe('N/A');
});

it('formats ISO string', () => {
expect(formatTimestamp(new Date(2025, 0, 15).toISOString())).not.toBe('N/A');
});

it('accepts custom Intl options', () => {
expect(formatTimestamp(new Date(2025, 0, 15).toISOString(), { year: 'numeric' })).toContain('2025');
});

it('accepts Date object', () => {
expect(formatTimestamp(new Date(2025, 0, 15))).not.toBe('N/A');
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for locale/timezone pinning in test setup files
find . -type f \( -name "vitest.config.*" -o -name "setup*.ts" -o -name "setupTests.*" \) | head -20

Repository: HardMax71/Integr8sCode

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Search for locale/timezone/Intl configuration in test-related files
rg -n --type=ts 'process\.env\.(TZ|LANG|LC_ALL)|Intl\.(DateTimeFormat|NumberFormat)' --glob='**/vitest*.ts' --glob='**/setup*.ts' --glob='**/test*.ts' 2>/dev/null | head -30

Repository: HardMax71/Integr8sCode

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Examine the actual test file
wc -l frontend/src/lib/__tests__/formatters.test.ts

Repository: HardMax71/Integr8sCode

Length of output: 115


🏁 Script executed:

#!/bin/bash
# Read the relevant test file sections
cat -n frontend/src/lib/__tests__/formatters.test.ts | sed -n '1,60p'

Repository: HardMax71/Integr8sCode

Length of output: 2272


🏁 Script executed:

#!/bin/bash
# Look for the formatNumber test at lines 147-152
cat -n frontend/src/lib/__tests__/formatters.test.ts | sed -n '140,160p'

Repository: HardMax71/Integr8sCode

Length of output: 758


🏁 Script executed:

#!/bin/bash
# Find and examine the formatTimestamp and formatNumber implementations
fd -t f 'formatters\.ts' --exclude='*.test.ts' | head -5

Repository: HardMax71/Integr8sCode

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Check vitest configuration
cat frontend/vitest.config.ts

Repository: HardMax71/Integr8sCode

Length of output: 1005


🏁 Script executed:

#!/bin/bash
# Examine the formatters implementation
cat -n frontend/src/lib/formatters.ts

Repository: HardMax71/Integr8sCode

Length of output: 6310


🏁 Script executed:

#!/bin/bash
# Check vitest setup file for locale pinning
cat frontend/vitest.setup.ts 2>/dev/null || echo "File not found"

Repository: HardMax71/Integr8sCode

Length of output: 2607


Make locale-dependent assertions deterministic.

Both formatTimestamp() and formatNumber() use toLocaleString() for locale-aware formatting, but the tests use brittle assertions that don't validate actual behavior. With no locale pinned in test setup, these tests will fail on non-en-US locales.

For line 43, toContain('2025') only checks substring presence—the function might include month/day depending on locale (e.g., ja-JP: "2025年1月15日"). For lines 147-152, toContain('1'), toContain('234'), toContain('567') assume digit sequences appear consecutively, but locale-specific grouping (de-DE uses "1.234.567", fr-FR uses "1 234 567") may break these assertions.

Compare against actual Intl output for the runtime locale instead:

🔧 Proposed test hardening
 it('accepts custom Intl options', () => {
-    expect(formatTimestamp(new Date(2025, 0, 15).toISOString(), { year: 'numeric' })).toContain('2025');
+    const date = new Date(2025, 0, 15);
+    const expected = date.toLocaleString(undefined, { year: 'numeric' });
+    expect(formatTimestamp(date, { year: 'numeric' })).toBe(expected);
 });

 it('formats with locale separators', () => {
-    const result = formatNumber(1234567);
-    expect(result).toContain('1');
-    expect(result).toContain('234');
-    expect(result).toContain('567');
+    const expected = new Intl.NumberFormat().format(1234567);
+    expect(formatNumber(1234567)).toBe(expected);
 });
🤖 Prompt for AI Agents
In `@frontend/src/lib/__tests__/formatters.test.ts` around lines 33 - 48, Tests in
formatters.test.ts are brittle because they assume US-style output; update the
assertions for formatTimestamp and formatNumber to compare against the actual
Intl output for the current runtime locale instead of hard-coded substrings. For
formatTimestamp tests, build an expected string via new
Intl.DateTimeFormat(undefined, <same options passed to formatTimestamp>)
formatted with the same Date/ISO input and assert equality (or strict
containment of that result); for formatNumber tests, use new
Intl.NumberFormat(undefined, <same options>) to produce the expected string for
the number and assert equality. Ensure you reference the same options used by
formatTimestamp and formatNumber so the tests are deterministic across locales.

@sonarqubecloud
Copy link

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.

2 participants