Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds a dashboard feature: authenticated dashboard API endpoints (stats, sessions, session detail, trends, heart status), dashboard API key support and dependency, heart deactivation DB column and migration, extensive backend schemas/tests, OpenAPI updates, and a complete frontend dashboard client, UI, hooks, and types. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Frontend
participant API
participant DB
participant Cache
rect rgba(200,200,255,0.5)
User->>Frontend: Enter dashboard API key (login)
Frontend->>API: GET /api/v1/dashboard/stats (X-Dashboard-Key)
API->>API: verify_dashboard_access(header)
API->>DB: Query heart metadata & aggregated stats
DB-->>API: Return rows
API->>Cache: Store aggregated result (TTL 60s)
API-->>Frontend: DashboardStatsResponse
Frontend->>Frontend: Render dashboard UI
end
rect rgba(200,255,200,0.5)
User->>Frontend: Toggle heart active state
Frontend->>API: PATCH /api/v1/dashboard/heart/status (X-Dashboard-Key, payload)
API->>DB: Update heart.is_active / deactivated_at
API->>Cache: Invalidate cached entries for heart
API-->>Frontend: DashboardHeartStatusResponse
Frontend->>Frontend: Refetch stats/trends and update UI
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In `@backend/src/api/v1/endpoints/dashboard.py`:
- Around line 619-639: The trend query and DashboardTrendPoint construction are
producing empty-date points because date_trunc is applied to
SessionDb.started_at which can be NULL; update the query used in trend_query to
bucket by coalesce(SessionDb.started_at, SessionDb.created_at) (or add a filter
to exclude rows where SessionDb.started_at is NULL) so the bucket is never NULL,
and keep the DashboardTrendPoint mapping the same—look for the
SessionDb.started_at/created_at usage and the trend_query/group_by(bucket) logic
to apply coalesce or the additional where clause.
- Around line 280-287: The function signature for get_dashboard_sessions
currently uses Query(...) calls as default values (page, per_page, verdict,
sort_by, sort_order, search, date_from, date_to) which triggers Ruff B008;
change each parameter to use Annotated[...] metadata instead of calling Query in
the default, e.g. import Annotated and replace occurrences like "page: int =
Query(default=1, ge=1)" with "page: Annotated[int, Query(default=1, ge=1)] = 1"
(and similarly for per_page, verdict, sort_by, sort_order, search, date_from,
date_to) so that Query(...) is part of the type annotation rather than a default
value.
In `@frontend/src/components/dashboard/SessionFilters.tsx`:
- Around line 42-45: The sort change handler in SessionFilters.tsx currently
updates sort_by and sort_order but doesn't reset pagination, causing empty
pages; modify the onChange callback inside the onChange={(e) => { ... }} block
to also set page: 1 when calling the parent onChange (i.e., call onChange({
...filters, sort_by, sort_order, page: 1 })) so that changing sort always resets
to the first page; update any related helper/handler that reads filters to
expect page to be provided on sort changes.
- Around line 10-52: Add accessible labels for each filter control (the search
input, verdict select, date_from and date_to inputs, and the sort select) so
screen readers can identify them: update the JSX around the inputs that use
filters, value and onChange (the search input, the verdict <select>, the date
inputs bound to filters.date_from and filters.date_to, and the sort <select>
that parses sort_by/sort_order) to include either a <label htmlFor="..."> with
matching id on the control or an aria-label/aria-labelledby attribute with a
visually-hidden label element; ensure ids are unique (e.g., search-input,
verdict-select, date-from-input, date-to-input, sort-select) and preserve
existing value/onChange behavior when adding the labels.
In `@frontend/src/hooks/useDashboardAuth.ts`:
- Around line 1-18: The hook reads STORAGE_KEY from localStorage once via
useMemo causing stale apiKey/isAuthenticated; change useDashboardAuth to use
useState for apiKey and derive isAuthenticated from that state, initialize from
sessionStorage (use sessionStorage instead of localStorage), update the state
inside login(key) after verifyDashboardKey and inside logout() after removing
the key from sessionStorage so consumers re-render correctly, and simplify the
hook's return to only expose isAuthenticated, login, and logout (remove unused
returned values).
In `@frontend/src/hooks/useDashboardStats.ts`:
- Around line 4-15: The dashboard queries (useDashboardStats -> statsQuery and
trendsQuery) are using static queryKey values so cached data can be shared
across different signed-in admins; include the current auth identity (e.g.
dashboard API key, user id, or session token) in the queryKey for both
statsQuery and trendsQuery and pass it through to the query functions (e.g. add
authKey to ['dashboard-stats', authKey] and ['dashboard-trends', authKey,
period, days]) so React Query scopes cache per user; ensure you read the auth
value from the existing auth context/session and handle undefined safely (do not
omit the authKey or use a constant) so cache never spills between users.
In `@frontend/src/pages/dashboard/DashboardLogin.tsx`:
- Around line 32-44: Add an explicit label element for the password input (match
label htmlFor to the input id) so the input bound to value={key} and
onChange={(e) => setKey(e.target.value)} is properly announced by screen
readers; mark the input aria-invalid={Boolean(error)} and add aria-describedby
pointing to the error message id; render the error text with role="alert" or
aria-live="assertive" and that same id so updates are announced; update the JSX
around the input, the error rendering, and any related props in
DashboardLogin.tsx to use these attributes.
In `@frontend/src/pages/dashboard/DashboardSessionDetail.tsx`:
- Around line 39-42: The rendering hides valid zero-length sessions because it
uses a truthy check on data.session.duration_seconds; update the conditional in
DashboardSessionDetail (the JSX that references data.session.started_at and
data.session.duration_seconds) to test for null/undefined explicitly (e.g.,
duration_seconds !== null && duration_seconds !== undefined) so that 0 is
treated as a valid value and still renders the minutes/seconds string
(Math.floor(duration_seconds/60) and duration_seconds % 60).
In `@frontend/src/pages/dashboard/DashboardSessions.tsx`:
- Around line 31-52: The UI currently treats missing data as an empty state;
update the rendering logic in DashboardSessions.tsx to check
sessionsQuery.isError (and use sessionsQuery.error?.message) before falling back
to the empty-state branch so real query failures show an error message to the
user (instead of "No sessions yet"); modify the ternary/conditional that uses
sessionsQuery.isLoading and data to add an isError branch that renders a clear
error UI (referencing sessionsQuery, sessionsQuery.error, data.sessions,
SessionCard, Pagination and setFilters) and preserve existing loading,
data-present, and empty-state behaviors.
🧹 Nitpick comments (9)
frontend/src/components/dashboard/PauseToggle.tsx (1)
9-20: Add ARIA state for toggle semantics.This helps assistive tech understand the pressed/loading state without changing behavior.
♿ Suggested accessibility tweak
<button type="button" disabled={isLoading} + aria-pressed={active} + aria-busy={isLoading} + aria-label={active ? 'Pause interviews' : 'Activate interviews'} onClick={() => onToggle(!active)} className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-sm font-medium ${ activefrontend/src/components/dashboard/DashboardLayout.tsx (1)
14-17: Use replace navigation on logout to avoid back-navigation to protected routes.
This keeps history clean after logout and reduces confusing back-button behavior.♻️ Proposed change
- const logout = () => { - localStorage.removeItem('dashboard_api_key'); - navigate('/dashboard/login'); - }; + const logout = () => { + localStorage.removeItem('dashboard_api_key'); + navigate('/dashboard/login', { replace: true }); + };backend/src/api/v1/endpoints/sessions.py (1)
269-273: Keep paused-state messaging consistent across endpoints.
start_sessionnow uses “Screening is currently paused…”, whilepre_checkstill says “Interviews are currently paused.” Consider aligning the wording (or centralizing it) to avoid mixed UX copy.backend/src/dependencies.py (1)
22-47: Consider timing-safe comparison for API keys.
A timing-safe compare (secrets.compare_digest) slightly improves security posture and can be shared with the admin key check for consistency.🔒 Suggested change
+import secrets + async def verify_admin_key(api_key: str | None = Security(admin_key_header)) -> str: """Simple API key auth for admin endpoints.""" expected = config.ADMIN_API_KEY - if not api_key or not expected or api_key != expected: + if not api_key or not expected or not secrets.compare_digest(api_key, expected): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Invalid admin API key", ) return api_key @@ async def verify_dashboard_access( x_dashboard_key: str | None = Security(dashboard_key_header), ) -> str: """API key auth for dashboard endpoints.""" expected = config.DASHBOARD_API_KEY - if not x_dashboard_key or not expected or x_dashboard_key != expected: + if ( + not x_dashboard_key + or not expected + or not secrets.compare_digest(x_dashboard_key, expected) + ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized", ) return x_dashboard_keyfrontend/src/App.tsx (1)
17-22: Avoid hard-coding the dashboard storage key.
Line 18 duplicates the key string, which can drift from the value in the dashboard auth hook.♻️ Suggested refactor (shared constant)
-import type { ReactElement } from 'react'; +import type { ReactElement } from 'react'; +import { DASHBOARD_STORAGE_KEY } from './hooks/useDashboardAuth'; ... function RequireDashboardAuth({ children }: { children: ReactElement }) { - const hasKey = Boolean(localStorage.getItem('dashboard_api_key')); + const hasKey = Boolean(localStorage.getItem(DASHBOARD_STORAGE_KEY)); if (!hasKey) { return <Navigate to="/dashboard/login" replace />; } return children; }If
DASHBOARD_STORAGE_KEYisn’t exported yet, consider moving it to a small shared constants module used by both places.frontend/src/components/dashboard/TranscriptView.tsx (1)
15-35: Addaria-expanded/aria-controlsfor toggle accessibility.Small a11y improvement for screen readers.
♿ Suggested tweak
{transcript.map((turn) => { const isExpanded = Boolean(expanded[turn.turn]); const answer = turn.answer || ''; const shouldCollapse = answer.length > 260; const shown = shouldCollapse && !isExpanded ? `${answer.slice(0, 260)}...` : answer; + const answerId = `transcript-answer-${turn.turn}`; return ( <div key={turn.turn} className="rounded-lg border border-slate-200 p-3"> <p className="text-sm font-semibold text-slate-800">🤖 Q{turn.turn}: {turn.question}</p> - <p className="mt-2 whitespace-pre-wrap text-sm text-slate-700">👤 A{turn.turn}: {shown}</p> + <p id={answerId} className="mt-2 whitespace-pre-wrap text-sm text-slate-700"> + 👤 A{turn.turn}: {shown} + </p> {shouldCollapse && ( <button type="button" className="mt-1 text-xs font-medium text-violet-600" onClick={() => setExpanded((prev) => ({ ...prev, [turn.turn]: !isExpanded }))} + aria-expanded={isExpanded} + aria-controls={answerId} > {isExpanded ? 'Show less' : 'Show more'} </button> )}frontend/src/pages/dashboard/DashboardSessions.tsx (1)
17-18:debouncedFiltersisn’t debounced; remove or implement a real debounce.
useMemojust returns the same object reference and doesn’t throttle requests, so the name is misleading. Either remove it or replace with an actual debounce/deferred value.♻️ Suggested simplification
- const debouncedFilters = useMemo(() => filters, [filters]); - const sessionsQuery = useSessions(debouncedFilters); + const sessionsQuery = useSessions(filters);frontend/src/types/dashboard.ts (1)
135-143: Constrain SessionQueryParams to the allowed enum values.Using broad
stringtypes makes it easy to send invalid query values.♻️ Suggested typing refinement
export interface SessionQueryParams { page?: number; per_page?: number; - verdict?: string; - sort_by?: string; - sort_order?: string; + verdict?: 'date' | 'no_date' | 'pending'; + sort_by?: 'date' | 'score' | 'name'; + sort_order?: 'asc' | 'desc'; search?: string; date_from?: string; date_to?: string; }backend/src/api/v1/endpoints/dashboard.py (1)
62-74: Useget_heart_id()dependency or fail fast whenapp.state.heart_idis unset.
_resolve_heartsilently falls back to the most recently created heart instead of following the fail-fast pattern established byget_heart_id()in dependencies.py. While startup guaranteesapp.state.heart_idis set (via events.py lifespan), the fallback behavior is inconsistent with the codebase's defensive patterns and defeats the explicit 503 error strategy used elsewhere. Refactor to either inject heart_id via the existingget_heart_id()dependency or match that function's fail-fast approach.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@backend/src/tests/conftest.py`:
- Line 45: The DASHBOARD_API_KEY environment variable set with
os.environ.setdefault("DASHBOARD_API_KEY", "dashboard-test-key") does not match
the test constant VALID_DASHBOARD_KEY ("test-dashboard-key-12345"), causing auth
failures in fixtures like client and dashboard_headers; fix by making them
identical—either change the os.environ.setdefault call to use
VALID_DASHBOARD_KEY's value or update VALID_DASHBOARD_KEY to match the
environment value so both DASHBOARD_API_KEY and VALID_DASHBOARD_KEY reference
the same string used by the client and dashboard_headers fixtures.
In `@backend/src/tests/test_m7_dashboard_api_contract.py`:
- Around line 8-17: The autouse fixture _patch_dashboard_key sets
config_module.config.DASHBOARD_API_KEY after the FastAPI app/client is already
initialized which can cause async loop mismatch; change the approach so the
config is patched before app creation—either make the fixture session-scoped and
ensure it runs before the client fixture, remove autouse and add an explicit
dependency from the client or tests to _patch_dashboard_key, or set the
DASHBOARD_API_KEY environment variable prior to importing src.core.config so
that config_module.config.DASHBOARD_API_KEY is set at import time; reference the
_patch_dashboard_key fixture, monkeypatch, and
config_module.config.DASHBOARD_API_KEY when making the change.
In `@backend/src/tests/test_m7_dashboard_heart_status.py`:
- Around line 294-302: Replace the broad Exception assertions in tests
test_m7_status_014_patch_missing_active_field and
test_m7_status_015_patch_invalid_active_type so they catch
pydantic.ValidationError raised by
DashboardHeartStatusPatchRequest.model_validate, i.e., import
pydantic.ValidationError in the test file and use pytest.raises(ValidationError)
around the DashboardHeartStatusPatchRequest.model_validate(...) calls to assert
the specific validation error.
In `@backend/src/tests/test_m7_dashboard_sessions.py`:
- Around line 627-628: The assertion is tautological; replace it with a real
descending-order check by comparing the original list of scores (vals, derived
from out.sessions) to a version sorted in descending order or by asserting each
adjacent pair is non-increasing; specifically, compute vals =
[s.scores.aggregate for s in out.sessions if s.scores] and then assert vals ==
sorted(vals, reverse=True) (or assert all(vals[i] >= vals[i+1] for i in
range(len(vals)-1))) so the test actually verifies the endpoint returns scores
in descending order.
🧹 Nitpick comments (9)
backend/src/tests/test_m7_dashboard_integrity.py (2)
147-152: Test only validates fixture data, not actual endpoint isolation.This test asserts that all sessions in the fixture have the same
heart_id, which is expected since the fixture creates them that way. It doesn't actually test that the endpoint prevents cross-heart data leakage.To properly test isolation, you'd need to create sessions for a different heart and verify they're not returned when querying for
m7_seeded_heart.
207-221: SQL injection test uses mocked DB—doesn't validate actual SQL safety.The test passes a SQL injection payload as a search parameter, but since
_build_sessions_dbreturns a fake DB with predefined results, this doesn't actually test whether the endpoint properly sanitizes input against real SQL execution. The test will always pass regardless of actual SQL injection vulnerability.Consider adding an integration test with a real database or verifying that the search parameter is properly parameterized in the query builder.
backend/src/tests/test_m7_dashboard_api_contract.py (1)
130-136: Non-deterministic assertion weakens test validity.The assertion
resp.status_code in (200, 422)doesn't definitively test the expected behavior for an invalid date range (start after end). If the API should reject this as invalid input, assert422; if it should return empty results, assert200.backend/src/tests/test_m7_dashboard_auth.py (1)
9-9: Duplicate constant—consider importing from conftest.
VALID_DASHBOARD_KEYis also defined inconftest.py. Import it to maintain a single source of truth:♻️ Proposed refactor
-VALID_DASHBOARD_KEY = "test-dashboard-key-12345" +from tests.conftest import VALID_DASHBOARD_KEYbackend/src/tests/test_m7_dashboard_trends.py (1)
189-204: Consider consistent config patching approach.This test uses the same monkeypatch pattern that may cause async loop issues (as seen in pipeline failures for
test_m7_dashboard_api_contract.py). If those failures are resolved with a different approach, apply the same fix here.backend/src/tests/test_m7_dashboard_stats.py (4)
45-53: Test name is misleading.The test name suggests it verifies an HTTP 200 status code, but it calls
get_dashboard_statsdirectly without making an HTTP request. Consider renaming to something liketest_m7_stats_001_succeeds_with_valid_author add an actual HTTP test via the test client if HTTP status verification is intended.
105-127: Consider adding coverage for SCORING and SCORED statuses.Per the implementation,
completed_sessionsaggregates counts forCOMPLETED,SCORING, andSCOREDstatuses. This test only exercises theCOMPLETEDstatus. Adding a test case withSCORINGorSCOREDrows would verify that all three statuses are correctly included incompleted_sessions.
249-267: Test name doesn't match test behavior.The test is named
score_distribution_boundary_values, which implies testing scores at tier boundaries (e.g., 79.9 vs 80.0). However, the test merely verifies that pre-aggregated tier counts are returned correctly—similar to test_012. If boundary testing is intended, it would require providing scores near thresholds and verifying the SQL CASE expression categorization, which this mock-based approach cannot do.Consider renaming to
test_m7_stats_013_score_distribution_all_tiers_presentor adding an integration test with actual scores at boundary values.
326-342: Consider adding an edge case for booking_rate when total_dates is zero.The implementation handles division by zero by returning
booking_rate = 0.0whentotal_datesis 0. Adding a test case to verify this behavior would strengthen coverage:`@pytest.mark.asyncio` async def test_m7_stats_019_booking_rate_zero_when_no_dates( dashboard_request, m7_seeded_heart, make_fake_db_m7, fake_result_builder_m7 ): dashboard_request.app.state.heart_id = m7_seeded_heart.id db = _build_stats_db( make_fake_db_m7, fake_result_builder_m7, booking_total=5, heart=m7_seeded_heart, ) out = await get_dashboard_stats(dashboard_request, "ok", db) assert out.bookings.booking_rate == 0.0
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@backend/src/tests/test_m7_dashboard_sessions.py`:
- Around line 682-711: The test test_m7_sessions_026_sort_by_name currently
asserts the suitor names using a set, which ignores order; change the assertion
to verify ordering by extracting the names in order (e.g., [s.suitor_name for s
in out.sessions]) and assert equality to the expected ordered list ["Alex",
"Bella", "Charlie"] so the sort_by="name", sort_order="asc" behavior is properly
tested.
🧹 Nitpick comments (2)
backend/src/tests/test_m7_dashboard_sessions.py (1)
35-59: Drop unused*argsto keep the helper signature clean.
_call_sessionsdoesn’t use*args, and Ruff flagged it; removing it clarifies intent.♻️ Proposed fix
-async def _call_sessions(func, request, auth, db, *args, **kwargs): +async def _call_sessions(func, request, auth, db, **kwargs):backend/src/tests/test_m7_dashboard_auth.py (1)
61-76: Remove unusedclientfixture parameters.These tests don’t use the fixture, and Ruff flagged it.
♻️ Proposed fix
-async def test_m7_auth_006_auth_does_not_affect_public_endpoints(client): +async def test_m7_auth_006_auth_does_not_affect_public_endpoints(): @@ -async def test_m7_auth_007_auth_does_not_affect_suitor_endpoints(client): +async def test_m7_auth_007_auth_does_not_affect_suitor_endpoints():
| @pytest.mark.asyncio | ||
| async def test_m7_sessions_026_sort_by_name( | ||
| dashboard_request, | ||
| m7_seeded_heart, | ||
| m7_sample_sessions, | ||
| make_fake_db_m7, | ||
| fake_result_builder_m7, | ||
| m7_sample_suitors, | ||
| ): | ||
| dashboard_request.app.state.heart_id = m7_seeded_heart.id | ||
| m7_sample_suitors[0].name = "Charlie" | ||
| m7_sample_suitors[1].name = "Alex" | ||
| m7_sample_suitors[2].name = "Bella" | ||
| rows = _rows_for_sessions(m7_sample_sessions[:3], m7_sample_suitors[:3]) | ||
| db = _build_db_for_sessions( | ||
| make_fake_db_m7, | ||
| fake_result_builder_m7, | ||
| total=3, | ||
| rows=rows, | ||
| heart=m7_seeded_heart, | ||
| ) | ||
| out = await _call_sessions( | ||
| get_dashboard_sessions, | ||
| dashboard_request, | ||
| "ok", | ||
| db, | ||
| sort_by="name", | ||
| sort_order="asc", | ||
| ) | ||
| assert {s.suitor_name for s in out.sessions} == {"Alex", "Bella", "Charlie"} |
There was a problem hiding this comment.
Assert actual order for name sorting.
Using a set only checks membership and won’t fail if the order is wrong.
🔧 Proposed fix
- assert {s.suitor_name for s in out.sessions} == {"Alex", "Bella", "Charlie"}
+ assert [s.suitor_name for s in out.sessions] == ["Alex", "Bella", "Charlie"]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @pytest.mark.asyncio | |
| async def test_m7_sessions_026_sort_by_name( | |
| dashboard_request, | |
| m7_seeded_heart, | |
| m7_sample_sessions, | |
| make_fake_db_m7, | |
| fake_result_builder_m7, | |
| m7_sample_suitors, | |
| ): | |
| dashboard_request.app.state.heart_id = m7_seeded_heart.id | |
| m7_sample_suitors[0].name = "Charlie" | |
| m7_sample_suitors[1].name = "Alex" | |
| m7_sample_suitors[2].name = "Bella" | |
| rows = _rows_for_sessions(m7_sample_sessions[:3], m7_sample_suitors[:3]) | |
| db = _build_db_for_sessions( | |
| make_fake_db_m7, | |
| fake_result_builder_m7, | |
| total=3, | |
| rows=rows, | |
| heart=m7_seeded_heart, | |
| ) | |
| out = await _call_sessions( | |
| get_dashboard_sessions, | |
| dashboard_request, | |
| "ok", | |
| db, | |
| sort_by="name", | |
| sort_order="asc", | |
| ) | |
| assert {s.suitor_name for s in out.sessions} == {"Alex", "Bella", "Charlie"} | |
| `@pytest.mark.asyncio` | |
| async def test_m7_sessions_026_sort_by_name( | |
| dashboard_request, | |
| m7_seeded_heart, | |
| m7_sample_sessions, | |
| make_fake_db_m7, | |
| fake_result_builder_m7, | |
| m7_sample_suitors, | |
| ): | |
| dashboard_request.app.state.heart_id = m7_seeded_heart.id | |
| m7_sample_suitors[0].name = "Charlie" | |
| m7_sample_suitors[1].name = "Alex" | |
| m7_sample_suitors[2].name = "Bella" | |
| rows = _rows_for_sessions(m7_sample_sessions[:3], m7_sample_suitors[:3]) | |
| db = _build_db_for_sessions( | |
| make_fake_db_m7, | |
| fake_result_builder_m7, | |
| total=3, | |
| rows=rows, | |
| heart=m7_seeded_heart, | |
| ) | |
| out = await _call_sessions( | |
| get_dashboard_sessions, | |
| dashboard_request, | |
| "ok", | |
| db, | |
| sort_by="name", | |
| sort_order="asc", | |
| ) | |
| assert [s.suitor_name for s in out.sessions] == ["Alex", "Bella", "Charlie"] |
🤖 Prompt for AI Agents
In `@backend/src/tests/test_m7_dashboard_sessions.py` around lines 682 - 711, The
test test_m7_sessions_026_sort_by_name currently asserts the suitor names using
a set, which ignores order; change the assertion to verify ordering by
extracting the names in order (e.g., [s.suitor_name for s in out.sessions]) and
assert equality to the expected ordered list ["Alex", "Bella", "Charlie"] so the
sort_by="name", sort_order="asc" behavior is properly tested.
Summary by CodeRabbit
New Features
Bug Fixes
Chores
Tests