Skip to content

perf(test): cache admin sign-in tokens in event.test.ts#5261

Open
h30s wants to merge 20 commits intoPalisadoesFoundation:developfrom
h30s:perf/cache-admin-token-event-tests
Open

perf(test): cache admin sign-in tokens in event.test.ts#5261
h30s wants to merge 20 commits intoPalisadoesFoundation:developfrom
h30s:perf/cache-admin-token-event-tests

Conversation

@h30s
Copy link
Copy Markdown

@h30s h30s commented Feb 18, 2026

What kind of change does this PR introduce?

Performance optimization (test suite)

Issue Number:

Fixes #5258

Snapshots/Videos:

N/A - This is a test performance optimization with no visual changes.

If relevant, did you update the documentation?

No documentation updates needed. This is an internal test optimization.

Summary

This PR optimizes test performance in test/graphql/types/Query/event.test.ts by implementing token caching for admin authentication.

Problem:
The helper functions getAdminToken() and getAdminTokenAndUserId() were performing fresh GraphQL sign-in mutations on every call. With 13+ calls throughout the test suite, this created significant performance overhead and increased test execution time.

Solution:
Refactored the helper functions to use the existing ensureAdminAuth() caching mechanism that was already present in the file but not being utilized by these helpers.

Changes:

  • Modified getAdminTokenAndUserId() to call ensureAdminAuth() instead of performing fresh sign-ins
  • Modified getAdminToken() to call ensureAdminAuth() instead of performing fresh sign-ins
  • Removed 19 lines of redundant sign-in code

Performance Impact:

  • Before: 13+ sign-in mutations per test run
  • After: 1 sign-in mutation per test run (cached via beforeAll hook)
  • Benefit: Significant reduction in test execution time and improved CI/CD feedback loop

Test Isolation:
This optimization maintains proper test isolation because:

  • Admin tokens are immutable within a test run
  • The admin user is not modified by any tests
  • Each test still receives a valid authentication token
  • Follows the project's coding guidelines for beforeAll usage with immutable setup

Does this PR introduce a breaking change?

No. This is a test-only optimization with no changes to production code or test behavior.

Checklist

CodeRabbit AI Review

  • I have reviewed and addressed all critical issues flagged by CodeRabbit AI
  • I have implemented or provided justification for each non-critical suggestion
  • I have documented my reasoning in the PR comments where CodeRabbit AI suggestions were not implemented

Test Coverage

  • I have written tests for all new changes/features (N/A - optimization only)
  • I have verified that test coverage meets or exceeds 95% (no coverage changes)
  • I have run the test suite locally and all tests pass

Other information

This optimization was identified during the review of PR #5188. The caching mechanism (ensureAdminAuth()) was already present in the file but the helper functions were not using it, resulting in unnecessary performance overhead.

Have you read the contributing guide?

Yes

Summary by CodeRabbit

  • Refactor

    • Centralized suite-scoped admin authentication with cached admin token/user and helpers for token retrieval; moved cleanup to suite-level cascade paths.
  • Tests

    • More deterministic, reliable tests: fixed base dates, structured setup/teardown, centralized invite-only scenario, revised auth flows, and guaranteed cleanup via table deletions or cascades.
    • Reduced reliance on fake timers; tightened assertions.
  • Chores

    • Test environment: added test Redis dependency and frontend URL to test server config.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 18, 2026

Our Pull Request Approval Process

This PR will be reviewed according to our:

  1. Palisadoes Contributing Guidelines

  2. AI Usage Policy

Your PR may be automatically closed if:

  1. Our PR template isn't filled in correctly
  2. You haven't correctly linked your PR to an issue

👉 https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue

Thanks for contributing!

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Replaces per-test admin sign-ins with a suite-scoped cached admin auth (ensureAdminAuth) initialized in beforeAll; adds deterministic test-data helpers, centralized cleanup via registered functions and direct DB deletions, and a test server env var. No public API changes.

Changes

Cohort / File(s) Summary
Event tests — auth, helpers & cleanup
test/graphql/types/Query/event.test.ts
Replace per-call admin sign-ins with suite-scoped ensureAdminAuth caching adminToken/adminUserId (initialized in beforeAll); add getAdminToken/getAdminTokenAndUserId; introduce helpers (createTestOrganization, createTestEvent, createUserAndReturnStaleToken, setupInviteOnlyTestScenario, etc.); adopt deterministic baseDate handling and remove many per-test explicit deletions in favor of cascade or direct drizzleClient organizationsTable deletes; centralize cleanup via testCleanupFunctions and adjust timer/authorization flows.
Docker compose (testing)
docker/compose.testing.yaml
Add redis-test to api service depends_on with condition: service_healthy.
Test server env
test/server.ts
Add API_FRONTEND_URL set to http://localhost:3000 in test server configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'perf(test): cache admin sign-in tokens in event.test.ts' directly and specifically describes the main optimization—caching admin tokens in tests to improve performance.
Description check ✅ Passed The PR description follows the template structure and provides comprehensive details including the problem statement, solution approach, performance impact, test isolation rationale, and completed checklists.
Linked Issues check ✅ Passed The PR successfully implements the core objective of issue #5258: eliminating repeated admin sign-in mutations by replacing per-call sign-ins with ensureAdminAuth() caching, reducing sign-ins from 13+ to 1 per test suite.
Out of Scope Changes check ✅ Passed All changes are scoped to the stated optimization objective. The docker/compose.testing.yaml redis dependency and test server API_FRONTEND_URL changes are minor test infrastructure updates directly supporting the main test refactoring.

✏️ 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
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can customize the tone of the review comments and chat replies.

Configure the tone_instructions setting to customize the tone of the review comments and chat replies. For example, you can set the tone to Act like a strict teacher, Act like a pirate and more.

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.37%. Comparing base (675939c) to head (d844036).
⚠️ Report is 194 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #5261      +/-   ##
===========================================
+ Coverage    93.69%   95.37%   +1.67%     
===========================================
  Files          861      906      +45     
  Lines        71332    78321    +6989     
  Branches      9893     9144     -749     
===========================================
+ Hits         66838    74695    +7857     
+ Misses        4494     3626     -868     
Flag Coverage Δ
install 72.72% <ø> (?)
integration 94.89% <ø> (+1.72%) ⬆️
unit 99.01% <ø> (+1.89%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 193 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
test/graphql/types/Query/event.test.ts (5)

344-356: ⚠️ Potential issue | 🟠 Major

Leftover inline sign-in defeats the PR's stated optimization goal

This test still performs a raw Query_signIn mutation instead of calling getAdminToken(). The PR claims to reduce sign-ins from "13+ per test run to a single sign-in," but this test is one of the sign-ins that was supposed to be eliminated.

🛠️ Proposed fix
-        test("provided event ID is not a valid ULID.", async () => {
-            const signInResult = await mercuriusClient.query(Query_signIn, {
-                variables: {
-                    input: {
-                        emailAddress:
-                            server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
-                        password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
-                    },
-                },
-            });
-
-            const authToken = signInResult.data?.signIn?.authenticationToken;
-            assertToBeNonNullish(authToken);
+        test("provided event ID is not a valid ULID.", async () => {
+            const authToken = await getAdminToken();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 344 - 356, This test is
still performing a raw Query_signIn call (signInResult / authToken) instead of
using the shared helper; replace the inline mercuriusClient.query(Query_signIn,
...) sign-in logic with a call to getAdminToken() and use its returned token for
authentication; update any variables (signInResult, authToken) to use the token
returned by getAdminToken and remove the Query_signIn dependency so the test
uses the central single-sign-in optimization.

444-475: ⚠️ Potential issue | 🔴 Critical

Missing cleanup — organization and event are never deleted

This top-level test creates an organization (line 450) and an event (line 456) with no cleanup. The outer suite has no afterEach, so both entities accumulate in the database across runs.

Consider wrapping the setup/teardown in a try/finally block, or adding an afterEach at the outer suite level that deletes known test-owned organizations/events.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 444 - 475, The test
"admin user can access event from any organization" creates resources (via
createTestOrganization and createTestEvent) but never cleans them up; wrap the
setup and assertions in a try/finally (or add an outer afterEach) and in the
finally block delete the created event and organization using your teardown
helpers or API calls (use the returned event.id and organization.id), ensuring
cleanup runs even on assertion failures; keep the
mercuriusClient.query/Query_event/assertToBeNonNullish logic intact and only add
the finally cleanup that calls the appropriate delete functions for the event
and organization.

62-64: ⚠️ Potential issue | 🟠 Major

beforeAll for admin auth token caching conflicts with maintainer policy

Using beforeAll to cache the admin auth token is the crux of this PR's optimization, but it directly contradicts an established repo policy. If the cached token becomes invalid mid-run (e.g., due to a session expiry or a preceding test that modifies auth state in ways not anticipated), every subsequent test that depends on it fails—and the failure is silent and hard to attribute. More critically, this pattern is explicitly discouraged for sharded CI environments where shard ordering and resource state can vary.

The guideline is clear: beforeAll is acceptable only for "truly immutable shared setup (e.g., test utilities, constants)"; auth tokens must be created per-test.

Based on learnings: "In talawa-api tests, creating shared resources like authentication tokens in beforeAll is discouraged. Auth tokens should be created per-test in beforeEach or within individual tests to prevent cascading failures and improve test isolation in sharded environments." (palisadoes, 2026-02-07). As per coding guidelines for test/**: "Auth tokens should be created per-test to avoid cascading failures."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 62 - 64, The test suite
currently calls ensureAdminAuth() in a beforeAll (the cached admin token), which
violates the repo policy requiring per-test auth creation; change the hook to
beforeEach(async () => { await ensureAdminAuth(); }) or call ensureAdminAuth()
inside each affected test so each test gets a fresh token, remove any
global/cached token usage, and ensure tests that reference the admin token (via
the existing helper used by these tests) use the freshly-created token rather
than a shared one.

479-592: ⚠️ Potential issue | 🔴 Critical

testCleanupFunctions is never populated in three tests — cleanup hook does nothing

The "Additional event tests" suite has an afterEach that drains testCleanupFunctions, but the following tests never push anything into it:

Test Resources created without cleanup
"handles events with past dates" (line 491) createTestOrganization result + direct eventsTable insert
"handles multi-day events" (line 539) createTestOrganization result + Mutation_createEvent result
"creates a never-ending recurring event" (line 593) createTestOrganization result + recurring event template + instances

Additionally, the invite-only tests (lines 659–1195) push cleanup for the regular user but not for the organization or event created in each test.

Each test should push cleanup functions for every resource it creates:

// Example for "handles events with past dates"
testCleanupFunctions.push(async () => {
  await server.drizzleClient.delete(eventsTable).where(eq(eventsTable.id, pastEventId));
  // organization deletion (if a delete mutation exists)
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 479 - 592, The afterEach
drains testCleanupFunctions but many tests never register cleanup; for each test
that calls createTestOrganization, server.drizzleClient.insert(eventsTable) or
mercuriusClient.mutate(Mutation_createEvent) (and the recurring event/template
flow used by the "creates a never-ending recurring event" tests), push
corresponding cleanup lambdas onto testCleanupFunctions that delete the created
event rows (use eventsTable/eq on id via server.drizzleClient.delete), remove
any recurring templates/instances, and delete the created organization (and user
if created) so resources are removed; also update the invite-only tests that
currently only push cleanup for the regular user to also push cleanup for the
organization and event created in those tests.

381-442: ⚠️ Potential issue | 🔴 Critical

Missing user cleanup — created user is never deleted

This top-level test creates a regular user via Mutation_createUser (line 391) but never calls Mutation_deleteUser. There is no afterEach at the outer suite("Query field event", ...) level, so the user leaks into the database after every run. In sharded CI, this accumulates across shards.

🧹 Proposed fix
+    const userId = user.user?.id;
+    assertToBeNonNullish(userId);

     // Try to access event as regular user
     const queryResult = await mercuriusClient.query(Query_event, {
       ...
     });

     expect(queryResult.data.event).toBeNull();
     expect(queryResult.errors).toEqual(...);
+
+    // Cleanup: delete the created user
+    await mercuriusClient.mutate(Mutation_deleteUser, {
+      headers: { authorization: `bearer ${adminAuthToken}` },
+      variables: { input: { id: userId } },
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 381 - 442, The test
creates a regular user via Mutation_createUser (stored in variable user) but
never deletes it; fix by recording created user.id (e.g., push to a local
createdUserIds array) and ensure you call Mutation_deleteUser for each id after
the test (or add an afterEach in the surrounding suite) using the adminAuthToken
in the headers; reference Mutation_createUser, user.id, and Mutation_deleteUser
(and adminAuthToken) so deletion runs even on failures (use finally/afterEach)
to prevent leaked users across tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 344-356: This test is still performing a raw Query_signIn call
(signInResult / authToken) instead of using the shared helper; replace the
inline mercuriusClient.query(Query_signIn, ...) sign-in logic with a call to
getAdminToken() and use its returned token for authentication; update any
variables (signInResult, authToken) to use the token returned by getAdminToken
and remove the Query_signIn dependency so the test uses the central
single-sign-in optimization.
- Around line 444-475: The test "admin user can access event from any
organization" creates resources (via createTestOrganization and createTestEvent)
but never cleans them up; wrap the setup and assertions in a try/finally (or add
an outer afterEach) and in the finally block delete the created event and
organization using your teardown helpers or API calls (use the returned event.id
and organization.id), ensuring cleanup runs even on assertion failures; keep the
mercuriusClient.query/Query_event/assertToBeNonNullish logic intact and only add
the finally cleanup that calls the appropriate delete functions for the event
and organization.
- Around line 62-64: The test suite currently calls ensureAdminAuth() in a
beforeAll (the cached admin token), which violates the repo policy requiring
per-test auth creation; change the hook to beforeEach(async () => { await
ensureAdminAuth(); }) or call ensureAdminAuth() inside each affected test so
each test gets a fresh token, remove any global/cached token usage, and ensure
tests that reference the admin token (via the existing helper used by these
tests) use the freshly-created token rather than a shared one.
- Around line 479-592: The afterEach drains testCleanupFunctions but many tests
never register cleanup; for each test that calls createTestOrganization,
server.drizzleClient.insert(eventsTable) or
mercuriusClient.mutate(Mutation_createEvent) (and the recurring event/template
flow used by the "creates a never-ending recurring event" tests), push
corresponding cleanup lambdas onto testCleanupFunctions that delete the created
event rows (use eventsTable/eq on id via server.drizzleClient.delete), remove
any recurring templates/instances, and delete the created organization (and user
if created) so resources are removed; also update the invite-only tests that
currently only push cleanup for the regular user to also push cleanup for the
organization and event created in those tests.
- Around line 381-442: The test creates a regular user via Mutation_createUser
(stored in variable user) but never deletes it; fix by recording created user.id
(e.g., push to a local createdUserIds array) and ensure you call
Mutation_deleteUser for each id after the test (or add an afterEach in the
surrounding suite) using the adminAuthToken in the headers; reference
Mutation_createUser, user.id, and Mutation_deleteUser (and adminAuthToken) so
deletion runs even on failures (use finally/afterEach) to prevent leaked users
across tests.

Copy link
Copy Markdown

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (9)
test/graphql/types/Query/event.test.ts (9)

349-425: ⚠️ Potential issue | 🔴 Critical

finally block only deletes the user; org and event are permanently leaked

createTestOrganization and createTestEvent at lines 352–356 create an organization, an admin membership, and an event. The finally block (lines 412–424) only issues Mutation_deleteUser. Neither the event nor the organization is ever deleted. The outer suite("Query field event") has no afterEach, so there is no secondary safety net.

🛡️ Proposed fix
  } finally {
+   // Delete event before organization (dependency order)
+   await server.drizzleClient.delete(eventsTable).where(eq(eventsTable.id, event.id));
+   // Delete organization (add Mutation_deleteOrganization if available)
    await mercuriusClient.mutate(Mutation_deleteUser, {
      headers: { authorization: `bearer ${adminAuthToken}` },
      variables: { input: { id: user.user.id } },
    });
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 349 - 425, The test
leaks resources because createTestOrganization and createTestEvent (creating
organization.id and event.id) are not cleaned up in the finally block which only
calls Mutation_deleteUser; update the finally block to also delete the created
event and organization using the adminAuthToken (call the corresponding
mutations for deleting the event and the organization or use existing teardown
helpers), perform deletions in safe order (delete event first, then
organization, then user), and guard each deletion against null ids/errors so
cleanup always attempts all removals.

40-48: ⚠️ Potential issue | 🟡 Minor

Remove redundant assertToBeNonNullish calls

The if (!authToken || !userId) guard at lines 40–46 already throws when either value is falsy, so assertToBeNonNullish(authToken) and assertToBeNonNullish(userId) at lines 47–48 are unreachable dead assertions. Same redundancy exists at line 83 inside createTestOrganization.

🧹 Proposed fix
     if (!authToken || !userId) {
       throw new Error(
         `Failed to sign in as admin. Errors: ${JSON.stringify(
           signInResult.errors,
         )}`,
       );
     }
-    assertToBeNonNullish(authToken);
-    assertToBeNonNullish(userId);
     return { authToken, userId };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 40 - 48, Remove the
unreachable redundant null-check assertions: delete the two
assertToBeNonNullish(authToken) and assertToBeNonNullish(userId) calls
immediately after the if-block that throws when (!authToken || !userId) in the
test setup, and likewise remove the similar redundant assertToBeNonNullish call
inside createTestOrganization; the thrown Error guard already guarantees
non-null values so only keep the initial guard/throw logic (refer to variables
authToken, userId and the createTestOrganization helper to locate the
assertions).

1003-1064: ⚠️ Potential issue | 🔴 Critical

"Event creator" test creates org+event with absolutely no cleanup

Unlike the other invite-only tests that at least push a user-deletion callback (albeit after assertions), this test registers nothing in testCleanupFunctions. The organization, admin membership, and invite-only event created at lines 1008–1034 are leaked on every test run.

🛡️ Proposed fix
  test("event creator can access invite-only event", async () => {
    const { authToken: adminAuthToken, userId: adminUserId } =
      await getAdminTokenAndUserId();
    const organization = await createTestOrganization(adminAuthToken, adminUserId);
+   testCleanupFunctions.push(async () => {
+     // Delete event then organization (dependency order)
+     // Add Mutation_deleteOrganization / direct eventsTable delete here
+   });
    const createEventResult = await mercuriusClient.mutate(Mutation_createEvent, { ... });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 1003 - 1064, This test
leaks created resources; after creating the organization with
createTestOrganization and after successfully creating the invite-only event
(createEvent via mercuriusClient.mutate producing eventId), push appropriate
cleanup callbacks into testCleanupFunctions to delete the event and the
organization (and any membership) so the admin membership and org/event are
removed after the test; reference createTestOrganization, eventId,
createEventResult, and testCleanupFunctions when adding the cleanup entries.

241-270: ⚠️ Potential issue | 🔴 Critical

Org and event created by setupTestData are never added to testCleanupFunctions

setupTestData at line 243 creates an organization, an admin membership, and an event in the database. Although this suite has an afterEach that drains testCleanupFunctions, nothing is ever pushed to that array here, so all three records leak permanently.

🛡️ Proposed fix
     const { authToken, userId } = await getAdminTokenAndUserId();
-    const { event } = await setupTestData(authToken, userId);
+    const { organization, event } = await setupTestData(authToken, userId);
+    testCleanupFunctions.push(async () => {
+      // Delete event then organization (reverse dependency order)
+      await server.drizzleClient.delete(eventsTable).where(eq(eventsTable.id, event.id));
+      // Add Mutation_deleteOrganization here if available in documentNodes
+    });
     const deletedUser = await createAndDeleteTestUser(authToken);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 241 - 270, The test
leaks DB records because setupTestData creates an organization, admin membership
and event but neither returns cleanup handles nor adds them to
testCleanupFunctions; update the test (or setupTestData) so the created
resources are registered for cleanup: either have setupTestData return the
created organization, membership and event and then push cleanup functions/ids
into the shared testCleanupFunctions array from the test, or modify
setupTestData to itself push appropriate cleanup closures into
testCleanupFunctions; reference setupTestData and testCleanupFunctions when
making this change so the org/membership/event get removed in afterEach.

757-762: ⚠️ Potential issue | 🔴 Critical

testCleanupFunctions.push placed after assertions — cleanup silently skipped on assertion failure

This pattern appears in all four invite-only tests: testCleanupFunctions.push at lines 757–762, 883–888, 995–1000, and 1172–1177 is called after the expect(…) assertions. If any assertion throws, the push is never reached, Vitest's afterEach drains an empty array, and the created user is leaked permanently.

Additionally, none of these tests register cleanup for the organization or event they create, so those records always leak regardless of test outcome.

Fix: move each testCleanupFunctions.push call to immediately after resource creation (before any queries or assertions), and extend it to also delete the event and organization.

🛡️ Representative fix for "registered-but-not-invited" test (apply same pattern to the other three)
  const regularUserId = regularUser.user.id;
  const regularUserToken = regularUser.authenticationToken;

+ // Register cleanup immediately after creation, before any assertions
+ testCleanupFunctions.push(async () => {
+   await mercuriusClient.mutate(Mutation_deleteUser, {
+     headers: { authorization: `bearer ${adminAuthToken}` },
+     variables: { input: { id: regularUserId } },
+   });
+   // Also delete event and organization here (dependency order: event first)
+ });

  // Add user to organization
  await mercuriusClient.mutate(Mutation_createOrganizationMembership, { ... });
  // ...
  // Query and assert
  const queryResult = await mercuriusClient.query(Query_event, { ... });
  expect(queryResult.data?.event).not.toBeNull();

- // Cleanup
- testCleanupFunctions.push(async () => {
-   await mercuriusClient.mutate(Mutation_deleteUser, { ... });
- });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 757 - 762, Move the test
cleanup registration (testCleanupFunctions.push) to immediately after creating
each resource (user, event, organization) instead of after assertions so cleanup
still runs if an assertion fails; locate the blocks around create calls that
later call mercuriusClient.mutate and Mutation_deleteUser (with
adminAuthToken/regularUserId) and register cleanup there; extend each pushed
cleanup function to also delete the created event and organization (call the
appropriate mutate operations for deleting event and organization using their
created IDs) so users, events, and organizations are all removed in afterEach;
apply this change consistently for the four invite-only tests referencing
testCleanupFunctions.push at the shown sites.

131-134: 🧹 Nitpick | 🔵 Trivial

Date.now() usage violates test determinism guideline

Per coding guidelines, Date.now() should be avoided in tests in favour of fixed UTC timestamps or mocked timers. This helper is the root source of Date.now() usage that propagates throughout the file (also direct at lines 484–485, 536–539, 583–584, 696–699, and more).

Because createEvent rejects past startAt values, using a fixed timestamp requires either a sufficiently far-future constant (e.g., "2099-01-01T00:00:00Z") or vi.useFakeTimers():

♻️ Option — far-future constant (simplest, no fake timers needed)
- const startAt = new Date(Date.now() + startOffset);
- const endAt = new Date(startAt.getTime() + durationInHours * 60 * 60 * 1000);
+ const BASE_FUTURE_TS = new Date("2099-01-01T00:00:00Z").getTime();
+ const startAt = new Date(BASE_FUTURE_TS + startOffset);
+ const endAt = new Date(startAt.getTime() + durationInHours * 60 * 60 * 1000);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 131 - 134, The test uses
Date.now() to compute startAt/endAt which breaks determinism; replace that usage
by using a fixed base timestamp or mocked timers: either define a constant fixed
UTC base (e.g., a far-future Date like "2099-01-01T00:00:00Z") and compute
startAt/endAt from that, or call vi.useFakeTimers() and set the fake clock to a
known timestamp before creating startAt/endAt; update the helper that computes
startAt/endAt (and any direct Date.now() calls around createEvent) to use the
fixed base or fake timer so createEvent's past-date rejection is avoided and
tests remain deterministic.

980-1001: ⚠️ Potential issue | 🟡 Minor

Incomplete error assertion — missing error extension code check

This test only asserts queryResult.data?.event is null, but doesn't verify the error extension code the way the structurally identical unauthorized-access test at line 349–411 does. A null event could result from any error path; asserting the specific code confirms the denial is for the right reason.

🛡️ Proposed addition
  expect(queryResult.data?.event).toBeNull();
+
+ expect(queryResult.errors).toEqual(
+   expect.arrayContaining<TalawaGraphQLFormattedError>([
+     expect.objectContaining<TalawaGraphQLFormattedError>({
+       extensions: expect.objectContaining({
+         code: "unauthorized_action_on_arguments_associated_resources",
+       }),
+       message: expect.any(String),
+       path: ["event"],
+     }),
+   ]),
+ );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 980 - 1001, The test for
unauthorized regular member using Query_event currently only checks that
queryResult.data?.event is null; update the test to also assert the GraphQL
error extension code (from queryResult.errors?.[0].extensions.code) equals the
same denial code used in the other unauthorized-access test (e.g., 'FORBIDDEN')
so the failure reason is verified; locate the Query_event invocation and add an
assertion on queryResult.errors to mirror the existing pattern in the other
test.

427-458: ⚠️ Potential issue | 🔴 Critical

Remove stale developer note and add missing cleanup for org/event

Two separate issues here:

  1. Line 427: The comment // Then refactor the "admin user can access event" test to: is a leftover refactoring note — remove it before merging.
  2. Lines 433–439: createTestOrganization and createTestEvent write an organization, admin membership, and event to the database. The outer suite("Query field event") has no afterEach, and this test has no try/finally, so all three records are never cleaned up.
🛡️ Proposed fix — wrap body in try/finally
- // Then refactor the "admin user can access event" test to:
  test("admin user can access event from any organization", async () => {
    const { authToken: adminAuthToken, userId: adminUserId } =
      await getAdminTokenAndUserId();
    const organization = await createTestOrganization(adminAuthToken, adminUserId);
    const event = await createTestEvent(adminAuthToken, organization.id);
+   try {
      const queryResult = await mercuriusClient.query(Query_event, { ... });
      // ... assertions ...
+   } finally {
+     await server.drizzleClient.delete(eventsTable).where(eq(eventsTable.id, event.id));
+     // Delete organization via Mutation_deleteOrganization if available
+   }
  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 427 - 458, Remove the
stale comment line "// Then refactor the \"admin user can access event\" test
to:" and wrap the test body in a try/finally so created DB records are always
cleaned up; after creating organization via createTestOrganization(...) and
event via createTestEvent(...), use the finally block to delete the created
records (e.g. call the project’s cleanup helpers or issue deletions for event.id
and organization.id using the adminAuthToken/adminUserId) to ensure both the
event and organization (and any admin membership the helpers created) are
removed even if assertions fail.

474-639: ⚠️ Potential issue | 🔴 Critical

Three tests in "Additional event tests" create org/event records without registering any cleanup

None of the following tests push to testCleanupFunctions, despite the suite having an afterEach that drains it:

  • Lines 474–520 ("past dates"): createTestOrganization + a direct drizzleClient.insert into eventsTable at lines 480–493. The raw DB insert bypasses the mutation layer and is especially important to clean up.
  • Lines 522–574 ("multi-day"): createTestOrganization + Mutation_createEvent.
  • Lines 576–639 ("recurring"): createTestOrganization + recurring Mutation_createEvent.

All three accumulate organizations, memberships, and events on every test run, making the suite a high-risk source of database bloat in sharded CI.

🛡️ Representative fix pattern (apply to all three tests)
  test("handles events with past dates correctly", async () => {
    const { authToken, userId } = await getAdminTokenAndUserId();
    const organization = await createTestOrganization(authToken, userId);
+   testCleanupFunctions.push(async () => {
+     // Clean up past event inserted directly, then the organization
+     await server.drizzleClient
+       .delete(eventsTable)
+       .where(eq(eventsTable.id, pastEventId));
+     // Delete organization via Mutation_deleteOrganization if available
+   });
    // ... rest of test body ...

Note: pastEventId must be declared before the push so it's in scope — declare it with let before the testCleanupFunctions.push call.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 474 - 639, These three
tests (the past-dates test using server.drizzleClient.insert, the multi-day test
using Mutation_createEvent, and the recurring event test using
Mutation_createEvent) create organizations/events without registering cleanup;
declare any IDs you need (e.g., change const pastEventId to let pastEventId
before insertion) and push corresponding cleanup functions onto
testCleanupFunctions immediately after creating those IDs/results: for the raw
DB insert (server.drizzleClient.insert) push a function that deletes the event
by pastEventId (and the created organization and membership) and for both
mutation-created events (results from Mutation_createEvent returning
createEvent.id) push cleanup functions that delete the event instance(s) and the
organization/membership using the returned id(s); ensure each push references
the correct symbols (pastEventId, multiDayEvent.id, baseRecurringEventId,
createEventResult.data.createEvent) so the afterEach drain removes those DB
records.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 349-425: The test leaks resources because createTestOrganization
and createTestEvent (creating organization.id and event.id) are not cleaned up
in the finally block which only calls Mutation_deleteUser; update the finally
block to also delete the created event and organization using the adminAuthToken
(call the corresponding mutations for deleting the event and the organization or
use existing teardown helpers), perform deletions in safe order (delete event
first, then organization, then user), and guard each deletion against null
ids/errors so cleanup always attempts all removals.
- Around line 40-48: Remove the unreachable redundant null-check assertions:
delete the two assertToBeNonNullish(authToken) and assertToBeNonNullish(userId)
calls immediately after the if-block that throws when (!authToken || !userId) in
the test setup, and likewise remove the similar redundant assertToBeNonNullish
call inside createTestOrganization; the thrown Error guard already guarantees
non-null values so only keep the initial guard/throw logic (refer to variables
authToken, userId and the createTestOrganization helper to locate the
assertions).
- Around line 1003-1064: This test leaks created resources; after creating the
organization with createTestOrganization and after successfully creating the
invite-only event (createEvent via mercuriusClient.mutate producing eventId),
push appropriate cleanup callbacks into testCleanupFunctions to delete the event
and the organization (and any membership) so the admin membership and org/event
are removed after the test; reference createTestOrganization, eventId,
createEventResult, and testCleanupFunctions when adding the cleanup entries.
- Around line 241-270: The test leaks DB records because setupTestData creates
an organization, admin membership and event but neither returns cleanup handles
nor adds them to testCleanupFunctions; update the test (or setupTestData) so the
created resources are registered for cleanup: either have setupTestData return
the created organization, membership and event and then push cleanup
functions/ids into the shared testCleanupFunctions array from the test, or
modify setupTestData to itself push appropriate cleanup closures into
testCleanupFunctions; reference setupTestData and testCleanupFunctions when
making this change so the org/membership/event get removed in afterEach.
- Around line 757-762: Move the test cleanup registration
(testCleanupFunctions.push) to immediately after creating each resource (user,
event, organization) instead of after assertions so cleanup still runs if an
assertion fails; locate the blocks around create calls that later call
mercuriusClient.mutate and Mutation_deleteUser (with
adminAuthToken/regularUserId) and register cleanup there; extend each pushed
cleanup function to also delete the created event and organization (call the
appropriate mutate operations for deleting event and organization using their
created IDs) so users, events, and organizations are all removed in afterEach;
apply this change consistently for the four invite-only tests referencing
testCleanupFunctions.push at the shown sites.
- Around line 131-134: The test uses Date.now() to compute startAt/endAt which
breaks determinism; replace that usage by using a fixed base timestamp or mocked
timers: either define a constant fixed UTC base (e.g., a far-future Date like
"2099-01-01T00:00:00Z") and compute startAt/endAt from that, or call
vi.useFakeTimers() and set the fake clock to a known timestamp before creating
startAt/endAt; update the helper that computes startAt/endAt (and any direct
Date.now() calls around createEvent) to use the fixed base or fake timer so
createEvent's past-date rejection is avoided and tests remain deterministic.
- Around line 980-1001: The test for unauthorized regular member using
Query_event currently only checks that queryResult.data?.event is null; update
the test to also assert the GraphQL error extension code (from
queryResult.errors?.[0].extensions.code) equals the same denial code used in the
other unauthorized-access test (e.g., 'FORBIDDEN') so the failure reason is
verified; locate the Query_event invocation and add an assertion on
queryResult.errors to mirror the existing pattern in the other test.
- Around line 427-458: Remove the stale comment line "// Then refactor the
\"admin user can access event\" test to:" and wrap the test body in a
try/finally so created DB records are always cleaned up; after creating
organization via createTestOrganization(...) and event via createTestEvent(...),
use the finally block to delete the created records (e.g. call the project’s
cleanup helpers or issue deletions for event.id and organization.id using the
adminAuthToken/adminUserId) to ensure both the event and organization (and any
admin membership the helpers created) are removed even if assertions fail.
- Around line 474-639: These three tests (the past-dates test using
server.drizzleClient.insert, the multi-day test using Mutation_createEvent, and
the recurring event test using Mutation_createEvent) create organizations/events
without registering cleanup; declare any IDs you need (e.g., change const
pastEventId to let pastEventId before insertion) and push corresponding cleanup
functions onto testCleanupFunctions immediately after creating those
IDs/results: for the raw DB insert (server.drizzleClient.insert) push a function
that deletes the event by pastEventId (and the created organization and
membership) and for both mutation-created events (results from
Mutation_createEvent returning createEvent.id) push cleanup functions that
delete the event instance(s) and the organization/membership using the returned
id(s); ensure each push references the correct symbols (pastEventId,
multiDayEvent.id, baseRecurringEventId, createEventResult.data.createEvent) so
the afterEach drain removes those DB records.

Copy link
Copy Markdown

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
test/graphql/types/Query/event.test.ts (6)

56-108: 🧹 Nitpick | 🔵 Trivial

createTestOrganization creates membership but has no cleanup path.

This helper creates both an organization and an admin membership. Since it's called by most tests, any cleanup strategy needs to account for both. Consider returning the membership ID as well, or relying on cascade deletes from organization deletion to clean up associated memberships.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 56 - 108, The helper
createTestOrganization currently creates an organization and an admin membership
but does not return or expose the created membership for cleanup; update
createTestOrganization to also return the membership ID (or the full membership
object) so tests can delete the membership explicitly, or alternatively ensure
callers delete the organization and rely on cascade deletes; locate the creation
of the membership in the mutate call using Mutation_createOrganizationMembership
and return membershipResult.data (or
membershipResult.data.createOrganizationMembership.id) alongside the
organization so teardown can clean both entities.

238-267: ⚠️ Potential issue | 🟠 Major

Missing cleanup: organization and event leak from setupTestData.

setupTestData creates an organization and event but no cleanup functions are pushed to testCleanupFunctions. The afterEach at line 205 runs but has nothing to clean. This leaves orphaned records in the database after every run.

Proposed fix: register cleanup in testCleanupFunctions
 			test("client triggering the graphql operation has no existing user associated to their authentication context.", async () => {
 				const { authToken, userId } = await getAdminTokenAndUserId();
 				const { event } = await setupTestData(authToken, userId);
+
+				// Register cleanup for the organization and event created by setupTestData
+				testCleanupFunctions.push(async () => {
+					await server.drizzleClient
+						.delete(eventsTable)
+						.where((fields, operators) => operators.eq(fields.id, event.id));
+					// Organization cleanup would require a delete mutation or direct DB delete
+				});
+
 				const deletedUser = await createAndDeleteTestUser(authToken);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 238 - 267, The test
leaks organization and event records because setupTestData creates an
organization and event but does not register cleanup functions with
testCleanupFunctions; update setupTestData (or the test) to push appropriate
async cleanup callbacks into the existing testCleanupFunctions array that delete
the created organization and event (use the created event.id and organization id
returned by setupTestData), so afterEach will remove those records; reference
the setupTestData helper, the testCleanupFunctions array and the afterEach hook
when adding these cleanup registrations.

663-1215: ⚠️ Potential issue | 🟠 Major

Invite-only event visibility suite: widespread resource leaks.

All five tests in this suite create organizations and events (and some create memberships, registrations, attendees) that are never cleaned up. Only user deletions are registered in testCleanupFunctions. The "event creator can access" test (lines 1039–1100) has zero cleanup.

Accumulated leaks per suite run: ≥5 organizations, ≥5 events, ≥4 memberships, plus registrations and attendees. Per the coding guidelines, this is a high-risk pattern for sharded CI execution (>10 entities with no cleanup).

Consider:

  1. Importing the organization and event tables for direct DB cleanup (events already imported).
  2. Pushing DB deletes for events into testCleanupFunctions in every test.
  3. Adding organization table cleanup (may require importing organizationsTable).
  4. Cleaning up in reverse dependency order: attendees → registrations → events → memberships → organizations → users.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 663 - 1215, The tests in
the "Invite-only event visibility" suite leak DB entities; ensure every test
that calls createTestOrganization, Mutation_createEvent,
Mutation_createOrganizationMembership, Mutation_registerForEvent or
Mutation_inviteEventAttendee registers cleanup steps into testCleanupFunctions
to delete attendees → registrations → events → memberships → organizations →
users in reverse dependency order; import and use the eventsTable (already
imported) and organizationsTable (add import if missing) and push direct DB
delete calls (or GraphQL delete mutations) for event ids, organization ids,
membership ids, registration/attendee records after creating them so each test
(including the "event creator can access" case) registers its cleanup
immediately after creation.

28-54: ⚠️ Potential issue | 🟠 Major

PR objective not achieved: admin tokens are not cached.

The PR title and linked issue #5258 state the goal is to cache admin sign-in tokens and reduce sign-in mutations from 13+ to 1. However, getAdminTokenAndUserId() performs a fresh Query_signIn on every call, and it's invoked ~14 times across the file — yielding no reduction.

Note: A retrieved learning from the maintainer states that per-test auth creation is preferred over beforeAll caching for sharded CI isolation. If the maintainer's intent has shifted to per-test auth, the PR title, description, and issue #5258 should be updated to reflect the actual change (helper extraction / refactoring), not a performance optimization that doesn't exist.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 28 - 54,
getAdminTokenAndUserId currently runs Query_signIn via mercuriusClient.query
every time it’s called (and getAdminToken simply delegates to it), so admin
tokens are not cached; change this by introducing a module-level cache (e.g.,
cachedAdminAuthToken and cachedAdminUserId) that getAdminTokenAndUserId checks
before calling Query_signIn and only performs
mercuriusClient.query(Query_signIn, ...) when cache is empty, populating the
cache on first successful sign-in; alternatively, if the intended behavior is
per-test auth creation rather than caching, update the PR title/description and
issue reference to reflect refactor-only intent instead of claiming token
caching/optimization.

346-458: ⚠️ Potential issue | 🟠 Major

Missing cleanup for organizations and events in standalone tests.

Both tests (lines 346–424 and 426–458) create organizations and events that are never deleted. The comments claim cleanup mutations aren't available, but the test at line 485–488 already demonstrates direct DB deletion of events via server.drizzleClient.delete(eventsTable). The same approach can be used here.

Per the coding guidelines, tests creating database entities must clean them up to prevent data accumulation in sharded CI. These two tests alone leak 2 organizations and 2 events per run.

Proposed fix for "admin user can access event" test (lines 426–458)
 	test("admin user can access event from any organization", async () => {
 		const { authToken: adminAuthToken, userId: adminUserId } =
 			await getAdminTokenAndUserId();
 
-		// Create test organization
-		const organization = await createTestOrganization(
-			adminAuthToken,
-			adminUserId,
-		);
-
-		// Create test event using helper
-		const event = await createTestEvent(adminAuthToken, organization.id);
-
-		// Try to access event as admin
-		const queryResult = await mercuriusClient.query(Query_event, {
-			headers: {
-				authorization: `bearer ${adminAuthToken}`,
-			},
-			variables: {
-				input: {
-					id: event.id,
-				},
-			},
-		});
-
-		const queriedEvent = queryResult.data.event;
-		expect(queriedEvent).not.toBeNull();
-		assertToBeNonNullish(queriedEvent);
-		expect(queriedEvent.id).toBe(event.id);
-		expect(queryResult.errors).toBeUndefined();
-		// Note: Event and organization cleanup would require additional mutations
-		// which are not currently available in the test helpers
+		const organization = await createTestOrganization(adminAuthToken, adminUserId);
+		const event = await createTestEvent(adminAuthToken, organization.id);
+
+		try {
+			const queryResult = await mercuriusClient.query(Query_event, {
+				headers: {
+					authorization: `bearer ${adminAuthToken}`,
+				},
+				variables: {
+					input: {
+						id: event.id,
+					},
+				},
+			});
+
+			const queriedEvent = queryResult.data.event;
+			expect(queriedEvent).not.toBeNull();
+			assertToBeNonNullish(queriedEvent);
+			expect(queriedEvent.id).toBe(event.id);
+			expect(queryResult.errors).toBeUndefined();
+		} finally {
+			await server.drizzleClient
+				.delete(eventsTable)
+				.where((fields, operators) => operators.eq(fields.id, event.id));
+			// TODO: add organization cleanup via direct DB delete
+		}
 	});

As per coding guidelines: "Tests creating database entities (orgs, users, events, etc.) MUST clean them up" and "Flag missing database cleanup as HIGH PRIORITY blocking issue."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 346 - 458, The tests
create organizations and events via createTestOrganization and createTestEvent
but never remove them; add cleanup that deletes the created event(s) and
organization(s) using the existing direct DB deletion pattern (e.g., call
server.drizzleClient.delete(eventsTable) and
server.drizzleClient.delete(organizationsTable) or equivalent) in the finally
blocks (or after the test) for both the "unauthorized regular user..." and
"admin user can access event..." tests; ensure you reference the created entity
IDs (event.id and organization.id) and perform deletions with the same client
(server.drizzleClient) used elsewhere in the suite so the database rows are
removed after each test.

480-503: 🧹 Nitpick | 🔵 Trivial

Leverage cascade deletes to simplify organization cleanup.

The events table schema specifies onDelete: "cascade" on the organizationId foreign key (src/drizzle/tables/events.ts:68). This means deleting an organization automatically deletes all its events. Since each test creates an organization, you can simplify cleanup across the file by deleting organizations directly—which will cascade-delete all associated events. This pattern is already used in 20+ other test files (e.g., test/graphql/types/Comment/post.test.ts, test/graphql/types/Mutation/updateVenue.test.ts). Import organizationsTable and replace event-by-event cleanup with organization cleanup to make the cleanup story significantly more maintainable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 480 - 503, Replace the
per-event direct delete with a cascade-backed organization delete: import
organizationsTable, and in the cleanup pushed to testCleanupFunctions call
server.drizzleClient.delete(organizationsTable).where(...) using the same
organization.id (instead of deleting eventsTable by pastEventId). Keep the
existing registration pattern (testCleanupFunctions.push(...)) and remove the
explicit eventsTable.delete for pastEventId so the DB cascade on organizationId
handles event removals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 636-640: The no-op cleanup pushed into testCleanupFunctions leaves
recurring events and their instances orphaned; replace the empty async function
with a real cleanup that calls the GraphQL mutations to remove the recurring
event, all generated instances, and the test organization (e.g. invoke
deleteEvent (or deleteRecurringEvent) with the recurring event's id and
deleteOrganization with the organization id), or call existing helper cleanup
functions used elsewhere in tests, ensuring the function awaits removal of
instances and the base event before returning so DB rows are not leaked.
- Around line 560-564: The pushed async cleanup in testCleanupFunctions is a
no-op (only comments) and must be replaced or removed; either implement real
deletion for the created event/organization (use the same approach as the other
cleanup in this file — e.g., call the project test DB helper or
prisma.event.delete/prisma.organization.delete with the created
eventId/organizationId inside the async () => { ... } you push) or remove the
empty push so tests don't pretend cleanup is handled; locate the no-op push to
testCleanupFunctions and replace it with the concrete delete calls referencing
the created resource IDs.
- Around line 1-3: Add vi to the vitest imports and call vi.restoreAllMocks()
inside each existing afterEach hook to prevent test pollution; specifically
update the import line to include vi (e.g., import { afterEach, expect, suite,
test, vi } from "vitest") and insert vi.restoreAllMocks() in the afterEach
callbacks used in this file (the afterEach function declarations).

---

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 56-108: The helper createTestOrganization currently creates an
organization and an admin membership but does not return or expose the created
membership for cleanup; update createTestOrganization to also return the
membership ID (or the full membership object) so tests can delete the membership
explicitly, or alternatively ensure callers delete the organization and rely on
cascade deletes; locate the creation of the membership in the mutate call using
Mutation_createOrganizationMembership and return membershipResult.data (or
membershipResult.data.createOrganizationMembership.id) alongside the
organization so teardown can clean both entities.
- Around line 238-267: The test leaks organization and event records because
setupTestData creates an organization and event but does not register cleanup
functions with testCleanupFunctions; update setupTestData (or the test) to push
appropriate async cleanup callbacks into the existing testCleanupFunctions array
that delete the created organization and event (use the created event.id and
organization id returned by setupTestData), so afterEach will remove those
records; reference the setupTestData helper, the testCleanupFunctions array and
the afterEach hook when adding these cleanup registrations.
- Around line 663-1215: The tests in the "Invite-only event visibility" suite
leak DB entities; ensure every test that calls createTestOrganization,
Mutation_createEvent, Mutation_createOrganizationMembership,
Mutation_registerForEvent or Mutation_inviteEventAttendee registers cleanup
steps into testCleanupFunctions to delete attendees → registrations → events →
memberships → organizations → users in reverse dependency order; import and use
the eventsTable (already imported) and organizationsTable (add import if
missing) and push direct DB delete calls (or GraphQL delete mutations) for event
ids, organization ids, membership ids, registration/attendee records after
creating them so each test (including the "event creator can access" case)
registers its cleanup immediately after creation.
- Around line 28-54: getAdminTokenAndUserId currently runs Query_signIn via
mercuriusClient.query every time it’s called (and getAdminToken simply delegates
to it), so admin tokens are not cached; change this by introducing a
module-level cache (e.g., cachedAdminAuthToken and cachedAdminUserId) that
getAdminTokenAndUserId checks before calling Query_signIn and only performs
mercuriusClient.query(Query_signIn, ...) when cache is empty, populating the
cache on first successful sign-in; alternatively, if the intended behavior is
per-test auth creation rather than caching, update the PR title/description and
issue reference to reflect refactor-only intent instead of claiming token
caching/optimization.
- Around line 346-458: The tests create organizations and events via
createTestOrganization and createTestEvent but never remove them; add cleanup
that deletes the created event(s) and organization(s) using the existing direct
DB deletion pattern (e.g., call server.drizzleClient.delete(eventsTable) and
server.drizzleClient.delete(organizationsTable) or equivalent) in the finally
blocks (or after the test) for both the "unauthorized regular user..." and
"admin user can access event..." tests; ensure you reference the created entity
IDs (event.id and organization.id) and perform deletions with the same client
(server.drizzleClient) used elsewhere in the suite so the database rows are
removed after each test.
- Around line 480-503: Replace the per-event direct delete with a cascade-backed
organization delete: import organizationsTable, and in the cleanup pushed to
testCleanupFunctions call
server.drizzleClient.delete(organizationsTable).where(...) using the same
organization.id (instead of deleting eventsTable by pastEventId). Keep the
existing registration pattern (testCleanupFunctions.push(...)) and remove the
explicit eventsTable.delete for pastEventId so the DB cascade on organizationId
handles event removals.

Comment thread test/graphql/types/Query/event.test.ts Outdated
Comment thread test/graphql/types/Query/event.test.ts Outdated
Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
test/graphql/types/Query/event.test.ts (3)

282-355: ⚠️ Potential issue | 🟡 Minor

Missing afterEach with vi.restoreAllMocks() in the "database query failure" suite.

The two other suites in this file both include an afterEach that calls vi.restoreAllMocks() (lines 207–217 and 482–492). The suite at lines 282–355 has no afterEach at all. While no mocks or DB entities are created here today, the pattern should be consistent across suites to prevent future test pollution if a spy is ever added.

🛠️ Proposed addition
 	suite(
 		`results in a graphql error with database query failure...`,
 		() => {
+			afterEach(() => {
+				vi.restoreAllMocks();
+			});
+
 			test("fails with ULID of wrong length", async () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 282 - 355, Add an
afterEach hook calling vi.restoreAllMocks() inside the suite whose title begins
with `results in a graphql error with database query failure in the "errors"
field and "null" as the value of "data.event" field if` so it mirrors the other
suites; place the afterEach directly inside that suite block (next to existing
tests for wrong-length ULID, invalid characters, and "invalid-id") to ensure
spies/mocks are restored after each test (use vi.restoreAllMocks()).

30-56: ⚠️ Potential issue | 🟠 Major

Per-call sign-in is correct per guidelines, but the PR's stated caching objective is unimplemented.

getAdminTokenAndUserId() and getAdminToken() perform a fresh Query_signIn mutation on every call — there is no caching, no memoization, and no call to ensureAdminAuth(). Counting invocations across the file yields 14 separate sign-in round-trips per test run (lines 242, 286, 309, 332, 358, 440, 494, 551, 613, 688, 815, 945, 1080, 1151), which is more than the 13+ the linked issue #5258 called out.

This is worth flagging for two reasons:

  1. PR description is inaccurate. The PR title says "cache admin sign-in tokens" and the description says these helpers "call the existing ensureAdminAuth() caching mechanism". Neither is true in the code.
  2. The linked issue's acceptance criterion — reduce sign-in mutations to a single call — is not met.

Ironically, the per-call pattern is compliant with the coding guidelines ("Auth tokens should be created per-test to avoid cascading failures"), so the code itself is fine. But either the PR description should be corrected to reflect the actual change (per-test sign-in replacing a shared beforeAll token), or the caching originally described should be implemented.

#!/bin/bash
# Verify whether ensureAdminAuth exists in the codebase and whether it was removed from this file.
rg -n "ensureAdminAuth" --type=ts -C 3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 30 - 56, The helpers
getAdminTokenAndUserId and getAdminToken execute Query_signIn on every call (14
sign-ins per run) despite the PR claiming tokens are cached; either update the
PR text to remove the caching claim or implement caching by routing calls
through the existing ensureAdminAuth (or add a simple memoization wrapper) so
Query_signIn runs only once per test run — locate
getAdminTokenAndUserId/getAdminToken in this test file and replace direct
mercuriusClient.query(Query_signIn, ...) usage with a call to ensureAdminAuth()
(or a module-level cached promise) and return the authToken/userId from that
single shared result.

357-476: ⚠️ Potential issue | 🟠 Major

Organization leaks if createTestEvent throws — try/finally starts too late.

In both standalone tests, the try/finally cleanup block (newly added at lines 387 and 452) is placed after the organization (and in the first test, after the user) has already been created. If createTestEvent(...) at line 364 (or line 450) throws — which the helper does on mutation errors — the organization is never cleaned up because the finally block is never entered.

Affected:

  • Lines 357–437 ("unauthorized regular user"): org created at line 360, event at line 364; try starts at line 387.
  • Lines 439–476 ("admin user can access event"): org created at line 444, event at line 450; try starts at line 452.
🛡️ Proposed fix — wrap from the point of the first potentially-leaking resource
-	const organization = await createTestOrganization(adminAuthToken, adminUserId);
-	const event = await createTestEvent(adminAuthToken, organization.id);
-
-	// Create a regular user...
-	const userResult = await mercuriusClient.mutate(Mutation_createUser, { ... });
-	const user = userResult.data?.createUser;
-	assertToBeNonNullish(user);
-	assertToBeNonNullish(user.authenticationToken);
-	assertToBeNonNullish(user.user);
-
-	try {
-		// ... query + assertions ...
-	} finally {
-		// delete user, delete org
-	}
+	const organization = await createTestOrganization(adminAuthToken, adminUserId);
+	try {
+		const event = await createTestEvent(adminAuthToken, organization.id);
+		// Create a regular user...
+		const userResult = await mercuriusClient.mutate(Mutation_createUser, { ... });
+		const user = userResult.data?.createUser;
+		assertToBeNonNullish(user);
+		assertToBeNonNullish(user.authenticationToken);
+		assertToBeNonNullish(user.user);
+		try {
+			// ... query + assertions ...
+		} finally {
+			await mercuriusClient.mutate(Mutation_deleteUser, { ... });
+		}
+	} finally {
+		await server.drizzleClient
+			.delete(organizationsTable)
+			.where(eq(organizationsTable.id, organization.id));
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 357 - 476, The tests
createTestOrganization(...) is called before the try/finally, so if
createTestEvent(...) throws the organization (and in the first test the created
user) will leak; move the try block to start immediately after creating the
organization (i.e., wrap from just after createTestOrganization through the rest
of the test) so the finally always runs, hoist variables (organization, event,
user) to outer scope so they are visible in finally, and in the finally block
conditionally call the same cleanup steps you already have (mutate
Mutation_deleteUser for user.user.id if user exists, and
server.drizzleClient.delete(organizationsTable).where(eq(organizationsTable.id,
organization.id)) if organization exists) to ensure proper teardown even on
createTestEvent failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 578-584: The cleanup callbacks for created resources (e.g.,
organization from createTestOrganization and users from regularUser.user) must
be pushed into testCleanupFunctions immediately after each creation (before any
subsequent operations like Mutation_createEvent, assertToBeNonNullish, addToOrg,
etc.) — modify the tests (e.g., the "handles multi-day events" and the other
listed tests) so that right after createTestOrganization returns you call
testCleanupFunctions.push(async () => { /* delete organization via
server.drizzleClient.delete(organizationsTable).where(eq(organizationsTable.id,
organization.id)) */ }); and likewise push a separate cleanup immediately after
creating any user (delete via Mutation_deleteUser or equivalent) so the
afterEach reversal deletes user first then org and prevents orphaned records.

---

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 282-355: Add an afterEach hook calling vi.restoreAllMocks() inside
the suite whose title begins with `results in a graphql error with database
query failure in the "errors" field and "null" as the value of "data.event"
field if` so it mirrors the other suites; place the afterEach directly inside
that suite block (next to existing tests for wrong-length ULID, invalid
characters, and "invalid-id") to ensure spies/mocks are restored after each test
(use vi.restoreAllMocks()).
- Around line 30-56: The helpers getAdminTokenAndUserId and getAdminToken
execute Query_signIn on every call (14 sign-ins per run) despite the PR claiming
tokens are cached; either update the PR text to remove the caching claim or
implement caching by routing calls through the existing ensureAdminAuth (or add
a simple memoization wrapper) so Query_signIn runs only once per test run —
locate getAdminTokenAndUserId/getAdminToken in this test file and replace direct
mercuriusClient.query(Query_signIn, ...) usage with a call to ensureAdminAuth()
(or a module-level cached promise) and return the authToken/userId from that
single shared result.
- Around line 357-476: The tests createTestOrganization(...) is called before
the try/finally, so if createTestEvent(...) throws the organization (and in the
first test the created user) will leak; move the try block to start immediately
after creating the organization (i.e., wrap from just after
createTestOrganization through the rest of the test) so the finally always runs,
hoist variables (organization, event, user) to outer scope so they are visible
in finally, and in the finally block conditionally call the same cleanup steps
you already have (mutate Mutation_deleteUser for user.user.id if user exists,
and
server.drizzleClient.delete(organizationsTable).where(eq(organizationsTable.id,
organization.id)) if organization exists) to ensure proper teardown even on
createTestEvent failures.

Comment thread test/graphql/types/Query/event.test.ts Outdated
Copy link
Copy Markdown

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
test/graphql/types/Query/event.test.ts (4)

112-158: ⚠️ Potential issue | 🟠 Major

Date.now() violates the test determinism guidelines.

Line 130 (new Date(Date.now() + startOffset)) and every inline use of new Date(Date.now() + …) across the test suite violate the coding guideline under Timezone Safety & Test Determinism: "Avoid Date.now() in tests (use fixed timestamps or mock timers)".

Use vi.useFakeTimers({ now: new Date("2030-01-01T00:00:00Z") }) (paired with vi.useRealTimers() in afterEach) as the base, and derive all timestamps from the fixed anchor — or pass pre-computed ISO strings as inputs.

📅 Example fix for `createTestEvent` helper
-		const startAt = new Date(Date.now() + startOffset);
-		const endAt = new Date(
-			startAt.getTime() + durationInHours * 60 * 60 * 1000,
-		);
+		// Use a fixed far-future UTC anchor instead of Date.now()
+		const BASE_FUTURE_TS = new Date("2030-06-01T00:00:00Z").getTime();
+		const startAt = new Date(BASE_FUTURE_TS + startOffset);
+		const endAt = new Date(
+			startAt.getTime() + durationInHours * 60 * 60 * 1000,
+		);

As per coding guidelines, "All test dates must use fixed UTC timestamps (e.g., '2025-01-01T10:00:00Z')".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 112 - 158, The
createTestEvent helper uses Date.now() (new Date(Date.now() + startOffset))
which breaks test determinism; change it to derive timestamps from a fixed
anchor or from the test's fake timer instead: either accept a baseNow
ISO/string/Date parameter on createTestEvent and compute startAt/endAt from that
fixed base, or ensure tests call vi.useFakeTimers({ now: new
Date("2030-01-01T00:00:00Z") }) and then compute startAt as new
Date(baseNow.getTime() + startOffset) inside createTestEvent (replace Date.now()
usages). Update callers to pass the fixed anchor or enable the fake timers in
setup and restore with vi.useRealTimers() in afterEach.

167-200: ⚠️ Potential issue | 🟡 Minor

createAndDeleteTestUser leaks the user if deletion fails.

If Mutation_deleteUser (line 188) throws (e.g., auth error), the function propagates the error but leaves the user record in the database. The caller's afterEach cleanup only covers the organization, not this user.

Wrap the deletion in a try/catch that re-throws after noting the leak, or register the user's cleanup into the suite-level testCleanupFunctions before attempting deletion.

Additionally, the name createAndDeleteTestUser obscures the intent (generating a stale auth token for a deleted user). A clearer name like createUserAndReturnStaleToken improves readability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 167 - 200, The helper
createAndDeleteTestUser currently creates a user then attempts to delete it with
Mutation_deleteUser and can leak the user if deletion throws; fix by registering
a cleanup for the created user into the suite-level testCleanupFunctions
immediately after creation (using the created user's id) and then attempt
deletion inside a try/catch that re-throws after noting the leak, or at minimum
wrap the Mutation_deleteUser call in try/catch and invoke the registered cleanup
on error to guarantee eventual deletion; also rename the function to
createUserAndReturnStaleToken to clarify intent (and update callers) so it
documents that it returns a token for a deleted user.

357-437: ⚠️ Potential issue | 🟠 Major

Cleanup gap: resource creation is outside the try block.

The try block at line 387 covers only the query and assertions. Organization (line 360), event (line 364), user (lines 367–380), and the three assertToBeNonNullish guards (lines 382–385) all execute before the try. If any of those throw, the finally clause never runs and all already-created resources are orphaned.

Same pattern applies to the "admin user can access event" test (lines 444–450 are before the try at line 452): if createTestEvent throws on line 450, the organization leaks.

🛡️ Fix: move resource creation inside try or use immediate registration
 	test("unauthorized regular user cannot access event...", async () => {
 		const { authToken: adminAuthToken, userId: adminUserId } =
 			await getAdminTokenAndUserId();
-		const organization = await createTestOrganization(adminAuthToken, adminUserId);
-		const event = await createTestEvent(adminAuthToken, organization.id);
-
-		const userResult = await mercuriusClient.mutate(Mutation_createUser, { ... });
-		const user = userResult.data?.createUser;
-		assertToBeNonNullish(user);
-		assertToBeNonNullish(user.authenticationToken);
-		assertToBeNonNullish(user.user);
-
-		try {
+		let organization: Awaited<ReturnType<typeof createTestOrganization>> | undefined;
+		let user: { user: { id: string }; authenticationToken: string } | undefined;
+		try {
+			organization = await createTestOrganization(adminAuthToken, adminUserId);
+			const event = await createTestEvent(adminAuthToken, organization.id);
+			const userResult = await mercuriusClient.mutate(Mutation_createUser, { ... });
+			user = userResult.data?.createUser;
+			assertToBeNonNullish(user);
+			assertToBeNonNullish(user.authenticationToken);
+			assertToBeNonNullish(user.user);
 			// ... query and assertions ...
 		} finally {
+			if (user?.user?.id) {
 				await mercuriusClient.mutate(Mutation_deleteUser, { ... });
+			}
+			if (organization?.id) {
 				await server.drizzleClient.delete(organizationsTable)
 					.where(eq(organizationsTable.id, organization.id));
+			}
 		}
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 357 - 437, The cleanup
leak happens because createTestOrganization, createTestEvent, user creation
(Mutation_createUser) and the assertToBeNonNullish checks run before the
try/finally so finally may not execute; fix by moving resource creation and the
assertToBeNonNullish calls into the try block (for this test "unauthorized
regular user cannot access event..." and the similar "admin user can access
event" test) or immediately register the created resources for cleanup before
any throws (e.g., push created ids into a local cleanup list) and ensure the
finally block deletes via Mutation_deleteUser and
server.drizzleClient.delete(organizationsTable) using those registered ids so
cleanup always runs even if creation throws.

30-56: ⚠️ Potential issue | 🟠 Major

PR objective not met — getAdminTokenAndUserId() and getAdminToken() perform fresh sign-ins on every call with no caching.

Both helpers issue a full Query_signIn mutation on each invocation. There is no module-level cache, beforeAll hook, or delegation to the established ensureAdminAuth() caching pattern used throughout peer test files (createEventVolunteer.test.ts, sendEventInvitations.test.ts, deleteEventVolunteer.test.ts, updateEventVolunteer.test.ts, etc.). With 14+ call-sites across the suite, this reproduces the same repeated sign-in problem that issue #5258 aimed to fix.

To meet the PR's stated goal, replace the custom helpers with the established ensureAdminAuth() pattern already used in peer test files:

async function ensureAdminAuth(): Promise<{ token: string; userId: string }> {
  const signInResult = await mercuriusClient.query(Query_signIn, {
    variables: {
      input: {
        emailAddress: server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS,
        password: server.envConfig.API_ADMINISTRATOR_USER_PASSWORD,
      },
    },
  });
  const authToken = signInResult.data?.signIn?.authenticationToken;
  const userId = signInResult.data?.signIn?.user?.id;
  if (!authToken || !userId) {
    throw new Error(`Failed to sign in as admin. Errors: ${JSON.stringify(signInResult.errors)}`);
  }
  return { authToken, userId };
}

Then cache the result in beforeAll to ensure a single sign-in per suite run:

let adminToken: string;
let adminUserId: string;

beforeAll(async () => {
  const { token, userId } = await ensureAdminAuth();
  adminToken = token;
  adminUserId = userId;
});

Replace all calls to getAdminToken() and getAdminTokenAndUserId() with references to adminToken and adminUserId.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 30 - 56, The two helpers
getAdminTokenAndUserId and getAdminToken currently perform fresh Query_signIn
requests on every call; replace them with the established ensureAdminAuth
pattern that performs a single sign-in via mercuriusClient.query(Query_signIn)
and returns { token, userId }, then cache the result in a beforeAll by assigning
to module-level adminToken and adminUserId variables; finally remove/stop using
getAdminToken*/and update all call-sites to reference adminToken and adminUserId
so the suite only signs in once.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 112-158: The createTestEvent helper uses Date.now() (new
Date(Date.now() + startOffset)) which breaks test determinism; change it to
derive timestamps from a fixed anchor or from the test's fake timer instead:
either accept a baseNow ISO/string/Date parameter on createTestEvent and compute
startAt/endAt from that fixed base, or ensure tests call vi.useFakeTimers({ now:
new Date("2030-01-01T00:00:00Z") }) and then compute startAt as new
Date(baseNow.getTime() + startOffset) inside createTestEvent (replace Date.now()
usages). Update callers to pass the fixed anchor or enable the fake timers in
setup and restore with vi.useRealTimers() in afterEach.
- Around line 167-200: The helper createAndDeleteTestUser currently creates a
user then attempts to delete it with Mutation_deleteUser and can leak the user
if deletion throws; fix by registering a cleanup for the created user into the
suite-level testCleanupFunctions immediately after creation (using the created
user's id) and then attempt deletion inside a try/catch that re-throws after
noting the leak, or at minimum wrap the Mutation_deleteUser call in try/catch
and invoke the registered cleanup on error to guarantee eventual deletion; also
rename the function to createUserAndReturnStaleToken to clarify intent (and
update callers) so it documents that it returns a token for a deleted user.
- Around line 357-437: The cleanup leak happens because createTestOrganization,
createTestEvent, user creation (Mutation_createUser) and the
assertToBeNonNullish checks run before the try/finally so finally may not
execute; fix by moving resource creation and the assertToBeNonNullish calls into
the try block (for this test "unauthorized regular user cannot access event..."
and the similar "admin user can access event" test) or immediately register the
created resources for cleanup before any throws (e.g., push created ids into a
local cleanup list) and ensure the finally block deletes via Mutation_deleteUser
and server.drizzleClient.delete(organizationsTable) using those registered ids
so cleanup always runs even if creation throws.
- Around line 30-56: The two helpers getAdminTokenAndUserId and getAdminToken
currently perform fresh Query_signIn requests on every call; replace them with
the established ensureAdminAuth pattern that performs a single sign-in via
mercuriusClient.query(Query_signIn) and returns { token, userId }, then cache
the result in a beforeAll by assigning to module-level adminToken and
adminUserId variables; finally remove/stop using getAdminToken*/and update all
call-sites to reference adminToken and adminUserId so the suite only signs in
once.

---

Duplicate comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 241-251: Tests register cleanup callbacks too late, causing
orphaned orgs/users if intermediate operations fail; fix by registering cleanup
callbacks immediately after resources are created. Specifically, after
createTestOrganization (or the org returned from
setupTestData/createTestOrganization) push a cleanup entry into
testCleanupFunctions right away (before performing further mutations like event
creation), and likewise push a cleanup callback immediately after creating any
user (e.g., after getAdminTokenAndUserId or user-creation helpers) so each
resource has its own callback; ensure you push user cleanup first and org
cleanup second so the final reverse() in afterEach deletes users before their
orgs.

@palisadoes
Copy link
Copy Markdown
Contributor

Please fix the conflicting file

h30s added 7 commits February 19, 2026 09:27
- Fixed indentation issues that caused nested test functions
- Corrected cleanup function registration logic
- Fixed database insertion vs deletion logic
- Removed orphaned code blocks that were causing syntax errors
- Tests now have proper suite/test structure
@palisadoes
Copy link
Copy Markdown
Contributor

Please fix the failing tests and checks

@palisadoes
Copy link
Copy Markdown
Contributor

Are you still working on this?

@h30s
Copy link
Copy Markdown
Author

h30s commented Feb 22, 2026

Are you still working on this?

Yes, I am working on this and shortly I will push the latest commit. Actually busy somewhere else, so I am going to do soon, for sure.

Sorry for the delay.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 6, 2026

This pull request did not get any activity in the past 10 days and will be closed in 360 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed.

@github-actions github-actions Bot added no-pr-activity No pull request activity and removed no-pr-activity No pull request activity labels Mar 6, 2026
@Cioppolo14
Copy link
Copy Markdown
Contributor

@coderabbitai full review and reevaluate all your previous comments that may have been dismissed, ignored or resolved. Evaluate whether the goals of the issue linked in the first PR comment are met. If there are test cases, ensure that the updated or created tests won't be flaky, especially in a sharded testing environment where test cleanup is important and response times are highly variable. Post your conclusion as an easily readable comment that also includes your findings outside the diffs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 11, 2026

🧩 Analysis chain

Let me gather the full file content and PR details to do a thorough review.


🏁 Script executed:

#!/bin/bash
# Read the full test file
cat test/graphql/types/Query/event.test.ts

Length of output: 40918


@Cioppolo14 Here is a comprehensive review of PR #5261, including findings both inside and outside the diff.


Full Review: test/graphql/types/Query/event.test.ts

✅ Issue #5258 Goals — Substantially Met

The PR correctly implements the requested caching mechanism:

  • ensureAdminAuth() is a lazy-init singleton that performs a GraphQL sign-in only once and stores the result in suite-level variables (adminToken, adminUserId).
  • A beforeAll hook warms the cache eagerly before any test runs.
  • getAdminTokenAndUserId() and getAdminToken() both delegate to ensureAdminAuth().
  • The admin user is never mutated by any test, so token reuse is safe.
  • Sign-in mutations are reduced from 13+ to exactly 1 per suite run. ✅

🔴 High Severity Issues

1. Inconsistent Token Access Pattern — Bypasses the Cache Helper

Several tests skip the helper functions entirely and access the raw adminToken/adminUserId variables directly using assertToBeNonNullish. This is inconsistent with the rest of the suite and is fragile if the caching design changes later.

Affected tests:

  • "fails with ULID of wrong length" and "fails with ULID containing invalid characters" in the database query failure suite
  • "handles events with past dates correctly" and "handles multi-day events correctly" in Additional event tests
  • All five invite-only tests (assertToBeNonNullish(adminToken); const adminAuthToken = adminToken;)

All of these should use getAdminToken() or getAdminTokenAndUserId() for consistency. The assertToBeNonNullish guard is redundant since beforeAll guarantees population — and if beforeAll fails, the tests would already be skipped.

// Current (inconsistent):
assertToBeNonNullish(adminToken);
assertToBeNonNullish(adminUserId);
const authToken = adminToken;
const userId = adminUserId;

// Should be:
const { authToken, userId } = await getAdminTokenAndUserId();

🟡 Medium Severity Issues

2. Double-Deletion Risk in createUserAndReturnStaleToken

The function registers a cleanup callback first, then immediately attempts to delete the user:

// Cleanup registered here...
cleanupFunctions.push(async () => { /* delete user */ });

// ...then immediate deletion attempted here
await mercuriusClient.mutate(Mutation_deleteUser, { ... });

If the immediate deletion succeeds, afterEach will still call the registered cleanup, which will attempt to delete an already-deleted user. This is caught by the try/catch in the caller's afterEach, but it will log a noisy "Cleanup failed" error on every successful test run. The cleanup should only be pushed if the immediate deletion fails, or the immediate deletion should be omitted in favour of relying entirely on the cleanup.

3. Flaky Recurring Event Test Due to Server-Side Materialization Window

The "creates a never-ending recurring event" test uses vi.useFakeTimers({ now: new Date("2026-06-15T12:00:00Z") }) to compute a startAt of approximately July 2026, then calls vi.useRealTimers() before any async API calls. The server, however, always uses the real system clock (March 2026 at the time of writing), and calculates its materialization window as now + N months.

The test assumes N >= 4 (so July 2026 falls within window). This assumption is undocumented and untested. If the server's window is smaller, the test will silently generate zero instances and fail on expect(generatedInstances.length).toBeGreaterThan(0). The comment in the test acknowledges this fragility but does not address it:

// Note: Cannot use fixed future dates (e.g., 2099) because the server calculates
// the materialization window as current_date + N months.

The safest fix is to either: (a) look up the actual server-side window constant and assert against it, or (b) use startOffset relative to server real time, not fake time.

4. Cascading + Explicit Deletion Race Produces Noisy Failures

In multiple tests (e.g., "unauthorized regular user cannot access event..."), both an org-level cascade delete (direct Drizzle delete on organizationsTable) and an explicit event delete are registered. Since afterEach iterates in reverse, the explicit event delete runs before the org delete — but the org delete then cascades and tries to reach rows that are already gone.

For the inverse case: the "admin user can access event from any organization" test pushes the event delete via Mutation_deleteStandaloneEvent (a GraphQL mutation), while the org cleanup uses a direct Drizzle delete which also cascades. Cleanup runs event → org via reverse order, so the GraphQL mutation fires first (fine), then the direct org delete cascades over what's left. This is correct but fragile: if the GraphQL mutation cleanup fails, the event record leaks until the org cascade.

Recommendation: Standardize on one deletion strategy — preferably cascade-via-org for all tests that own an organization, and only register the org cleanup.


🟢 Low Severity / Observations

5. testCleanupFunctions is Suite-Scoped, Not Test-Scoped

The testCleanupFunctions arrays are declared at the suite level. Since afterEach sets testCleanupFunctions.length = 0 after each test, this is safe in sequential execution. In a sharded environment, each shard runs a subset of tests sequentially, so this pattern holds — but only because within a shard, tests in a suite still run sequentially. This is fine as-is.

6. createTestOrganization Redundantly Adds Admin as a Member

The helper creates the organization and then explicitly adds the system admin user as an "administrator" member. Since the admin is a system-level administrator, this membership may be redundant (depending on the authorization model). It's not harmful, but it adds an extra mutation to every test setup.

7. Suites Without afterEach/Cleanup Are Correct

The database query failure, arguments_associated_resources_not_found, and invalid_arguments suites do not define afterEach or cleanup arrays. This is correct — none of their tests create persisted resources.


Summary Table

# Severity Issue Location
1 🔴 High Direct adminToken access bypasses helper — inconsistent Multiple tests across 3 suites
2 🟡 Medium Double-deletion causes noisy cleanup errors createUserAndReturnStaleToken
3 🟡 Medium Recurring event test fragile — depends on undocumented server window "creates a never-ending recurring event"
4 🟡 Medium Mixed cascade + explicit deletion causes noisy afterEach errors "Access control" and "Additional event tests" suites
5 🟢 Low Suite-scoped cleanup array is safe sequentially but worth noting All suites with testCleanupFunctions
6 🟢 Low Redundant admin membership creation createTestOrganization

Overall Conclusion

The PR meets the primary goal of issue #5258 — admin sign-in is now cached and performed exactly once per suite run via ensureAdminAuth(). The cleanup patterns are functional and generally prevent resource leaks. However, the codebase quality is weakened by the inconsistent use of the helper vs. direct variable access (Issue #1), and there are two real risks of noisy but non-fatal cleanup errors (Issues #2 and #4). Issue #3 is a genuine flakiness risk in CI environments where the server's materialization window is not guaranteed. These should be addressed before merging.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/graphql/types/Query/event.test.ts (2)

190-261: 🧹 Nitpick | 🔵 Trivial

Cleanup registration addresses past review concerns, but double-deletion may produce noisy logs.

The helper now correctly registers cleanup immediately after user creation (lines 213-235), ensuring orphaned records are handled even if the subsequent deletion fails. However, when the immediate deletion (lines 244-254) succeeds, the cleanup function will attempt to delete the same user again, resulting in a console error log.

This is functional but creates test output noise. Consider tracking deletion success to conditionally skip cleanup:

♻️ Optional: Track deletion success to reduce log noise
 	async function createUserAndReturnStaleToken(
 		authToken: string,
 		cleanupFunctions: Array<() => Promise<void>>,
 	) {
+		let deletionSucceeded = false;
 		const userResult = await mercuriusClient.mutate(Mutation_createUser, {
 			// ...
 		});

 		const user = userResult.data?.createUser;

 		// Register cleanup immediately after creation in case deletion fails
 		if (user?.user?.id) {
 			const userId = user.user.id;
 			cleanupFunctions.push(async () => {
+				if (deletionSucceeded) return; // Skip if already deleted
 				try {
 					await mercuriusClient.mutate(Mutation_deleteUser, {
 						// ...
 					});
 				} catch (error) {
 					console.error(
 						"Failed to cleanup user in createUserAndReturnStaleToken:",
 						error,
 					);
 				}
 			});
 		}

 		// ...

 		// Attempt deletion - if it fails, cleanup will handle it
 		try {
 			await mercuriusClient.mutate(Mutation_deleteUser, {
 				// ...
 			});
+			deletionSucceeded = true;
 		} catch (error) {
 			// Deletion failed, but cleanup is registered
 			console.error("Failed to delete user immediately:", error);
 		}

 		return user;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 190 - 261, The helper
createUserAndReturnStaleToken registers a cleanup function immediately but still
calls Mutation_deleteUser afterwards, causing double-deletion noise; update the
function to track whether the immediate deletion succeeded (e.g., set a local
deleted flag after the mercuriusClient.mutate call to Mutation_deleteUser) and
if deletion succeeds remove or skip pushing the cleanup function into
cleanupFunctions (or conditionally no-op the registered cleanup), referencing
createUserAndReturnStaleToken, cleanupFunctions, Mutation_deleteUser and
mercuriusClient.mutate to locate where to change the logic.

615-764: 🧹 Nitpick | 🔵 Trivial

Cleanup is functional but uses mixed approaches.

The access control suite properly implements cleanup with vi.restoreAllMocks() and immediate cleanup registration. However, cleanup approaches are inconsistent within this suite:

  • Lines 640-642, 649-651: Use drizzle deletes
  • Lines 730-732: Use drizzle delete for organization
  • Lines 740-743: Use GraphQL mutation (Mutation_deleteStandaloneEvent) for event

Both approaches work correctly (cascade relationships handle dependencies), but the inconsistency could be confusing for future maintainers. Consider standardizing on one approach per entity type across the test file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 615 - 764, The test
suite uses mixed cleanup methods—direct DB deletes via server.drizzleClient
(organizationsTable, eventsTable) and GraphQL mutations
(Mutation_deleteStandaloneEvent via mercuriusClient)—which is confusing; pick
and apply a single consistent approach per entity (e.g., always use
server.drizzleClient deletes for organizations and events or always use the
corresponding GraphQL mutations via mercuriusClient), then update the cleanup
registrations in the Access control tests (references: testCleanupFunctions,
createTestOrganization, createTestEvent, Mutation_deleteStandaloneEvent,
Mutation_deleteUser, organizationsTable, eventsTable) so all cleanup for an
entity type uses the chosen method uniformly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 190-261: The helper createUserAndReturnStaleToken registers a
cleanup function immediately but still calls Mutation_deleteUser afterwards,
causing double-deletion noise; update the function to track whether the
immediate deletion succeeded (e.g., set a local deleted flag after the
mercuriusClient.mutate call to Mutation_deleteUser) and if deletion succeeds
remove or skip pushing the cleanup function into cleanupFunctions (or
conditionally no-op the registered cleanup), referencing
createUserAndReturnStaleToken, cleanupFunctions, Mutation_deleteUser and
mercuriusClient.mutate to locate where to change the logic.
- Around line 615-764: The test suite uses mixed cleanup methods—direct DB
deletes via server.drizzleClient (organizationsTable, eventsTable) and GraphQL
mutations (Mutation_deleteStandaloneEvent via mercuriusClient)—which is
confusing; pick and apply a single consistent approach per entity (e.g., always
use server.drizzleClient deletes for organizations and events or always use the
corresponding GraphQL mutations via mercuriusClient), then update the cleanup
registrations in the Access control tests (references: testCleanupFunctions,
createTestOrganization, createTestEvent, Mutation_deleteStandaloneEvent,
Mutation_deleteUser, organizationsTable, eventsTable) so all cleanup for an
entity type uses the chosen method uniformly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3ee7680a-e194-4f1c-97fd-45be057825d7

📥 Commits

Reviewing files that changed from the base of the PR and between 0ba83a5 and 56254ff.

📒 Files selected for processing (3)
  • docker/compose.testing.yaml
  • test/graphql/types/Query/event.test.ts
  • test/server.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 11, 2026
@Cioppolo14
Copy link
Copy Markdown
Contributor

@h30s Can you address the comments here: #5261 (comment)

@Cioppolo14 Cioppolo14 self-requested a review March 14, 2026 00:17
Copy link
Copy Markdown
Contributor

@Cioppolo14 Cioppolo14 left a comment

Choose a reason for hiding this comment

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

Please address the most recent coderabbit comments.

@h30s
Copy link
Copy Markdown
Author

h30s commented Mar 14, 2026

Please address the most recent coderabbit comments.

Done...

@palisadoes
Copy link
Copy Markdown
Contributor

@coderabbitai full review and reevaluate all your previous comments that may have been dismissed, ignored or resolved. Evaluate whether the goals of the issue linked in the first PR comment are met. If there are test cases, ensure that the updated or created tests won't be flaky, especially in a sharded testing environment where test cleanup is important and response times are highly variable. Post your conclusion as an easily readable comment that also includes your findings outside the diffs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 15, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat test/graphql/types/Query/event.test.ts

Length of output: 38323


@palisadoes Here is a full re-evaluation of PR #5261 after h30s's latest changes.


Full Re-Review: test/graphql/types/Query/event.test.ts

✅ Issue #5258 Goals — Met

ensureAdminAuth() is a lazy singleton invoked via beforeAll, reducing sign-in mutations to exactly 1 per suite run. The cache is safe because the admin user is never mutated by any test. This goal is fully achieved.


Previously Flagged Issues — Status

🟢 Issue 1 (was 🔴 High): Direct adminToken Access — FIXED

All tests that previously bypassed the helper functions now correctly call getAdminToken() or getAdminTokenAndUserId(). No test directly accesses adminToken/adminUserId anymore. The assertToBeNonNullish(adminToken) guard pattern has been removed in all affected places.

🟢 Issue 2 (was 🟡 Medium): Double-Deletion in createUserAndReturnStaleTokenFIXED

The try/catch pattern is now correct: the cleanup callback is only registered in the catch block if the immediate deletion fails, eliminating the noisy double-deletion errors on successful test runs.

🟡 Issue 3 (was 🟡 Medium): Flaky Recurring Event Test — IMPROVED, minor risk remains

Fake timers have been removed. The test now uses new Date() + 1 month offset against the real clock, which is significantly more robust. The comment correctly documents the hotWindowMonthsAhead = 12 server-side assumption.

One minor risk remains: the assumption is only in a comment and is not verified against the actual server-side constant. If that constant ever changes to a value less than 1 month, the test silently fails. Additionally, setUTCMonth(month + 1) has a known JavaScript month-overflow quirk (e.g., January 31 + 1 month = March 3, skipping February), which could produce a slightly unexpected startAt. Neither issue is a blocker given the 12-month window, but they are worth noting.

🟡 Issue 4 (was 🟡 Medium): Mixed Cascade + Explicit Deletion — SUBSTANTIALLY FIXED, new inconsistency

No test now registers both an explicit event delete and an org cascade delete for the same event. This is the correct fix.

However, a new inconsistency has been introduced: org cleanup is handled two different ways across suites:

Test Org Cleanup Method
"unauthorized regular user..." server.drizzleClient.delete(organizationsTable) (direct Drizzle)
"admin user can access event..." server.drizzleClient.delete(organizationsTable) (direct Drizzle)
"handles events with past dates..." Mutation_deleteOrganization (GraphQL mutation)
"handles multi-day events..." Mutation_deleteOrganization (GraphQL mutation)
All invite-only tests Mutation_deleteOrganization (GraphQL mutation)

In a sharded environment with highly variable response times, direct Drizzle deletes (no HTTP round-trip, no server middleware) are more reliable for cleanup than GraphQL mutations. If the API server is slow or has a transient failure during afterEach, the GraphQL-mutation-based cleanups can fail and leave orphaned records, while the Drizzle-direct cleanups will not. Standardizing on direct Drizzle deletes for org cleanup (as done in the Access control suite) would be more resilient:

// Recommended uniform cleanup pattern for all suites:
testCleanupFunctions.push(async () => {
    await server.drizzleClient
        .delete(organizationsTable)
        .where(eq(organizationsTable.id, organization.id));
});

🟢 Issue 5 (Low): Suite-Scoped Cleanup Array — Acceptable, no change needed

🟡 Issue 6 (Low): Redundant Admin Membership in createTestOrganizationStill Present

createTestOrganization still explicitly adds the system admin user as an "administrator" member after creating the org. This adds an extra mutation to every test setup. It is not harmful, but it is unnecessary if the system admin's role already grants access regardless of membership.


Summary Table

# Severity Issue Status
1 🔴 High Direct adminToken access bypasses helper ✅ Fixed
2 🟡 Medium Double-deletion in createUserAndReturnStaleToken ✅ Fixed
3 🟡 Medium Recurring event relies on undocumented hotWindowMonthsAhead constant + month-overflow quirk ⚠️ Improved, minor risk remains
4 🟡 Medium Inconsistent org cleanup: Drizzle direct vs. GraphQL mutation across suites ⚠️ New inconsistency, GraphQL cleanups less resilient in sharded environments
5 🟢 Low Suite-scoped cleanup array ✅ Acceptable
6 🟢 Low Redundant admin membership in createTestOrganization ⚠️ Still present

Overall Conclusion

The PR is significantly improved from its previous state. The primary goals of issue #5258 are fully met, and the two major issues (inconsistent token access, double-deletion) have been cleanly resolved. The two remaining medium items — the hotWindowMonthsAhead assumption in the recurring event test and the inconsistent org cleanup strategy — are real but low-risk concerns. The org cleanup inconsistency (Issue 4) is the one most relevant to a sharded testing environment and is worth addressing before merging by standardizing all org cleanup to use direct Drizzle deletes.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/graphql/types/Query/event.test.ts (1)

909-916: ⚠️ Potential issue | 🔴 Critical

Remove runtime clock usage from this test path.

new Date() at Line 912 makes this scenario time-dependent and non-deterministic. This violates the test determinism requirement and can still flake in sharded CI over time boundaries.

As per coding guidelines: "Avoid new Date() without arguments" and "All test dates must use fixed UTC timestamps."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 909 - 916, The test uses
new Date() in event.test.ts (variables baseDate and startAt) making the scenario
time-dependent; replace the runtime clock usage by constructing a deterministic
fixed UTC base timestamp (e.g., a fixed ISO string) and derive startAt from that
fixed base so the test no longer depends on the current time—update the baseDate
creation and any setUTCMonth/setUTCHours adjustments in this test path (same
function/scope where baseDate and startAt are defined) to use the fixed UTC
timestamp approach so the test is fully deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 218-248: The current try/catch around
mercuriusClient.mutate(Mutation_deleteUser, ...) only handles thrown exceptions
but ignores GraphQL errors returned in the mutation response; update the call to
capture the mutation result (e.g., const result = await
mercuriusClient.mutate(...)) and check result.errors (or result.data validity)
after the call and, if errors exist or deletion was not confirmed, register the
cleanupFunctions push fallback just like in the catch branch; keep the catch to
handle thrown exceptions and ensure references to Mutation_deleteUser,
mercuriusClient.mutate, cleanupFunctions, authToken and userId are used to
locate and modify the code.

---

Outside diff comments:
In `@test/graphql/types/Query/event.test.ts`:
- Around line 909-916: The test uses new Date() in event.test.ts (variables
baseDate and startAt) making the scenario time-dependent; replace the runtime
clock usage by constructing a deterministic fixed UTC base timestamp (e.g., a
fixed ISO string) and derive startAt from that fixed base so the test no longer
depends on the current time—update the baseDate creation and any
setUTCMonth/setUTCHours adjustments in this test path (same function/scope where
baseDate and startAt are defined) to use the fixed UTC timestamp approach so the
test is fully deterministic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1c519a74-1533-4e98-9498-dc9e03aa5586

📥 Commits

Reviewing files that changed from the base of the PR and between 0ba83a5 and d844036.

📒 Files selected for processing (3)
  • docker/compose.testing.yaml
  • test/graphql/types/Query/event.test.ts
  • test/server.ts

Comment on lines +218 to +248
// Delete the user immediately; register cleanup only if deletion fails
try {
await mercuriusClient.mutate(Mutation_deleteUser, {
headers: {
authorization: `bearer ${authToken}`,
},
},
});
variables: {
input: {
id: userId,
},
},
});
} catch (error) {
// Immediate deletion failed — register cleanup as fallback
console.error(
"Immediate user deletion failed, registering cleanup:",
error,
);
cleanupFunctions.push(async () => {
await mercuriusClient.mutate(Mutation_deleteUser, {
headers: {
authorization: `bearer ${authToken}`,
},
variables: {
input: {
id: userId,
},
},
});
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Mercurius v16 test clients, are GraphQL resolver/mutation failures returned on result.errorsinstead of being thrown as exceptions forquery/mutate calls?

💡 Result:

Yes. Mercurius test clients return GraphQL execution failures (e.g., a resolver/mutation throws) as a normal GraphQL response with a populated errors array (i.e., result.errors), rather than throwing from query() / mutate() themselves. The mercurius-integration-testing client explicitly documents queryResponse.errors / mutationResponse.errors as the “Possible array of errors from the API.” [1] Mercurius’s own programmatic API (app.graphql(...)) likewise returns the GraphQL execution result object (which, per GraphQL conventions, includes errors when execution fails) instead of treating resolver errors as thrown exceptions. [2]

Sources: [1] [2]


Handle GraphQL mutation errors explicitly in stale-token creation.

The current try/catch only catches thrown exceptions. Mercurius returns GraphQL errors as a populated result.errors array rather than throwing exceptions. If Mutation_deleteUser returns GraphQL errors in the response, this helper skips the fallback cleanup registration entirely.

🛠️ Proposed fix
 		const userId = user.user.id;
+		const cleanupDeleteUser = async () => {
+			await mercuriusClient.mutate(Mutation_deleteUser, {
+				headers: {
+					authorization: `bearer ${authToken}`,
+				},
+				variables: {
+					input: { id: userId },
+				},
+			});
+		};
+		// Register fallback cleanup first; remove it only after verified success.
+		cleanupFunctions.push(cleanupDeleteUser);

-		// Delete the user immediately; register cleanup only if deletion fails
-		try {
-			await mercuriusClient.mutate(Mutation_deleteUser, {
-				headers: {
-					authorization: `bearer ${authToken}`,
-				},
-				variables: {
-					input: {
-						id: userId,
-					},
-				},
-			});
-		} catch (error) {
-			// Immediate deletion failed — register cleanup as fallback
-			console.error(
-				"Immediate user deletion failed, registering cleanup:",
-				error,
-			);
-			cleanupFunctions.push(async () => {
-				await mercuriusClient.mutate(Mutation_deleteUser, {
-					headers: {
-						authorization: `bearer ${authToken}`,
-					},
-					variables: {
-						input: {
-							id: userId,
-						},
-					},
-				});
-			});
-		}
+		const deleteResult = await mercuriusClient.mutate(Mutation_deleteUser, {
+			headers: {
+				authorization: `bearer ${authToken}`,
+			},
+			variables: {
+				input: { id: userId },
+			},
+		});
+		if (deleteResult.errors?.length) {
+			throw new Error(
+				`Immediate user deletion failed: ${JSON.stringify(deleteResult.errors)}`,
+			);
+		}
+		cleanupFunctions.pop();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/graphql/types/Query/event.test.ts` around lines 218 - 248, The current
try/catch around mercuriusClient.mutate(Mutation_deleteUser, ...) only handles
thrown exceptions but ignores GraphQL errors returned in the mutation response;
update the call to capture the mutation result (e.g., const result = await
mercuriusClient.mutate(...)) and check result.errors (or result.data validity)
after the call and, if errors exist or deletion was not confirmed, register the
cleanupFunctions push fallback just like in the catch branch; keep the catch to
handle thrown exceptions and ensure references to Mutation_deleteUser,
mercuriusClient.mutate, cleanupFunctions, authToken and userId are used to
locate and modify the code.

Copy link
Copy Markdown
Contributor

@palisadoes palisadoes left a comment

Choose a reason for hiding this comment

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

Please address this:

@github-actions
Copy link
Copy Markdown

This pull request did not get any activity in the past 10 days and will be closed in 360 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed.

@github-actions github-actions Bot added no-pr-activity No pull request activity and removed no-pr-activity No pull request activity labels Mar 26, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 6, 2026

This pull request did not get any activity in the past 10 days and will be closed in 360 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed.

@github-actions github-actions Bot added no-pr-activity No pull request activity and removed no-pr-activity No pull request activity labels Apr 6, 2026
@github-actions
Copy link
Copy Markdown

This pull request did not get any activity in the past 10 days and will be closed in 360 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed.

@github-actions github-actions Bot added no-pr-activity No pull request activity and removed no-pr-activity No pull request activity labels Apr 17, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

This pull request did not get any activity in the past 10 days and will be closed in 360 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed.

@github-actions github-actions Bot added no-pr-activity No pull request activity and removed no-pr-activity No pull request activity labels May 4, 2026
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.

3 participants