Skip to content

chore: address remaining PR feedback for types, ESM resolution, and tech debt - #84

Merged
pramodnarayana merged 5 commits into
developmentfrom
feature/esm-migration-option-a
Mar 7, 2026
Merged

pramodnarayana merged 5 commits into
developmentfrom
feature/esm-migration-option-a

Conversation

@pramodnarayana

@pramodnarayana pramodnarayana commented Mar 7, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Bug Fixes

    • Defaulted invitation role to "member" when undefined.
  • Documentation

    • Updated PII guidance to time-bounded retention (30 days) with a scheduled anonymization/cleanup and audit logging.
  • Chores

    • Exposed a public encryption interface and adjusted imports.
    • Improved type-only imports, ESM compatibility, and package type exports.
  • Tests

    • Strengthened and updated unit tests and mocks for better assertions and module resolution.

@coderabbitai

coderabbitai Bot commented Mar 7, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@pramodnarayana has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 45 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b46aebc-fe55-44b8-b263-243fb1432646

📥 Commits

Reviewing files that changed from the base of the PR and between 568ff94 and 726d346.

📒 Files selected for processing (1)
  • packages/identity/src/adapters/drizzle-user.adapter.spec.ts
📝 Walkthrough

Walkthrough

This PR makes ESM compatibility edits, converts several runtime imports to type-only, introduces a shared EncryptionService contract and updates AesEncryptionService and token manager to use it, exposes package type entrypoints, hardens tests, and updates TECHNICAL_DEBT.md to recommend a 30-day scheduled PII cleanup approach.

Changes

Cohort / File(s) Summary
ESM & bootstrap
apps/api/src/db/database-manager.spec.ts, apps/api/src/db/reset-e2e.ts, apps/api/src/scripts/admin-bootstrap.ts, apps/api/src/main.ts
Switched test mocks/imports to .js, added ESM filename/dirname shims for env resolution, and replaced async wrapper with top-level await in main bootstrap.
Type-only imports & typing cleanup
apps/api/src/modules/connections/connections/callback.controller.spec.ts, apps/api/src/modules/trigger/trigger.module.ts, apps/api/src/modules/identity/users/users.controller.ts, apps/api/src/scripts/admin-bootstrap.ts
Converted runtime imports to type-only to avoid runtime side effects; adjusted imports to use type where appropriate and updated users controller to import User as a type.
Encryption abstraction & connectors
packages/connectors/src/crypto/encryption.interface.ts, packages/connectors/src/crypto/encryption.service.ts, packages/connectors/src/oauth/token-manager.service.ts, packages/connectors/src/index.ts
Added exported abstract EncryptionService, made AesEncryptionService extend it (added super()), replaced local contract with shared interface import, and re-exported the interface from the package index.
Package typings & TS config
packages/database/package.json, packages/pieces/salesforce/tsconfig.json
Exposed type entrypoints in package exports and changed TS moduleResolution from NodeNext to nodenext.
Identity tests & logic
packages/identity/src/adapters/drizzle-user.adapter.spec.ts, packages/identity/src/services/permission-seeder.spec.ts, apps/api/src/modules/identity/users/users.controller.ts
Tightened and extended unit test assertions and mock lifecycles; defaulted invitation role to 'member' when missing; updated tests to use .js mocks and ensure mock cleanup.
Docs
TECHNICAL_DEBT.md
Changed recommended approach from pre-insert anonymization to retaining raw IP/UA on insert with a scheduled daily PII cleanup for sessions older than 30 days and audit logging guidance.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through code with nimble paws,

Types tucked in, no runtime claws.
Encryption’s shared, the AES sings true,
Bootstrap awaits with an awaited cue.
Scheduler nibbles PII at thirty days anew. 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: addressing type-only imports, ESM resolution adjustments, and technical debt updates across multiple files.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/esm-migration-option-a

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
packages/identity/src/services/permission-seeder.spec.ts (1)

196-302: ⚠️ Potential issue | 🟠 Major

Wrap each module mock in try/finally to guarantee cleanup.

If the dynamic import, seedSystemRbac(...), or the assertion throws before vi.doUnmock("../constants.js"), the mock persists and poisons later tests. vi.restoreAllMocks() does not cover module-level cleanup; only vi.doUnmock() removes the mock from the registry. Use try/finally to ensure cleanup always runs.

Suggested fix
   it("seed skips rolePermission insertion if all exist", async () => {
     vi.resetModules();
     vi.doMock("../constants.js", async (importOriginal) => {
       const actual = await importOriginal<typeof import("../constants.js")>();
       return {
         ...actual,
         // Include all permissions referenced by the member role in rbac-seeding
         ALL_PERMISSIONS: [
           "users:read",
           "tenants:read",
           "dashboard:read",
           "admin_dashboard:view",
           "system_users:read",
           "system_tenants:read",
         ],
         isSystemPermission: (p: string) =>
           p.startsWith("system_") || p.startsWith("admin_dashboard:"),
       };
     });
-
-    // Re-import to pickup mock
-    const { seedSystemRbac } = await import("../utils/rbac-seeding.js");
-
-    const dbMock = mkDb();
+    try {
+      // Re-import to pickup mock
+      const { seedSystemRbac } = await import("../utils/rbac-seeding.js");
+
+      const dbMock = mkDb();
 
-    // Return existing rows covering every permission that seedSystemRbac would generate
-    // for owner, admin, and member roles so the deduplication sees them all as existing.
-    const existingRows = [
-      // member base perms (organizationId: null)
-      { roleId: "member", permissionId: "users:read", organizationId: null },
-      { roleId: "member", permissionId: "tenants:read", organizationId: null },
-      {
-        roleId: "member",
-        permissionId: "dashboard:read",
-        organizationId: null,
-      },
-      // member system perms (organizationId: "sys")
-      {
-        roleId: "member",
-        permissionId: "admin_dashboard:view",
-        organizationId: "sys",
-      },
-      {
-        roleId: "member",
-        permissionId: "system_users:read",
-        organizationId: "sys",
-      },
-      {
-        roleId: "member",
-        permissionId: "system_tenants:read",
-        organizationId: "sys",
-      },
-      // admin & owner — all perms (null + sys scoped)
-      ...["admin", "owner"].flatMap((role) => [
-        { roleId: role, permissionId: "users:read", organizationId: null },
-        { roleId: role, permissionId: "tenants:read", organizationId: null },
-        { roleId: role, permissionId: "dashboard:read", organizationId: null },
-        {
-          roleId: role,
-          permissionId: "admin_dashboard:view",
-          organizationId: "sys",
-        },
-        {
-          roleId: role,
-          permissionId: "system_users:read",
-          organizationId: "sys",
-        },
-        {
-          roleId: role,
-          permissionId: "system_tenants:read",
-          organizationId: "sys",
-        },
-      ]),
-    ];
-    dbMock.select.mockReturnValue(mockChainedQuery(existingRows));
+      // Return existing rows covering every permission that seedSystemRbac would generate
+      // for owner, admin, and member roles so the deduplication sees them all as existing.
+      const existingRows = [
+        // member base perms (organizationId: null)
+        { roleId: "member", permissionId: "users:read", organizationId: null },
+        { roleId: "member", permissionId: "tenants:read", organizationId: null },
+        {
+          roleId: "member",
+          permissionId: "dashboard:read",
+          organizationId: null,
+        },
+        // member system perms (organizationId: "sys")
+        {
+          roleId: "member",
+          permissionId: "admin_dashboard:view",
+          organizationId: "sys",
+        },
+        {
+          roleId: "member",
+          permissionId: "system_users:read",
+          organizationId: "sys",
+        },
+        {
+          roleId: "member",
+          permissionId: "system_tenants:read",
+          organizationId: "sys",
+        },
+        // admin & owner — all perms (null + sys scoped)
+        ...["admin", "owner"].flatMap((role) => [
+          { roleId: role, permissionId: "users:read", organizationId: null },
+          { roleId: role, permissionId: "tenants:read", organizationId: null },
+          { roleId: role, permissionId: "dashboard:read", organizationId: null },
+          {
+            roleId: role,
+            permissionId: "admin_dashboard:view",
+            organizationId: "sys",
+          },
+          {
+            roleId: role,
+            permissionId: "system_users:read",
+            organizationId: "sys",
+          },
+          {
+            roleId: role,
+            permissionId: "system_tenants:read",
+            organizationId: "sys",
+          },
+        ]),
+      ];
+      dbMock.select.mockReturnValue(mockChainedQuery(existingRows));
 
-    const loggerMock = { log: vi.fn(), error: vi.fn() } as unknown as Logger;
-    const optionsMock = mkOptions();
+      const loggerMock = { log: vi.fn(), error: vi.fn() } as unknown as Logger;
+      const optionsMock = mkOptions();
 
-    await seedSystemRbac(dbMock, optionsMock.constants, loggerMock);
+      await seedSystemRbac(dbMock, optionsMock.constants, loggerMock);
 
-    expect(loggerMock.log).toHaveBeenCalledWith(
-      "No new role permissions to insert.",
-    );
-
-    vi.doUnmock("../constants.js");
+      expect(loggerMock.log).toHaveBeenCalledWith(
+        "No new role permissions to insert.",
+      );
+    } finally {
+      vi.doUnmock("../constants.js");
+      vi.resetModules();
+    }
   });

Apply the same pattern to the test starting at line 283.

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

In `@packages/identity/src/services/permission-seeder.spec.ts` around lines 196 -
302, The test blocks that call vi.doMock("../constants.js") must ensure module
mock cleanup even if imports or assertions throw; wrap the dynamic import of
seedSystemRbac, the calls to seedSystemRbac, and the expect assertions in a
try/finally and move vi.doUnmock("../constants.js") into the finally block so
the mock is always removed. Specifically, for the two tests that mock constants
(the one verifying "No new role permissions to insert." and the "seed throws
error on invalid permission format" test), keep vi.resetModules() and
vi.doMock(...) as-is, then perform the await import("../utils/rbac-seeding.js")
and subsequent calls to seedSystemRbac/dbMock/loggerMock/optionsMock and
assertions inside a try, and call vi.doUnmock("../constants.js") in the finally
to guarantee cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/connectors/src/crypto/encryption.service.ts`:
- Around line 3-7: The current AesEncryptionService imports the base
EncryptionService from TokenManagerService, coupling crypto to the OAuth module;
create a new standalone abstraction (e.g., export an abstract class or interface
named EncryptionService in a new crypto-level file) and move the
EncryptionService declaration there, then update AesEncryptionService to import
EncryptionService from that new module and also update TokenManagerService to
import the same EncryptionService; ensure the new file exports the same symbol
name (EncryptionService) so AesEncryptionService and TokenManagerService
reference identical types and remove the original export from
token-manager.service.

In `@packages/database/package.json`:
- Line 45: The file ends with a closing brace '}' but lacks a trailing newline;
open the package.json that ends with that '}' and add a single newline character
after it (ensuring the file ends with '\n') so it conforms to POSIX/newline
conventions and linters.

In `@packages/identity/src/adapters/drizzle-user.adapter.spec.ts`:
- Line 2: Remove the file-wide "/* eslint-disable
`@typescript-eslint/unbound-method` */" and instead add inline eslint-disable
comments only on the specific assertion lines that provoke the Vitest false
positive (the expect(...) calls in drizzle-user.adapter.spec.ts that reference
unbound methods). Locate the offending expect(...) statements in the spec and
append an inline comment like // eslint-disable-next-line
`@typescript-eslint/unbound-method` to each such line, leaving the rest of the
file linting intact.

In `@TECHNICAL_DEBT.md`:
- Around line 25-26: The doc currently recommends implementing runPIICleanup
with `@nestjs/schedule` which will execute in each replica and produce duplicate
cleanup/audit events; update the guidance in the background/jobs section to
explicitly warn against using an in-process cron without a singleton guarantee
and list safe alternatives: runPIICleanup only from a single dedicated worker
process, schedule it via an external orchestrator (e.g., Kubernetes CronJob), or
protect the in-process job with a distributed lock/leader election (e.g., Redis
Redlock, Consul/etcd leader election) and provide a short note to ensure audit
events are emitted exactly once per cleanup pass.

---

Outside diff comments:
In `@packages/identity/src/services/permission-seeder.spec.ts`:
- Around line 196-302: The test blocks that call vi.doMock("../constants.js")
must ensure module mock cleanup even if imports or assertions throw; wrap the
dynamic import of seedSystemRbac, the calls to seedSystemRbac, and the expect
assertions in a try/finally and move vi.doUnmock("../constants.js") into the
finally block so the mock is always removed. Specifically, for the two tests
that mock constants (the one verifying "No new role permissions to insert." and
the "seed throws error on invalid permission format" test), keep
vi.resetModules() and vi.doMock(...) as-is, then perform the await
import("../utils/rbac-seeding.js") and subsequent calls to
seedSystemRbac/dbMock/loggerMock/optionsMock and assertions inside a try, and
call vi.doUnmock("../constants.js") in the finally to guarantee cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f13c53ac-67d6-4edd-a1f2-46f01ef6321e

📥 Commits

Reviewing files that changed from the base of the PR and between ce7a599 and 152cbae.

📒 Files selected for processing (13)
  • TECHNICAL_DEBT.md
  • apps/api/src/db/database-manager.spec.ts
  • apps/api/src/db/reset-e2e.ts
  • apps/api/src/main.ts
  • apps/api/src/modules/connections/connections/callback.controller.spec.ts
  • apps/api/src/modules/identity/users/users.controller.ts
  • apps/api/src/modules/trigger/trigger.module.ts
  • apps/api/src/scripts/admin-bootstrap.ts
  • packages/connectors/src/crypto/encryption.service.ts
  • packages/database/package.json
  • packages/identity/src/adapters/drizzle-user.adapter.spec.ts
  • packages/identity/src/services/permission-seeder.spec.ts
  • packages/pieces/salesforce/tsconfig.json

Comment on lines +3 to +7
import { EncryptionService } from '../oauth/token-manager.service.js';
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';

@Injectable()
export class AesEncryptionService {
export class AesEncryptionService extends EncryptionService {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Decouple the crypto contract from token-manager.service.

Line 3 now pulls the base abstraction from packages/connectors/src/oauth/token-manager.service.ts:8-11, so the crypto implementation depends on the OAuth module just to inherit EncryptionService. That boundary is backwards and makes reuse/circular-import regressions easier. Please move EncryptionService into its own file under crypto or a shared abstractions module, then have both AesEncryptionService and TokenManagerService import it from there.

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

In `@packages/connectors/src/crypto/encryption.service.ts` around lines 3 - 7, The
current AesEncryptionService imports the base EncryptionService from
TokenManagerService, coupling crypto to the OAuth module; create a new
standalone abstraction (e.g., export an abstract class or interface named
EncryptionService in a new crypto-level file) and move the EncryptionService
declaration there, then update AesEncryptionService to import EncryptionService
from that new module and also update TokenManagerService to import the same
EncryptionService; ensure the new file exports the same symbol name
(EncryptionService) so AesEncryptionService and TokenManagerService reference
identical types and remove the original export from token-manager.service.

"typescript": "^5.7.3"
}
}
} No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor: Missing trailing newline.

The file ends without a trailing newline. POSIX convention and most linters expect files to end with a newline character.

🔧 Add trailing newline
   }
-}
+}
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

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

In `@packages/database/package.json` at line 45, The file ends with a closing
brace '}' but lacks a trailing newline; open the package.json that ends with
that '}' and add a single newline character after it (ensuring the file ends
with '\n') so it conforms to POSIX/newline conventions and linters.

@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/unbound-method */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Scope the unbound-method suppression to the offending assertions.

Disabling @typescript-eslint/unbound-method for the whole spec hides real unbound calls in the rest of the file. Keep the suppression inline on the specific expect(...) lines that trigger the Vitest false positive instead of broadening it to file scope.

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

In `@packages/identity/src/adapters/drizzle-user.adapter.spec.ts` at line 2,
Remove the file-wide "/* eslint-disable `@typescript-eslint/unbound-method` */"
and instead add inline eslint-disable comments only on the specific assertion
lines that provoke the Vitest false positive (the expect(...) calls in
drizzle-user.adapter.spec.ts that reference unbound methods). Locate the
offending expect(...) statements in the spec and append an inline comment like
// eslint-disable-next-line `@typescript-eslint/unbound-method` to each such line,
leaving the rest of the file linting intact.

Comment thread TECHNICAL_DEBT.md
Comment on lines +23 to +26
- Keep raw IP/user-agent on insert for security auditing.
- Remove or rename the legacy `anonymizeIp` and `anonymizeUserAgent` pre-insert helpers if they exist.
- Implement `runPIICleanup` inside the `background`/`jobs` module using `@nestjs/schedule` to run daily.
- This job will find sessions older than 30 days and anonymize their PII (nullify or hash), emitting audit logs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Specify one irreversible cleanup strategy.

nullify or hash is too ambiguous for a compliance-sensitive path. Those options have different privacy properties, and a hash can still leave the data linkable. Please document a single approved transformation and explicitly forbid raw IP/user-agent from being copied into the audit logs produced by this job.

Suggested doc change
-- This job will find sessions older than 30 days and anonymize their PII (nullify or hash), emitting audit logs.
+- This job will find sessions older than 30 days and apply a single approved irreversible cleanup strategy to their PII.
+- Prefer nulling the raw `ipAddress` and `userAgent` fields unless a reviewed retention requirement explicitly requires a non-reversible derived value.
+- Audit logs must record counts/record IDs/timestamps only and must not persist raw `ipAddress` or `userAgent`.

Comment thread TECHNICAL_DEBT.md
Comment on lines +25 to +26
- Implement `runPIICleanup` inside the `background`/`jobs` module using `@nestjs/schedule` to run daily.
- This job will find sessions older than 30 days and anonymize their PII (nullify or hash), emitting audit logs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid documenting this as an in-process cron without a singleton guarantee.

Putting runPIICleanup behind @nestjs/schedule in the app module will run it once per replica in a scaled deployment. That creates duplicate cleanup passes and duplicate audit events unless you also define leader election or a distributed lock.

Suggested doc change
-- Implement `runPIICleanup` inside the `background`/`jobs` module using `@nestjs/schedule` to run daily.
+- Implement `runPIICleanup` as a singleton scheduled task.
+- If `@nestjs/schedule` is used, guard execution with leader election or a distributed lock; otherwise run it from a dedicated worker/queue so only one instance performs the daily cleanup.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Implement `runPIICleanup` inside the `background`/`jobs` module using `@nestjs/schedule` to run daily.
- This job will find sessions older than 30 days and anonymize their PII (nullify or hash), emitting audit logs.
- Implement `runPIICleanup` as a singleton scheduled task.
- If `@nestjs/schedule` is used, guard execution with leader election or a distributed lock; otherwise run it from a dedicated worker/queue so only one instance performs the daily cleanup.
- This job will find sessions older than 30 days and anonymize their PII (nullify or hash), emitting audit logs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TECHNICAL_DEBT.md` around lines 25 - 26, The doc currently recommends
implementing runPIICleanup with `@nestjs/schedule` which will execute in each
replica and produce duplicate cleanup/audit events; update the guidance in the
background/jobs section to explicitly warn against using an in-process cron
without a singleton guarantee and list safe alternatives: runPIICleanup only
from a single dedicated worker process, schedule it via an external orchestrator
(e.g., Kubernetes CronJob), or protect the in-process job with a distributed
lock/leader election (e.g., Redis Redlock, Consul/etcd leader election) and
provide a short note to ensure audit events are emitted exactly once per cleanup
pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (1)
packages/identity/src/adapters/drizzle-user.adapter.spec.ts (1)

181-196: 🧹 Nitpick | 🔵 Trivial

Assert the full cascade, not just “some delete happened.”

txCalls.length > 0 still passes if only one table is deleted. Since packages/identity/src/adapters/drizzle-user.adapter.ts:67-80 is explicitly cascading through five tables, this test should verify that full sequence so partial cleanup regressions do not slip through.

Proposed test strengthening
-    const txCalls: string[] = [];
+    const deletedTables: unknown[] = [];
     db.transaction.mockImplementation((fn: (tx: MockTx) => unknown) => {
       const tx = {
-        delete: vi.fn().mockImplementation(() => {
-          txCalls.push("delete");
+        delete: vi.fn().mockImplementation((table) => {
+          deletedTables.push(table);
           return { where: vi.fn().mockReturnThis() };
         }),
       } as unknown as MockTx;
       return fn(tx);
     });
@@
     await adapter.delete("u1");
 
     expect(db.transaction).toHaveBeenCalled();
-    expect(txCalls.length).toBeGreaterThan(0);
+    expect(deletedTables).toEqual([
+      schema.member,
+      schema.invitation,
+      schema.session,
+      schema.account,
+      schema.user,
+    ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/identity/src/adapters/drizzle-user.adapter.spec.ts` around lines 181
- 196, The test currently only asserts txCalls.length > 0 which allows partial
cascades; update the db.transaction mock and assertions to validate the full
cascade invoked by adapter.delete: in the mockImplementation of db.transaction
(the tx delete mock that pushes into txCalls), record distinct identifiers for
each targeted table (or push an entry when the chained where() is invoked) in
the order the adapter cascades through tables referenced in the delete
implementation, then assert txCalls equals the exact expected sequence (five
entries in the correct order) and maybe verify the exact count equals 5; keep
the db.transaction mock, txCalls array and the call to await
adapter.delete("u1") but replace the weak
expect(txCalls.length).toBeGreaterThan(0) with strict equality and order
assertions matching the cascade in the adapter.delete implementation.
🤖 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 `@packages/identity/src/adapters/drizzle-user.adapter.spec.ts`:
- Around line 181-196: The test currently only asserts txCalls.length > 0 which
allows partial cascades; update the db.transaction mock and assertions to
validate the full cascade invoked by adapter.delete: in the mockImplementation
of db.transaction (the tx delete mock that pushes into txCalls), record distinct
identifiers for each targeted table (or push an entry when the chained where()
is invoked) in the order the adapter cascades through tables referenced in the
delete implementation, then assert txCalls equals the exact expected sequence
(five entries in the correct order) and maybe verify the exact count equals 5;
keep the db.transaction mock, txCalls array and the call to await
adapter.delete("u1") but replace the weak
expect(txCalls.length).toBeGreaterThan(0) with strict equality and order
assertions matching the cascade in the adapter.delete implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3bbec596-f20b-417a-adc5-5bd5eddcda56

📥 Commits

Reviewing files that changed from the base of the PR and between 152cbae and 2ed8e49.

📒 Files selected for processing (6)
  • packages/connectors/src/crypto/encryption.interface.ts
  • packages/connectors/src/crypto/encryption.service.ts
  • packages/connectors/src/index.ts
  • packages/connectors/src/oauth/token-manager.service.ts
  • packages/identity/src/adapters/drizzle-user.adapter.spec.ts
  • packages/identity/src/services/permission-seeder.spec.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/identity/src/adapters/drizzle-user.adapter.spec.ts`:
- Around line 482-489: The test currently verifies that countChain.innerJoin was
called but not that the tenant filter was applied to the count query; update the
spec that calls adapter.findAll to also assert countChain.where was invoked with
the same tenant predicate used for dataChain (e.g., check countChain.where was
called and/or calledWith matching tenantId predicate), referencing the existing
mocks countChain and dataChain and the adapter.findAll invocation so the
count-side pagination total is validated for tenant scoping.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6d8ea45f-5ae2-4180-9bf7-4dadf5d0ad77

📥 Commits

Reviewing files that changed from the base of the PR and between 2ed8e49 and 568ff94.

📒 Files selected for processing (1)
  • packages/identity/src/adapters/drizzle-user.adapter.spec.ts

Comment on lines 482 to 489
await adapter.findAll({ tenantId: "t1" });

expect(dataChain.innerJoin).toHaveBeenCalled();

expect(countChain.innerJoin).toHaveBeenCalled();

expect(dataChain.where).toHaveBeenCalled();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assert tenant filtering on the count query too.

This spec verifies innerJoin() on countChain, but it never checks that the tenant predicate is applied there. If the count-side where() is omitted, the test still passes while pagination totals leak across tenants.

Suggested fix
     expect(dataChain.innerJoin).toHaveBeenCalled();
     expect(countChain.innerJoin).toHaveBeenCalled();
     expect(dataChain.where).toHaveBeenCalled();
+    expect(countChain.where).toHaveBeenCalled();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await adapter.findAll({ tenantId: "t1" });
expect(dataChain.innerJoin).toHaveBeenCalled();
expect(countChain.innerJoin).toHaveBeenCalled();
expect(dataChain.where).toHaveBeenCalled();
});
await adapter.findAll({ tenantId: "t1" });
expect(dataChain.innerJoin).toHaveBeenCalled();
expect(countChain.innerJoin).toHaveBeenCalled();
expect(dataChain.where).toHaveBeenCalled();
expect(countChain.where).toHaveBeenCalled();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/identity/src/adapters/drizzle-user.adapter.spec.ts` around lines 482
- 489, The test currently verifies that countChain.innerJoin was called but not
that the tenant filter was applied to the count query; update the spec that
calls adapter.findAll to also assert countChain.where was invoked with the same
tenant predicate used for dataChain (e.g., check countChain.where was called
and/or calledWith matching tenantId predicate), referencing the existing mocks
countChain and dataChain and the adapter.findAll invocation so the count-side
pagination total is validated for tenant scoping.

@pramodnarayana
pramodnarayana merged commit f5d4906 into development Mar 7, 2026
2 checks passed
This was referenced Mar 21, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 5, 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.

1 participant