Skip to content

Conversation

@Chrahuldeveloper
Copy link

@Chrahuldeveloper Chrahuldeveloper commented Nov 24, 2025

Added unit test cases for all functions

Summary by CodeRabbit

  • Tests

    • Added Jest test suites and test scripts across functions to improve reliability and coverage.
  • Bug Fixes

    • More robust request validation and consistent error responses for join, OTP, and email flows.
    • Improved cleanup and pairing flows to reduce stale data and edge-case failures.
  • Improvements

    • Strengthened input validation, logging, and observability across services.
  • Refactor

    • Standardized module format and exports for consistent runtime behavior.

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

@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Warning

Rate limit exceeded

@Chrahuldeveloper has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 28 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 24046c3 and 1863852.

📒 Files selected for processing (2)
  • functions/match-maker/src/main.js (1 hunks)
  • functions/start-friend-call/src/main.js (2 hunks)

Walkthrough

Converted many functions from ES modules to CommonJS, added Jest test scripts and multiple Jest suites, refactored database-cleaner to cursor-based batched cleanup with new existence checks, and applied validation, formatting, and minor control-flow adjustments across handlers and utilities.

Changes

Cohort / File(s) Summary
Package manifests
functions/*/package.json (e.g., functions/create-room/package.json, functions/database-cleaner/package.json, functions/delete-room/package.json, functions/join-room/package.json, functions/match-maker/package.json, functions/send-otp/package.json, functions/sync-*/package.json, functions/verify-*/package.json, ...)
Added "test": "jest" and @jest/globals & jest to many packages, removed several "type": "module" entries, and applied minor EOF/formatting tweaks.
Module system migration
functions/*/src/*.js (examples: .../src/main.js, .../src/appwrite.js, .../src/utils.js, .../src/mail.js)
Replaced ESM import/export with CommonJS require()/module.exports across many handlers and utilities; preserved public signatures while adjusting import/export mechanics and formatting.
Database-cleaner refactor
functions/database-cleaner/src/appwrite.js, functions/database-cleaner/src/utils.js, functions/database-cleaner/src/appwrite.test.js
Reworked cleanup to cursor-based batched pagination (participants: 25, OTPs: 100), added doesRoomExist(roomId), removed cleanActivePairsCollection(), centralized 404 handling, and converted to CommonJS; added tests.
Appwrite service adjustments
functions/*/src/appwrite.js (e.g., functions/create-room/src/appwrite.js, functions/send-otp/src/appwrite.js)
Switched Appwrite SDK usage to require, removed some .setKey usage in places, and changed exports to module.exports; core create-room/create-otp semantics unchanged.
New/updated Jest tests with mocks
functions/*/src/*.test.js (examples: functions/create-room/src/appwrite.test.js, functions/database-cleaner/src/appwrite.test.js, functions/delete-room/src/main.test.js, functions/join-room/src/main.test.js, functions/match-maker/src/main.test.js, functions/send-otp/src/main.test.js, functions/sync-*/src/main.test.js, functions/verify-*/src/main.test.js, ...)
Added comprehensive Jest suites that mock node-appwrite, MeiliSearch, Firebase, nodemailer, LiveKit and validate success, pagination, validation errors, and failure modes.
Handler logic tweaks & validations
functions/join-room/src/main.js, functions/match-maker/src/main.js, functions/delete-room/src/main.js, functions/sync-*/src/main.js, functions/verify-*/src/main.js
Introduced/centralized environment and request-field validations, adjusted trigger ID extraction and try/catch placements, moved some parallel deletions to sequential/batched flows; preserved overall behavior.
Mail & minor formatting edits
functions/send-otp/src/mail.js, functions/create-room/src/livekit.js, functions/create-room/src/main.js, functions/start-friend-call/src/main.js, init.sh, ...
Converted nodemailer import/exports to CommonJS, small whitespace/indent/quoting/trailing-comma adjustments; no behavior changes for MailService or LiveKit token logic.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

  • Areas needing extra attention:
    • functions/database-cleaner/src/appwrite.js — cursor pagination loop, cursor update/termination conditions, batch deletion correctness, and doesRoomExist error handling (404 vs other errors).
    • Module-system migration — ensure deployment/runtime consumers expect CommonJS (verify no remaining ESM-only runtimes or bundler mismatches).
    • New tests/mocks — confirm mock shapes and error scenarios reflect real SDK behavior (node-appwrite, MeiliSearch, Firebase, nodemailer, LiveKit).
    • Handlers with changed deletion/iteration semantics (delete-room, match-maker) — confirm transactional ordering and side effects.

Possibly related PRs

Suggested labels

enhancement

Poem

🐇 I hopped through imports, quick and neat,

Swapped exports, tests, and batching beat.
Pages march in cursors, cleaned with care,
Mocks hum softly in the test-run air.
A nibble, a patch — the code feels light and sweet.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding unit test cases across all functions in the repository.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

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

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (2)
functions/sync-all-documents-with-meilisearch/src/utils.js (1)

8-18: Code duplication: extract shared throwIfMissing to a common utility.

The throwIfMissing function is duplicated across multiple files (functions/match-maker/src/utils.js, functions/send-otp/src/utils.js, functions/sync-stories-with-meilisearch/src/utils.js, functions/sync-users-with-meilisearch/src/utils.js). This violates the DRY principle and makes maintenance harder.

Consider creating a shared utility module that all functions can import:

  1. Create shared/utils.js at the repository root:
const throwIfMissing = (obj, keys) => {
  const missing = [];
  for (let key of keys) {
    if (!(key in obj) || !obj[key]) {
      missing.push(key);
    }
  }
  if (missing.length > 0) {
    throw new Error(`Missing required fields: ${missing.join(', ')}`);
  }
};

module.exports = { throwIfMissing };
  1. Update each function's utils.js to import from the shared module:
const { throwIfMissing } = require('../../shared/utils');
module.exports = { throwIfMissing };
functions/sync-all-documents-with-meilisearch/src/main.js (1)

5-21: Validate Appwrite env vars now that you require an API key

You added .setKey(process.env.APPWRITE_API_KEY), but throwIfMissing only validates the Meili envs. If any of APPWRITE_ENDPOINT, APPWRITE_FUNCTION_PROJECT_ID, or APPWRITE_API_KEY are missing, you’ll get harder‑to‑diagnose runtime errors from the Appwrite client.

Consider:

-  throwIfMissing(process.env, [
-    'MEILISEARCH_ENDPOINT',
-    'MEILISEARCH_ADMIN_API_KEY',
-  ]);
+  throwIfMissing(process.env, [
+    'MEILISEARCH_ENDPOINT',
+    'MEILISEARCH_ADMIN_API_KEY',
+    'APPWRITE_ENDPOINT',
+    'APPWRITE_FUNCTION_PROJECT_ID',
+    'APPWRITE_API_KEY',
+  ]);

to fail fast with a clear message.

🟠 Major comments (10)
functions/upcomingRoom-Message-Notification/package.json-13-15 (1)

13-15: Move Jest dependencies to devDependencies.

Testing libraries should be in devDependencies, not dependencies. Production deployments don't need testing tools, and including them in dependencies unnecessarily increases bundle size.

Apply this diff:

   "license": "ISC",
   "dependencies": {
-    "@jest/globals": "^30.2.0",
     "firebase-admin": "^11.11.0",
+    "node-appwrite": "^17.0.0"
+  },
+  "devDependencies": {
+    "@jest/globals": "^30.2.0",
     "jest": "^30.2.0",
-    "node-appwrite": "^17.0.0"
   }
 }

Committable suggestion skipped: line range outside the PR's diff.

functions/upcomingRoom-isTime-checker/package.json-7-7 (1)

7-7: Move Jest packages to devDependencies.

Test dependencies belong in devDependencies, not dependencies, to keep production bundles lean and follow npm conventions.

Apply this diff:

   "scripts": {
     "test": "jest"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
+  "devDependencies": {
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0"
+  },
   "dependencies": {
-    "@jest/globals": "^30.2.0",
     "firebase-admin": "^11.11.0",
-    "jest": "^30.2.0",
     "node-appwrite": "^17.0.0"
   }

Jest 30.2.0 is the latest stable version as of November 2025, confirming the version specified is current and stable.

Also applies to: lines 13-13, 15-15

functions/verify-otp/package.json-12-16 (1)

12-16: Move Jest to devDependencies.

Jest and @jest/globals are test-time dependencies and should be in devDependencies, not dependencies. Placing them in runtime dependencies increases bundle size and installation time in production.

Apply this diff:

   },
-  "dependencies": {
+  "devDependencies": {
     "@jest/globals": "^30.2.0",
     "jest": "^30.2.0",
-    "node-appwrite": "^17.0.0"
-  },
-  "devDependencies": {
+    "node-appwrite": "^17.0.0",
     "prettier": "^3.0.0"
   }

Committable suggestion skipped: line range outside the PR's diff.

functions/join-room/src/main.js-10-18 (1)

10-18: Avoid parsing request body twice.

JSON.parse(req.body) is called on line 11 and again on line 18. This is inefficient and could theoretically produce different results if the body is modified between calls.

Apply this diff to parse once:

+  let body;
   try {
-    throwIfMissing(JSON.parse(req.body), ["roomName", "uid"]);
+    body = JSON.parse(req.body);
+    throwIfMissing(body, ["roomName", "uid"]);
   } catch (err) {
     return res.json({ msg: err.message }, 400);
   }

   try {
     log(req);
-    const { roomName, uid: userId } = JSON.parse(req.body);
+    const { roomName, uid: userId } = body;
functions/delete-room/package.json-12-17 (1)

12-17: Move Jest to devDependencies.

Jest and @jest/globals are test-time only dependencies and should be in devDependencies, not dependencies. This reduces production bundle size.

Apply this diff:

     },
     "dependencies": {
-        "@jest/globals": "^30.2.0",
-        "jest": "^30.2.0",
         "livekit-server-sdk": "^1.2.6",
         "node-appwrite": "^17.0.0"
     },
     "devDependencies": {
+        "@jest/globals": "^30.2.0",
+        "jest": "^30.2.0",
         "prettier": "^3.0.0"
     }
functions/join-room/src/main.js-4-8 (1)

4-8: Move environment validation inside the request handler.

Environment validation at module level executes when the module is loaded (on require()), not per request. If any environment variable is missing, the entire service crashes on startup, making it impossible to handle the error gracefully or return a meaningful response.

Apply this diff to move validation inside the handler:

 const { generateToken } = require("./livekit.js");
 const { throwIfMissing } = require("./utils.js");
+
 module.exports = async ({ req, res, log, error }) => {
+  try {
   throwIfMissing(process.env, [
     "LIVEKIT_API_KEY",
     "LIVEKIT_API_SECRET",
     "LIVEKIT_SOCKET_URL",
   ]);
+  } catch (err) {
+    error(err.message);
+    return res.json({ msg: err.message }, 500);
+  }

   try {
functions/sync-all-documents-with-meilisearch/package.json-12-17 (1)

12-17: Move Jest to devDependencies.

Jest and @jest/globals should be in devDependencies since they're only needed during testing, not at runtime.

Apply this diff:

   "keywords": [],
-  "dependencies": {
-    "@jest/globals": "^30.2.0",
-    "jest": "^30.2.0",
+  "dependencies": {
     "meilisearch": "^0.40.0",
     "node-appwrite": "^14.1.0"
   },
   "devDependencies": {
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0",
     "prettier": "^3.2.5"
   }
functions/send-otp/src/main.js-17-33 (1)

17-33: Return a non-200 status code on sendOtp errors.

In the catch block you log the error and return a JSON error payload, but you don’t set an explicit HTTP status, so it will likely return 200 even on failure. Other handlers in this PR use 500 for similar error cases; aligning with that would make error handling clearer for clients.

Consider:

-  } catch (e) {
-    error(String(e));
-    return res.json({ message: `Error: ${String(e)}` });
-  }
+  } catch (e) {
+    error(String(e));
+    return res.json({ message: `Error: ${String(e)}` }, 500);
+  }

Committable suggestion skipped: line range outside the PR's diff.

functions/match-maker/src/main.js-19-23 (1)

19-23: Guard against missing event header and avoid logging full headers

  • triggerEvent is read from req.headers["x-appwrite-event"] and then .split is called immediately. If the header is absent, this will throw at runtime.
  • Logging the entire req.headers can leak sensitive information (auth tokens, signatures) into logs.

Consider:

-  log(req.headers);
-  const triggerEvent = req.headers["x-appwrite-event"];
-  const parts = triggerEvent.split(".");
+  const triggerEvent = req.headers["x-appwrite-event"];
+  if (!triggerEvent) {
+    return res.json({ message: "Missing x-appwrite-event header" }, 400);
+  }
+  log({ "x-appwrite-event": triggerEvent });
+  const parts = triggerEvent.split(".");

This both hardens error handling and limits logged data.

functions/database-cleaner/src/appwrite.js-32-47 (1)

32-47: Fix forEach with async callbacks and add pagination to match other cleaners

cleanParticipantsCollection has two issues:

  1. Race condition: forEach does not await async callbacks, so the method resolves before deletions complete.
  2. Incomplete cleanup: Without pagination, only the first ~25 participants are processed (Appwrite's default limit per request).

The cleanActivePairsCollection method (line 49+) implements the correct pattern with do...while pagination and Promise.all(). Apply the same approach here.

  async cleanParticipantsCollection() {
+   let done = false;
+   do {
      const participantDocs = await this.databases.listDocuments(
        process.env.MASTER_DATABASE_ID,
        process.env.PARTICIPANTS_COLLECTION_ID
+       [Query.limit(25)]
      );
-
-   participantDocs.documents.forEach(async (participantDoc) => {
-     if (!(await this.doesRoomExist(participantDoc.roomId))) {
-       await this.databases.deleteDocument(
-         process.env.MASTER_DATABASE_ID,
-         process.env.PARTICIPANTS_COLLECTION_ID,
-         participantDoc.$id
-       );
-     }
-   });
+
+     await Promise.all(
+       participantDocs.documents.map(async (participantDoc) => {
+         if (!(await this.doesRoomExist(participantDoc.roomId))) {
+           await this.databases.deleteDocument(
+             process.env.MASTER_DATABASE_ID,
+             process.env.PARTICIPANTS_COLLECTION_ID,
+             participantDoc.$id
+           );
+         }
+       })
+     );
+
+     done = participantDocs.documents.length < 25;
+   } while (!done);
  }
🟡 Minor comments (5)
init.sh-8-9 (1)

8-9: Remove trailing whitespace on line 8 and reconsider the blank line addition.

Line 8 introduces trailing whitespace after the string value "UNKNOWN:$OS", which violates standard code quality practices and will likely fail linting checks. Additionally, the blank line added at line 9 appears arbitrary and unrelated to the PR's stated objective of adding unit tests. These formatting changes to the bash initialization script seem incidental to the PR scope.

Apply this diff to remove the trailing whitespace:

-    *)          OS_TYPE="UNKNOWN:$OS" 
+    *)          OS_TYPE="UNKNOWN:$OS"
-    
+
functions/join-room/src/main.test.js-9-15 (1)

9-15: Mock implementation doesn't match actual utility behavior.

The mock on line 12 throws "Missing required field: ${key}" (singular), but the actual throwIfMissing implementation throws "Missing required fields: ${missing.join(', ')}" (plural with all missing keys). This discrepancy means the test on line 67 validates against mock behavior rather than actual behavior.

Apply this diff to match the actual implementation:

 jest.mock("./utils.js", () => ({
   throwIfMissing: jest.fn((obj, arr) => {
+    const missing = [];
     arr.forEach((key) => {
-      if (!obj[key]) throw new Error(`Missing required field: ${key}`);
+      if (!(key in obj) || !obj[key]) missing.push(key);
     });
+    if (missing.length > 0) {
+      throw new Error(`Missing required fields: ${missing.join(', ')}`);
+    }
   }),
 }));

And update the test expectation:

     expect(mockRes.json).toHaveBeenCalledWith(
-      { msg: "Missing required field: roomName" },
+      { msg: "Missing required fields: roomName" },
       400
     );
functions/verify-otp/src/main.js-4-24 (1)

4-24: Tighten env + body validation to avoid hard crashes

Right now you only validate APPWRITE_API_KEY and DB/collection IDs, but you also unconditionally use APPWRITE_FUNCTION_PROJECT_ID and assume req.body is valid JSON with otpID, userOTP, and verify_ID. If any of these are missing/invalid, the function will throw before your try/catch blocks and may not return a clean JSON error.

Consider:

  • Adding APPWRITE_FUNCTION_PROJECT_ID to the throwIfMissing env list.
  • Optionally wrapping JSON.parse(req.body) + destructuring in a small try/catch and validating the required body fields, returning a 400 on bad input instead of letting it bubble.
functions/verify-email/src/main.js-5-23 (1)

5-23: Align env validation and response semantics with other functions

A couple of consistency / robustness points here:

  • You validate only APPWRITE_API_KEY, but also rely on APPWRITE_FUNCTION_PROJECT_ID. If it’s missing, setProject(undefined) will blow up later in the SDK. Add it to the throwIfMissing list.
  • For symmetry with verify-otp and clearer semantics, it’s worth returning an explicit 200:
-  throwIfMissing(process.env, ["APPWRITE_API_KEY"]);
+  throwIfMissing(process.env, [
+    "APPWRITE_API_KEY",
+    "APPWRITE_FUNCTION_PROJECT_ID",
+  ]);-  return res.json({ message: "null" });
+  return res.json({ message: "null" }, 200);

You may also want to (optionally) wrap JSON.parse(req.body) in a try/catch and treat malformed bodies as 400s.

functions/delete-room/src/main.test.js-13-16 (1)

13-16: Fix Query.equal mock to return something usable

The current mock:

  • Misspells field as feild.
  • Uses a block body without return, so Query.equal always returns undefined.

If the code under test ever depends on the return value, this will cause misleading failures. A simple, self-describing stub works better:

-    Query: {
-      equal: jest.fn((feild, value) => {
-        feild, value;
-      }),
-    },
+    Query: {
+      equal: jest.fn((field, value) => ({ field, value })),
+    },
🧹 Nitpick comments (17)
functions/upcomingRoom-Message-Notification/src/main.test.js (1)

35-117: Consider adding error handling test cases.

While the current tests cover success, partial failure, and missing tokens scenarios well, consider adding tests for:

  • Firebase/Appwrite throwing exceptions
  • Malformed request body (invalid JSON)
  • Missing required fields (roomId or payload)

These would improve robustness validation but are not critical for the current PR scope.

functions/upcomingRoom-isTime-checker/src/main.test.js (1)

36-127: Test coverage is solid, but consider mocking Date for reliability.

The tests correctly verify the ±5 minute window logic and handle multiple scenarios. However, each test creates its own now while the function under test creates a separate now, introducing potential timing skew that could cause flakiness.

For more reliable tests, consider either:

  1. Mock the Date constructor:
beforeEach(() => {
  jest.useFakeTimers();
  jest.setSystemTime(new Date('2025-01-01T12:00:00Z'));
});

afterEach(() => {
  jest.useRealTimers();
});
  1. Or refactor main.js to accept an optional time parameter for testing (requires changes outside this PR).

Additionally, consider adding tests for error scenarios (e.g., database failures, malformed dates).

functions/sync-stories-with-meilisearch/src/utils.js (1)

8-20: Extract shared utility to eliminate duplication.

The throwIfMissing function is duplicated across at least 5 files (match-maker, send-otp, sync-all-documents, sync-users, and this file). Consider extracting this to a shared utility module to reduce duplication and ensure consistency.

Do you want me to generate a shared utility module structure for this project?

functions/delete-room/package.json (1)

8-8: Remove extra space in test script.

There's an extra space before the colon in "test" : "jest". While functional, it's inconsistent with the other scripts.

Apply this diff:

     "scripts": {
-        "test" : "jest",
+        "test": "jest",
         "start": "node src/main.js",
functions/send-otp/src/utils.js (1)

1-13: throwIfMissing implementation is fine; confirm falsy‑value semantics

Logic and CommonJS export look good. Note that !obj[key] treats 0, false, and '' as missing—fine for config/env checks but surprising if those are valid values. Adjust condition if you ever need to allow falsy-but-present values.

functions/create-room/src/appwrite.test.js (1)

1-40: Solid AppwriteService test; consider resetting the shared mock

The mock setup correctly verifies createRoom’s interaction with createDocument and the returned $id. To keep future tests isolated, it’s worth adding createDocumentMock.mockReset() inside beforeEach so additional tests don’t inherit prior call history.

functions/sync-users-with-meilisearch/src/utils.js (1)

8-20: throwIfMissing is correct; consider centralizing this shared helper

The implementation and CommonJS export are fine. Since the same throwIfMissing logic appears in several utils modules, you might later want to centralize it (e.g., a shared helper package) to avoid copy‑paste divergence.

functions/sync-stories-with-meilisearch/src/main.test.js (1)

1-80: Good coverage of Meilisearch sync flow; optional assertion on index name.

The tests nicely cover create, update, delete, and validation (missing ID/trigger) paths with mocked MeiliSearch. If you want stronger regression protection, consider making index a jest.fn() and asserting it’s called with the expected index name (e.g., 'stories'), but this is optional polish.

functions/sync-users-with-meilisearch/src/main.test.js (1)

1-80: User sync tests are solid; optional enhancement similar to stories tests.

This suite cleanly covers create/update/delete plus error cases for the users index. As with the stories tests, you could optionally assert the index method is invoked with 'users' to lock down the correct index selection.

functions/send-otp/src/main.test.js (1)

1-39: Happy-path OTP test is correct; consider adding error-path coverage.

The test wiring and assertions for mail sending and OTP document creation look good and align with sendOtp. To better protect against regressions, consider adding tests for failure scenarios (e.g., sendMail or createOtpDocument rejecting, or malformed req.body) to assert that the error handler and JSON error response behave as expected.

functions/sync-users-with-meilisearch/src/main.js (1)

1-56: User sync handler and new guards look correct; consider explicit handling for unknown events.

The handler’s structure, MeiliSearch usage, and new guards for missing document IDs and non-Appwrite-triggered requests all look solid and match the tests. You might optionally add a final else/default branch for unexpected x-appwrite-event values (neither delete nor create/update) to return a clear 4xx response instead of silently doing nothing.

functions/database-cleaner/src/utils.js (1)

13-17: Make RETENTION_PERIOD_DAYS parsing defensive

+(process.env.RETENTION_PERIOD_DAYS ?? 1) will yield NaN if the env is non‑numeric, and setDate(getDate() - NaN) results in an invalid date, so toISOString() will throw.

Suggest normalizing with a safe number and sane default, e.g.:

-  const retentionPeriod = +(process.env.RETENTION_PERIOD_DAYS ?? 1);
+  const raw = Number(process.env.RETENTION_PERIOD_DAYS ?? 1);
+  const retentionPeriod = Number.isFinite(raw) && raw >= 0 ? raw : 1;

This keeps behavior the same for valid values but avoids crashes on bad configuration.

functions/match-maker/src/main.test.js (1)

5-106: Good coverage of main branches; consider asserting env validation

The mocks and three tests nicely exercise the “not random”, “empty queue”, and “pairing error” paths, and the Appwrite client/database mocks are sufficient for the current behavior.

If you want to harden the contract a bit more, you could also assert that throwIfMissing is called once at the start of the handler (since you already mock it), so future changes don’t accidentally drop env validation.

functions/match-maker/src/utils.js (1)

3-15: LGTM; optional: deduplicate throwIfMissing across functions

This throwIfMissing implementation is straightforward and consistent with the other function folders.

If this pattern keeps spreading, consider extracting a shared helper (e.g., a small internal package or shared utils module) to avoid having to update the same logic in multiple places later.

functions/database-cleaner/src/appwrite.test.js (1)

1-125: Solid coverage; consider tightening async semantics of cleanParticipantsCollection

The mocks and three tests give good coverage of doesRoomExist, cleanParticipantsCollection, and clearOldOTPs, including happy paths and error handling.

The extra:

await new Promise(process.nextTick);

in the cleanParticipantsCollection test hints that the implementation may fire off async work without fully awaiting it. Ideally, await service.cleanParticipantsCollection() should only resolve once deletions are complete, letting you drop the manual nextTick and rely purely on the method’s Promise.

Not a blocker, but worth revisiting when you next touch AppwriteService.

functions/sync-stories-with-meilisearch/src/main.js (1)

18-47: Return an explicit response for unsupported Appwrite event types

If x-appwrite-event does not include .delete, .create, or .update, the function currently falls through the try block without sending any response. It would be clearer and safer to return an explicit 200/400 with a message indicating that the event type is ignored/unsupported.

Example:

       if (eventType.includes('.delete')) {
         ...
-      } else if (
+      } else if (
         eventType.includes('.create') ||
         eventType.includes('.update')
       ) {
         ...
+      } else {
+        log(`Ignoring unsupported event type: ${eventType}`);
+        return res.text(`No action for event type: ${eventType}`, 200);
       }
functions/match-maker/src/main.js (1)

5-10: Include APPWRITE_FUNCTION_PROJECT_ID in env validation

The Appwrite client is configured with APPWRITE_FUNCTION_PROJECT_ID, but that variable is not part of the throwIfMissing check. If it’s unset, the client will be misconfigured in a non-obvious way.

   throwIfMissing(process.env, [
     "APPWRITE_API_KEY",
+    "APPWRITE_FUNCTION_PROJECT_ID",
     "DATABASE_ID",
     "REQUESTS_COLLECTION_ID",
     "ACTIVE_PAIRS_COLLECTION_ID",
   ]);

This keeps configuration failures fail-fast and easier to diagnose.

Also applies to: 12-15

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 795f29b and 0742618.

⛔ Files ignored due to path filters (14)
  • functions/create-room/package-lock.json is excluded by !**/package-lock.json
  • functions/database-cleaner/package-lock.json is excluded by !**/package-lock.json
  • functions/delete-room/package-lock.json is excluded by !**/package-lock.json
  • functions/join-room/package-lock.json is excluded by !**/package-lock.json
  • functions/match-maker/package-lock.json is excluded by !**/package-lock.json
  • functions/send-otp/package-lock.json is excluded by !**/package-lock.json
  • functions/start-friend-call/package-lock.json is excluded by !**/package-lock.json
  • functions/sync-all-documents-with-meilisearch/package-lock.json is excluded by !**/package-lock.json
  • functions/sync-stories-with-meilisearch/package-lock.json is excluded by !**/package-lock.json
  • functions/sync-users-with-meilisearch/package-lock.json is excluded by !**/package-lock.json
  • functions/upcomingRoom-Message-Notification/package-lock.json is excluded by !**/package-lock.json
  • functions/upcomingRoom-isTime-checker/package-lock.json is excluded by !**/package-lock.json
  • functions/verify-email/package-lock.json is excluded by !**/package-lock.json
  • functions/verify-otp/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (50)
  • functions/create-room/package.json (2 hunks)
  • functions/create-room/src/appwrite.js (1 hunks)
  • functions/create-room/src/appwrite.test.js (1 hunks)
  • functions/create-room/src/livekit.js (1 hunks)
  • functions/create-room/src/main.js (1 hunks)
  • functions/database-cleaner/package.json (1 hunks)
  • functions/database-cleaner/src/appwrite.js (1 hunks)
  • functions/database-cleaner/src/appwrite.test.js (1 hunks)
  • functions/database-cleaner/src/utils.js (1 hunks)
  • functions/delete-room/package.json (1 hunks)
  • functions/delete-room/src/main.js (1 hunks)
  • functions/delete-room/src/main.test.js (1 hunks)
  • functions/join-room/package.json (1 hunks)
  • functions/join-room/src/main.js (1 hunks)
  • functions/join-room/src/main.test.js (1 hunks)
  • functions/match-maker/package.json (1 hunks)
  • functions/match-maker/src/main.js (1 hunks)
  • functions/match-maker/src/main.test.js (1 hunks)
  • functions/match-maker/src/utils.js (1 hunks)
  • functions/send-otp/package.json (1 hunks)
  • functions/send-otp/src/appwrite.js (1 hunks)
  • functions/send-otp/src/mail.js (1 hunks)
  • functions/send-otp/src/main.js (1 hunks)
  • functions/send-otp/src/main.test.js (1 hunks)
  • functions/send-otp/src/utils.js (1 hunks)
  • functions/start-friend-call/package.json (1 hunks)
  • functions/start-friend-call/src/main.js (2 hunks)
  • functions/sync-all-documents-with-meilisearch/package.json (1 hunks)
  • functions/sync-all-documents-with-meilisearch/src/main.js (4 hunks)
  • functions/sync-all-documents-with-meilisearch/src/main.test.js (1 hunks)
  • functions/sync-all-documents-with-meilisearch/src/utils.js (2 hunks)
  • functions/sync-stories-with-meilisearch/package.json (1 hunks)
  • functions/sync-stories-with-meilisearch/src/main.js (3 hunks)
  • functions/sync-stories-with-meilisearch/src/main.test.js (1 hunks)
  • functions/sync-stories-with-meilisearch/src/utils.js (2 hunks)
  • functions/sync-users-with-meilisearch/package.json (1 hunks)
  • functions/sync-users-with-meilisearch/src/main.js (3 hunks)
  • functions/sync-users-with-meilisearch/src/main.test.js (1 hunks)
  • functions/sync-users-with-meilisearch/src/utils.js (2 hunks)
  • functions/upcomingRoom-Message-Notification/package.json (1 hunks)
  • functions/upcomingRoom-Message-Notification/src/main.test.js (1 hunks)
  • functions/upcomingRoom-isTime-checker/package.json (1 hunks)
  • functions/upcomingRoom-isTime-checker/src/main.test.js (1 hunks)
  • functions/verify-email/package.json (1 hunks)
  • functions/verify-email/src/main.js (1 hunks)
  • functions/verify-email/src/main.test.js (1 hunks)
  • functions/verify-otp/package.json (1 hunks)
  • functions/verify-otp/src/main.js (1 hunks)
  • functions/verify-otp/src/main.test.js (1 hunks)
  • init.sh (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (28)
functions/join-room/src/main.test.js (1)
functions/join-room/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/verify-otp/src/main.test.js (1)
functions/verify-otp/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/match-maker/src/main.js (1)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/match-maker/src/utils.js (4)
functions/send-otp/src/utils.js (2)
  • throwIfMissing (1-11)
  • missing (2-2)
functions/sync-all-documents-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-stories-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-users-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-users-with-meilisearch/src/utils.js (4)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/sync-all-documents-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-stories-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/send-otp/src/main.test.js (2)
functions/send-otp/src/main.js (3)
  • require (1-1)
  • MailService (3-3)
  • AppwriteService (2-2)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/database-cleaner/src/appwrite.test.js (2)
functions/create-room/src/appwrite.test.js (2)
  • AppwriteService (19-19)
  • service (22-22)
functions/database-cleaner/src/appwrite.js (2)
  • require (1-1)
  • require (3-3)
functions/delete-room/src/main.js (1)
functions/delete-room/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/send-otp/src/mail.js (1)
functions/send-otp/src/main.js (3)
  • require (1-1)
  • MailService (3-3)
  • otp (21-21)
functions/create-room/src/livekit.js (1)
functions/create-room/src/main.js (1)
  • accessToken (50-50)
functions/verify-email/src/main.test.js (1)
functions/verify-email/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • JSON (16-16)
functions/create-room/src/appwrite.test.js (1)
functions/create-room/src/appwrite.js (1)
  • require (1-1)
functions/verify-otp/src/main.js (2)
functions/verify-otp/src/main.test.js (1)
  • require (2-2)
functions/verify-otp/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/upcomingRoom-isTime-checker/src/main.test.js (1)
functions/upcomingRoom-isTime-checker/src/main.js (1)
  • now (41-41)
functions/sync-users-with-meilisearch/src/main.test.js (1)
functions/sync-users-with-meilisearch/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/sync-stories-with-meilisearch/src/utils.js (4)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/sync-all-documents-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-users-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/match-maker/src/main.test.js (1)
functions/match-maker/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/send-otp/src/utils.js (4)
functions/match-maker/src/utils.js (2)
  • throwIfMissing (3-13)
  • missing (4-4)
functions/sync-all-documents-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-stories-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-users-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-all-documents-with-meilisearch/src/main.js (2)
functions/sync-stories-with-meilisearch/src/main.js (1)
  • meilisearch (11-14)
functions/sync-users-with-meilisearch/src/main.js (1)
  • meilisearch (11-14)
functions/sync-stories-with-meilisearch/src/main.test.js (1)
functions/sync-stories-with-meilisearch/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/database-cleaner/src/appwrite.js (1)
functions/database-cleaner/src/utils.js (1)
  • getExpiryDate (13-18)
functions/send-otp/src/appwrite.js (1)
functions/send-otp/src/main.js (3)
  • require (1-1)
  • AppwriteService (2-2)
  • otp (21-21)
functions/create-room/src/appwrite.js (1)
functions/create-room/src/appwrite.test.js (1)
  • AppwriteService (19-19)
functions/verify-email/src/main.js (2)
functions/verify-email/src/main.test.js (1)
  • require (2-2)
functions/verify-email/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/sync-all-documents-with-meilisearch/src/main.test.js (1)
functions/sync-all-documents-with-meilisearch/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • require (3-3)
functions/sync-all-documents-with-meilisearch/src/utils.js (4)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/sync-stories-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-users-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-stories-with-meilisearch/src/main.js (3)
functions/sync-all-documents-with-meilisearch/src/main.js (4)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • meilisearch (18-21)
functions/sync-users-with-meilisearch/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • meilisearch (11-14)
functions/sync-stories-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/join-room/src/main.js (2)
functions/join-room/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/join-room/src/livekit.js (3)
  • accessToken (4-10)
  • generateToken (3-26)
  • generateToken (3-26)
🔇 Additional comments (19)
functions/start-friend-call/src/main.js (1)

1-2: LGTM: CommonJS conversion.

The conversion from ES modules to CommonJS is correct and aligns with the broader PR changes.

functions/upcomingRoom-Message-Notification/src/main.test.js (5)

1-31: Well-structured mocking setup.

The mocking strategy appropriately isolates external dependencies (Firebase Admin, Firebase Messaging, and Appwrite) and uses virtual mocking for the service account file. The chained method setup for the Appwrite Client correctly mirrors the actual API.


35-57: Solid test setup with proper isolation.

The beforeEach hook correctly resets mocks and initializes environment variables, ensuring test isolation. The request/response/log/error stubs follow Jest best practices.


59-85: Comprehensive happy path test.

This test effectively validates token aggregation from both subscription documents and creator tokens, verifies the correct multicast payload structure, and confirms the success response. The assertions clearly document expected behavior.


87-102: Good failure scenario coverage.

This test verifies that partial Firebase failures are handled gracefully, with the function still returning success while logging the failure. This demonstrates resilient error handling.


104-117: Edge case handled correctly.

The test confirms that the function handles missing tokens gracefully by sending an empty tokens array without throwing errors.

functions/upcomingRoom-Message-Notification/package.json (1)

13-15: I need to verify one more detail about the @jest/globals package version:

Jest and @jest/globals versions are valid.

@jest/globals version 30.2.0 is the latest version published 2 months ago, matching the Jest version already confirmed in the previous search. Both packages are stable releases and compatible with each other.

functions/upcomingRoom-isTime-checker/src/main.test.js (1)

1-17: Mock setup looks good.

The Appwrite SDK mocking correctly captures the builder pattern for Client and provides test doubles for Databases methods.

functions/sync-all-documents-with-meilisearch/src/utils.js (1)

20-20: The review comment is incorrect—getExpiryDate does not exist in this file.

The utils.js file contains only the throwIfMissing function, which is the only function being exported. The module.exports statement is correct and complete. There is no getExpiryDate function to export. The original review appears to be based on inaccurate information.

Likely an incorrect or invalid review comment.

functions/create-room/src/livekit.js (1)

4-41: LivekitService constructor/token/room creation look correct

RoomServiceClient initialization, token grant configuration, and createRoom wrapper are all consistent and straightforward; no issues spotted.

functions/create-room/package.json (1)

8-20: Jest wiring looks good; double‑check version and ESM/CJS config

Adding "test": "jest" and Jest devDependencies makes sense here. Given this package is "type": "module" while several source/test files use require/module.exports, please confirm your Jest config (likely at repo root) runs these tests in a compatible mode and that the specified Jest versions resolve correctly in your environment.

functions/create-room/src/appwrite.js (1)

1-27: AppwriteService logic matches tests; confirm consumer import style

Client setup, Databases wiring, and createRoom correctly pass MASTER_DATABASE_ID, ROOMS_COLLECTION_ID, a unique ID, and the room payload, returning the document $id. Since this file now uses require/module.exports, please ensure all callers (e.g., main.js) also use a compatible CommonJS import pattern so nothing breaks at runtime.

functions/sync-all-documents-with-meilisearch/src/main.test.js (1)

1-73: MeiliSearch sync tests are well‑structured and cover key paths

Mocks for node-appwrite and MeiliSearch are set up cleanly, pagination vs empty‑result behavior is exercised, and mocks/env are reset per test. This gives good confidence in the sync flow.

functions/send-otp/src/appwrite.js (1)

1-26: AppwriteService OTP write flow looks correct

Client initialization, Databases wiring, and createOtpDocument’s use of VERIFICATION_DATABASE_ID, OTP_COLLECTION_ID, and the { otp, date } payload all look good, and the CommonJS export matches how it’s consumed.

functions/send-otp/src/mail.js (1)

1-25: MailService CommonJS migration looks correct.

The nodemailer transporter setup and sendMail behavior are unchanged, and the switch to require/module.exports is consistent with the rest of the PR.

functions/verify-email/src/main.test.js (1)

1-67: Email verification tests cover key success and failure paths well.

The mocks for node-appwrite and throwIfMissing are set up correctly, and the tests validate both the happy path and an error from updateEmailVerification, including status code and error logging. This gives good confidence in the function’s behavior.

functions/verify-otp/src/main.test.js (1)

1-95: OTP verification tests thoroughly exercise success and both error paths.

The Databases mocks and assertions on getDocument/createDocument calls plus 200/500 responses look correct and comprehensive. This suite gives strong coverage over the OTP verification function’s behavior.

functions/sync-stories-with-meilisearch/src/main.js (1)

1-16: CommonJS conversion and MeiliSearch client setup look correct

Imports, env validation with throwIfMissing, and MeiliSearch client/index initialization are consistent with the patterns used elsewhere and should work as expected.

If you want extra safety, you can quickly confirm MeiliSearch client construction against the versioned docs you’re using.

functions/database-cleaner/src/appwrite.js (1)

5-16: Client and Databases initialization pattern looks good

The constructor’s use of Client with default endpoint fallback, project ID, and API key, followed by new Databases(client), is consistent and should integrate cleanly with other Appwrite-based functions.

If desired, double-check the endpoint/project env names against your deployment configuration.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

♻️ Duplicate comments (17)
functions/start-friend-call/src/main.js (1)

23-34: Critical: Error handling issue remains unresolved from previous review.

The function still returns success even when notification sending fails. The error is caught and logged (lines 28-30), but the caller receives { message: "Notification sent" } regardless of the actual outcome, which is misleading.

Additionally, mixing await with .then().catch() is not idiomatic. Use try/catch for clearer error handling and proper error propagation.

Apply this diff to fix the error handling:

-  await getMessaging(app)
-    .send(message)
-    .then((response) => {
-      log("Notifications were sent successfully, " + JSON.stringify(response));
-    })
-    .catch((error) => {
-      log("Error sending message:", error);
-    });
-
-  return res.json({
-    message: "Notification sent",
-  });
+  try {
+    const response = await getMessaging(app).send(message);
+    log("Notifications were sent successfully, " + JSON.stringify(response));
+    return res.json({
+      message: "Notification sent",
+    });
+  } catch (err) {
+    log("Error sending message: " + JSON.stringify(err));
+    return res.status(500).json({
+      message: "Failed to send notification",
+      error: err.message,
+    });
+  }
functions/verify-email/package.json (2)

12-19: Move Jest packages to devDependencies (test-only).

jest and @jest/globals are only needed at test time; keeping them in dependencies bloats the deployed function bundle and may slow cold starts. Move them to devDependencies alongside prettier.

Suggested change:

  "dependencies": {
-    "@jest/globals": "^30.2.0",
-    "jest": "^30.2.0",
-    "node-appwrite": "^17.0.0"
+    "node-appwrite": "^17.0.0"
  },
  "devDependencies": {
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0",
     "prettier": "^3.0.0"
  }

6-6: Fix CommonJS/ESM mismatch: remove "type": "module" or convert code to ESM.

src/main.js now uses CommonJS (require, module.exports), but "type": "module" forces Node to treat .js files as ES modules, where require/module.exports are not available. This will break the function at runtime. Either remove the "type" field (so it defaults to CommonJS) or convert the source back to ESM syntax.

Recommended quick fix:

-  "type": "module",
functions/sync-all-documents-with-meilisearch/package.json (1)

6-14: Scope Jest to devDependencies and re-verify module type vs source syntax.

  • "@jest/globals" and "jest" are listed under dependencies, so they’ll ship with the function bundle even though they’re only needed for tests. For consistency with other functions and to keep production lighter, they should live under devDependencies.
  • package.json still declares "type": "module". A previous review noted that this package’s src files were using CommonJS (require / module.exports). If that’s still true, this config will cause runtime issues.

I’d suggest:

  • Moving Jest packages into devDependencies.
  • Re-checking the module system used in functions/sync-all-documents-with-meilisearch/src/** and aligning either by dropping "type": "module" for full CommonJS, or by converting all sources to ESM.

Example diff for dependency scoping:

   "scripts": {
     "test" : "jest",
     "format": "prettier --write ."
   },
   "keywords": [],
   "dependencies": {
-    "@jest/globals": "^30.2.0",
-    "jest": "^30.2.0",
     "meilisearch": "^0.40.0",
     "node-appwrite": "^14.1.0"
   },
   "devDependencies": {
-    "prettier": "^3.2.5"
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0",
+    "prettier": "^3.2.5"
   }

You can re-run a quick check like:

#!/bin/bash
rg -n "module\\.exports|= require\\(" functions/sync-all-documents-with-meilisearch/src/
rg -n "^import " functions/sync-all-documents-with-meilisearch/src/
rg -n "^export " functions/sync-all-documents-with-meilisearch/src/

to confirm what module syntax is in use now.

functions/database-cleaner/package.json (1)

6-14: Align module system and move Jest to devDependencies.

Two things to tighten up here:

  1. Jest in dependencies:
    "@jest/globals" and "jest" are test-only and should live under devDependencies to keep the deployed bundle lean.

  2. ESM vs CommonJS mismatch (re-raising):
    package.json declares "type": "module", but an earlier review found functions/database-cleaner/src mixing import/export with require()/module.exports. Unless all sources have now been unified to pure ESM, this will break at runtime.

Suggested change for dependency scoping:

   "scripts": {
       "test" : "jest",
       "start": "node src/main.js",
       "format": "prettier --write ."
   },
   "dependencies": {
-        "@jest/globals": "^30.2.0",
-        "jest": "^30.2.0",
         "node-appwrite": "^17.0.0"
   },
   "devDependencies": {
+        "@jest/globals": "^30.2.0",
+        "jest": "^30.2.0",
         "prettier": "^3.0.0"
   }

And please re-validate the module system choice (full ESM vs full CommonJS) for functions/database-cleaner/src/** and update "type" or source files accordingly.

You can reuse the previous check:

#!/bin/bash
rg -n "module\\.exports|= require\\(" functions/database-cleaner/src/
rg -n "^import " functions/database-cleaner/src/
rg -n "^export " functions/database-cleaner/src/

Also applies to: 20-20

functions/sync-users-with-meilisearch/package.json (1)

6-14: Move Jest to devDependencies and confirm module type matches source code.

  • "@jest/globals" and "jest" are currently in dependencies, so they’ll be installed in production even though they’re only used for tests.
  • package.json still declares "type": "module", while an earlier review showed functions/sync-users-with-meilisearch/src using CommonJS. If that hasn’t changed, this combo will cause runtime failures.

Suggested dependency adjustment:

  "dependencies": {
-    "@jest/globals": "^30.2.0",
-    "jest": "^30.2.0",
     "meilisearch": "^0.40.0"
  },
  "devDependencies": {
-    "prettier": "^3.2.5"
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0",
+    "prettier": "^3.2.5"
  }

And please re-check that "type": "module" matches the actual module syntax in src/**; either drop it and go full CommonJS, or convert all sources to ESM.

You can confirm with:

#!/bin/bash
rg -n "module\\.exports|= require\\(" functions/sync-users-with-meilisearch/src/
rg -n "^import " functions/sync-users-with-meilisearch/src/
rg -n "^export " functions/sync-users-with-meilisearch/src/

Also applies to: 20-20

functions/delete-room/src/main.js (1)

60-73: Await participant deletions instead of using forEach with an async callback.

participantColRef.documents.forEach(async (participant) => { ... }) fires deletes without awaiting them. The handler can return (and the function environment can tear down) before all participant documents are removed, leaving orphaned records.

Use a for...of loop or Promise.all so all deletions complete before proceeding:

-    log(participantColRef);
-    participantColRef.documents.forEach(async (participant) => {
-      await databases.deleteDocument(
-        process.env.MASTER_DATABASE_ID,
-        process.env.PARTICIPANTS_COLLECTION_ID,
-        participant.$id
-      );
-    });
+    log(participantColRef);
+    for (const participant of participantColRef.documents) {
+      await databases.deleteDocument(
+        process.env.MASTER_DATABASE_ID,
+        process.env.PARTICIPANTS_COLLECTION_ID,
+        participant.$id
+      );
+    }
functions/start-friend-call/package.json (1)

6-7: Add Jest (and related) as devDependencies for this package.

The "test": "jest" script now points to Jest, but the package.json doesn’t declare Jest or @jest/globals anywhere. Running npm test for this function will fail.

To align with other functions in this PR, add them under devDependencies, e.g.:

  "license": "ISC",
+ "devDependencies": {
+   "jest": "^30.2.0",
+   "@jest/globals": "^30.2.0"
+ },
  "dependencies": {
    "firebase-admin": "^11.11.0"
  }
}

After adding them, run tests for this package to confirm jest is resolved correctly.

functions/match-maker/package.json (2)

6-6: Module type mismatch flagged in previous review.

This issue was already identified in a previous review. The "type": "module" declaration conflicts with CommonJS usage in source files.


13-14: Jest dependencies placement flagged in previous review.

This issue was already identified in a previous review. Test dependencies should be in devDependencies, not dependencies.

functions/send-otp/package.json (2)

6-6: Module type mismatch flagged in previous review.

This issue was already identified in a previous review. The "type": "module" declaration conflicts with CommonJS usage in source files.


13-14: Jest dependencies placement flagged in previous review.

This issue was already identified in a previous review. Test dependencies should be in devDependencies, not dependencies.

functions/sync-stories-with-meilisearch/package.json (2)

6-6: Module type mismatch flagged in previous review.

This issue was already identified in a previous review. The "type": "module" declaration conflicts with CommonJS usage in source files.


13-14: Jest dependencies placement flagged in previous review.

This issue was already identified in a previous review. Test dependencies should be in devDependencies, not dependencies.

functions/join-room/package.json (2)

6-6: Module type mismatch flagged in previous review.

This issue was already identified in a previous review. The "type": "module" declaration conflicts with the module syntax used in source files.


13-14: Jest dependencies placement flagged in previous review.

This issue was already identified in a previous review. Test dependencies should be in devDependencies, not dependencies.

functions/match-maker/src/main.js (1)

37-88: Fix out-of-bounds access: iterate over documents.length, not total

The pairing loop still uses requestDocsRef.total as the upper bound even though the query applies Query.limit(25). total is the total number of matching documents across all pages, while requestDocsRef.documents contains only the current page. When total > documents.length, indices ≥ documents.length will be undefined, leading to runtime errors when you access .uid, .$id, or .userName.

Update the loop to iterate only over the returned page (or use a for...of) and reference the current document via a local variable:

-  for (let index = 0; index < requestDocsRef.total; index++) {
-    try {
-      const newPairDoc = await db.createDocument(
+  for (const candidate of requestDocsRef.documents) {
+    try {
+      const newPairDoc = await db.createDocument(
         process.env.DATABASE_ID,
         process.env.ACTIVE_PAIRS_COLLECTION_ID,
         ID.unique(),
         {
           uid1: newRequestDoc.uid,
-          uid2: requestDocsRef.documents[index].uid,
+          uid2: candidate.uid,
           userDocId1: newRequestDocId,
-          userDocId2: requestDocsRef.documents[index].$id,
+          userDocId2: candidate.$id,
           ...(newRequestDoc.isAnonymous
             ? {}
             : {
                 userName1: newRequestDoc.userName,
-                userName2: requestDocsRef.documents[index].userName,
+                userName2: candidate.userName,
               }),
         }
       );
...
-      await db.deleteDocument(
-        process.env.DATABASE_ID,
-        process.env.REQUESTS_COLLECTION_ID,
-        requestDocsRef.documents[index].$id
-      );
+      await db.deleteDocument(
+        process.env.DATABASE_ID,
+        process.env.REQUESTS_COLLECTION_ID,
+        candidate.$id
+      );

This preserves behavior while avoiding undefined accesses when there are more matches than the page size.

🧹 Nitpick comments (22)
init.sh (1)

8-9: Remove trailing whitespace and unnecessary blank line for consistency.

Line 8 contains a trailing space after the quoted string, and line 9 introduces an extra blank line within the case statement. While these changes don't affect functionality, removing the trailing whitespace and consolidating blank lines would improve consistency with the rest of the codebase and prevent linter warnings.

Apply this diff to clean up the formatting:

-    *)          OS_TYPE="UNKNOWN:$OS" 
-    
+    *)          OS_TYPE="UNKNOWN:$OS"
functions/upcomingRoom-isTime-checker/package.json (1)

12-17: Move Jest dependencies to devDependencies.

Jest and @jest/globals are test-only dependencies and should be in devDependencies rather than dependencies. This prevents them from being bundled in production deployments, reducing the serverless function's deployment size.

Apply this diff to move test dependencies:

   "license": "ISC",
   "dependencies": {
-    "@jest/globals": "^30.2.0",
     "firebase-admin": "^11.11.0",
-    "jest": "^30.2.0",
     "node-appwrite": "^17.0.0"
+  },
+  "devDependencies": {
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0"
   }
functions/verify-email/src/main.test.js (1)

1-67: Nice coverage; consider asserting env validation via throwIfMissing.

The tests cleanly cover both success and error paths. Since ./utils.js is mocked, you can also assert that throwIfMissing is called with the expected env keys, tightening the contract around configuration:

expect(throwIfMissing).toHaveBeenCalledWith(process.env, ["APPWRITE_API_KEY"]);

This would fit naturally in the success test after calling fn(...).

functions/verify-email/src/main.js (1)

4-23: Tighten env validation and handle config errors via the same error path.

Right now only APPWRITE_API_KEY is validated, and throwIfMissing runs outside the try, so missing config throws before you hit the catch/res.json path.

Consider:

  • Validating all required env vars (at least APPWRITE_API_KEY and APPWRITE_FUNCTION_PROJECT_ID; keep APPWRITE_ENDPOINT optional since you already default it).
  • Moving the env check and Client construction into the try so configuration issues are reported via the same { message, 500 } response as SDK errors.

For example:

-module.exports = async ({ req, res, log, error }) => {
-  throwIfMissing(process.env, ["APPWRITE_API_KEY"]);
-
-  const client = new Client()
-    .setEndpoint(
-      process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1"
-    )
-    .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
-    .setKey(process.env.APPWRITE_API_KEY);
-
-  try {
+module.exports = async ({ req, res, log, error }) => {
+  try {
+    throwIfMissing(process.env, [
+      "APPWRITE_API_KEY",
+      "APPWRITE_FUNCTION_PROJECT_ID",
+    ]);
+
+    const client = new Client()
+      .setEndpoint(
+        process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1"
+      )
+      .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
+      .setKey(process.env.APPWRITE_API_KEY);
+
     log(req.body);
     const { userID } = JSON.parse(req.body);
     await new Users(client).updateEmailVerification(userID, true);
+    return res.json({ message: "null" });
   } catch (e) {
     error(String(e));
     return res.json({ message: String(e) }, 500);
   }
-
-  return res.json({ message: "null" });
 };
functions/verify-otp/package.json (1)

6-18: Treat Jest as a devDependency, not a runtime dependency.

In this function, Jest and @jest/globals are only needed for tests, but they’re in dependencies. That will pull them into production installs unnecessarily.

Consider scoping them to devDependencies:

  "scripts": {
    "test": "jest",
    "start": "node src/main.js",
    "format": "prettier --write ."
  },
- "dependencies": {
-    "@jest/globals": "^30.2.0",
-    "jest": "^30.2.0",
-    "node-appwrite": "^17.0.0"
-  },
-  "devDependencies": {
-    "prettier": "^3.0.0"
-  }
+ "dependencies": {
+    "node-appwrite": "^17.0.0"
+  },
+ "devDependencies": {
+    "@jest/globals": "^30.2.0",
+    "jest": "^30.2.0",
+    "prettier": "^3.0.0"
+  }

Please also ensure your root Jest config (if any) picks up this function’s tests as expected after the change.

functions/sync-users-with-meilisearch/src/utils.js (1)

2-20: Clarify doc vs implementation behavior for throwIfMissing

The helper also treats falsy values as “missing” (not just absent keys), which is appropriate for env validation but slightly broader than the JSDoc wording. Consider updating the comment to say “missing or falsy” and, longer term, centralizing this shared helper since the same implementation appears in multiple functions.

functions/create-room/src/appwrite.test.js (1)

1-39: AppwriteService test is valid; make env usage more explicit

The mock shape for Client, Databases, and ID.unique matches AppwriteService and the assertion on createDocument + returned $id looks good. To better catch mis-wiring of env vars, consider setting MASTER_DATABASE_ID and ROOMS_COLLECTION_ID to known values in beforeEach and asserting against those literals instead of relying on their current implicit process.env values.

functions/sync-users-with-meilisearch/src/main.js (1)

1-57: Handler logic looks correct; optionally handle unsupported events explicitly

Env validation, Meilisearch client/index setup, and the delete/create/update branches (plus the new 400 responses for missing documentId and missing trigger) all look consistent and correct. For non‑delete/create/update events, the function currently falls through and returns undefined; you may want to log and return an explicit 400/204 in that case for more predictable behavior.

functions/join-room/src/main.test.js (1)

1-85: Good coverage; align throwIfMissing mock with real util behavior

The tests nicely cover the success path, missing body field, and token-generation error. Because the mocked throwIfMissing reimplements both the validation and the exact error message, these tests will still pass even if the real utils.js implementation or its message wording changes. Consider delegating to jest.requireActual('./utils.js').throwIfMissing in the mock, or asserting that the error response message contains "roomName" rather than matching the full string.

functions/create-room/src/appwrite.js (1)

1-27: CommonJS export and Appwrite wiring look consistent

Requiring Client, Databases, and ID and exporting AppwriteService via module.exports lines up with the new test file, and createRoom correctly forwards the database/collection IDs and ID.unique(). As a small robustness improvement, you could add an env validation step in the constructor (e.g., via a shared throwIfMissing) so misconfigured env vars fail fast.

functions/sync-stories-with-meilisearch/src/main.test.js (1)

1-80: Strong Meilisearch sync coverage; optionally add a 500-path test

The suite cleanly exercises create/update/delete flows and the two 400 cases (no document ID, no trigger), with the Meilisearch client nicely mocked. If you want to exercise the error handling in main.js, you could add a test where addDocuments or deleteDocument throws and assert that the handler logs the error and returns a 500 with the expected message.

functions/sync-users-with-meilisearch/src/main.test.js (1)

1-80: User Meilisearch sync tests mirror stories suite well

These tests mirror the stories sync coverage for the users collection and exercise the key success and 400 cases correctly. As with the stories tests, you might optionally add a case where Meilisearch operations throw to assert the 500-path behavior and logging, but the current coverage is already solid.

functions/send-otp/src/main.test.js (1)

1-39: Happy-path OTP flow is covered; consider adding validation/error tests

This test confirms that the handler calls the mail and Appwrite services with the expected arguments and returns { message: 'mail sent' }, which is good. To harden the function, consider adding tests for (a) missing/invalid request fields and (b) failures from the mail or Appwrite layers, asserting that the catch block logs the error and returns the expected error response.

functions/sync-stories-with-meilisearch/src/utils.js (1)

8-20: Consider consolidating duplicate utility functions.

The throwIfMissing function is duplicated across multiple modules (match-maker, send-otp, sync-all-documents-with-meilisearch, sync-users-with-meilisearch, and this module). This violates the DRY principle and makes maintenance harder.

Consider creating a shared utilities package or module that all functions can import from:

// functions/shared/utils.js
const throwIfMissing = (obj, keys) => {
  const missing = [];
  for (let key of keys) {
    if (!(key in obj) || !obj[key]) {
      missing.push(key);
    }
  }
  if (missing.length > 0) {
    throw new Error(`Missing required fields: ${missing.join(', ')}`);
  }
};

module.exports = { throwIfMissing };

Then import from the shared location:

const { throwIfMissing } = require('../shared/utils.js');
functions/verify-otp/src/main.test.js (1)

54-94: Consider adding tests for OTP mismatch scenario.

The current tests cover success and database error cases, but don't test the scenario where the user-provided OTP doesn't match the stored OTP. This is a common failure case in OTP verification.

Add a test case:

test("fail — OTP mismatch", async () => {
  mockGetDocument.mockResolvedValue({ otp: "1234" }); // Different from userOTP "5555"

  await fn({ req: mockReq, res: mockRes, log: mockLog, error: mockError });

  expect(mockCreateDocument).not.toHaveBeenCalled();
  expect(mockRes.json).toHaveBeenCalledWith(
    { message: "Invalid OTP" },
    400
  );
});
functions/verify-otp/src/main.js (1)

1-55: Handler flow looks solid; consider tightening env and input validation

The CommonJS conversion and overall control flow (env check → fetch OTP doc → write verification doc → respond) look good. Two optional improvements you might consider:

  • Add APPWRITE_FUNCTION_PROJECT_ID (and possibly APPWRITE_ENDPOINT if you don’t always want the default) to the throwIfMissing list so misconfiguration fails fast instead of at the Appwrite call site.
  • If you want clearer client semantics on bad requests, you could use throwIfMissing on the parsed body to return a 4xx when otpID, userOTP, or verify_ID are missing, instead of letting this fall through to a 500 from the Appwrite SDK.

These are non-blocking polish items; current behavior is consistent and testable.

functions/sync-all-documents-with-meilisearch/src/main.js (1)

1-84: Sync logic is correct; consider DRY-ing pagination and expanding env checks

The Appwrite→MeiliSearch sync loops for stories and users are well-structured and use cursor pagination appropriately; the CommonJS migration also looks fine.

Two non-blocking improvements to consider:

  • Env validation: you rely on APPWRITE_ENDPOINT, APPWRITE_FUNCTION_PROJECT_ID, and APPWRITE_API_KEY when building the Appwrite client but only validate the MeiliSearch envs. For consistency with other functions in this PR, you could add those Appwrite vars to the throwIfMissing list so misconfigurations fail fast.
  • Duplication: the stories and users sync blocks are nearly identical. Extracting a small helper (e.g., syncCollection({db, index, databaseId, collectionId, log})) would reduce duplication and keep future changes (like tweaking limits or logging) in one place.

Both are refactors rather than required fixes.

functions/join-room/src/main.js (1)

1-30: Avoid double JSON.parse on req.body

Logic and status codes (400 for invalid/missing fields, 500 for internal errors) look good. You can simplify and avoid parsing twice by parsing once and reusing the payload:

-  try {
-    throwIfMissing(JSON.parse(req.body), ["roomName", "uid"]);
-  } catch (err) {
-    return res.json({ msg: err.message }, 400);
-  }
+  let payload;
+  try {
+    payload = JSON.parse(req.body);
+    throwIfMissing(payload, ["roomName", "uid"]);
+  } catch (err) {
+    return res.json({ msg: err.message }, 400);
+  }
...
-    const { roomName, uid: userId } = JSON.parse(req.body);
+    const { roomName, uid: userId } = payload;

This keeps behavior the same while reducing work and chance of divergence.

functions/sync-stories-with-meilisearch/src/main.js (1)

1-56: Add an explicit response for unsupported Appwrite event types

The CommonJS migration and added guards (env validation, 400 when there’s no Appwrite trigger, 400 when $id is missing) are good improvements.

Within the x-appwrite-trigger branch, if x-appwrite-event doesn’t contain .delete, .create, or .update, the function currently exits the try block without returning a response, which may leave the request hanging depending on the runtime.

Consider adding a final else to return a 4xx or 2xx “no-op” response for unhandled event types, e.g.:

if (eventType.includes('.delete')) {
  ...
} else if (eventType.includes('.create') || eventType.includes('.update')) {
  ...
} else {
  return res.text(`Event type ${eventType} not handled`, 400);
}

This makes behavior explicit and future-proof if new event variants appear.

functions/send-otp/src/main.js (1)

1-36: Solid wiring; consider explicit error status and fuller env validation

The CommonJS conversion and integration with AppwriteService and MailService look good, and the OTP generation + persistence flow is straightforward.

Two small improvements you might want:

  • Return a failure status on exceptions: right now the catch block calls res.json({ message: \Error: ${String(e)}` })` without a status, which likely defaults to 200. To let clients detect failures reliably, you can pass a 5xx:
-    return res.json({ message: `Error: ${String(e)}` });
+    return res.json({ message: `Error: ${String(e)}` }, 500);
  • Env validation parity: if AppwriteService depends on additional env vars (e.g., APPWRITE_FUNCTION_PROJECT_ID, APPWRITE_ENDPOINT), consider adding them to the throwIfMissing list so misconfigurations fail fast.

Both are behavioral tweaks, not blockers.

functions/database-cleaner/src/appwrite.js (2)

49-72: Batch deletion loop for expired active pairs is reasonable; consider small clarity/safety tweaks

The pattern of querying expired pairs with Query.lessThan("$createdAt", getExpiryDate()), deleting the current batch in parallel via Promise.all, and looping until total === 0 is a reasonable approach.

Two optional refinements you might consider:

  • Initialize done to false for readability since its initial value is overwritten inside the loop anyway.
  • Add a defensive iteration cap or an extra break condition (e.g., if activePairDocs.documents.length === 0 while total > 0) to avoid a potential infinite loop if the SDK semantics or query behavior ever change.

74-100: OTP cleanup logic works but may delete non-expired/future records; tighten predicate if needed

The loop structure mirrors cleanActivePairsCollection and looks fine. The main semantic question is the predicate:

const currentDate = new Date().toDateString();
const queries = [Query.notEqual("date", currentDate), Query.limit(100)];

Query.notEqual("date", currentDate) will match all documents whose date field is not today, including any future‑dated OTPs or records without a date exactly equal to toDateString() format. If you ever pre‑create OTPs for future days or store more precise timestamps, you may want a stricter condition (e.g., a < comparison against a normalized cutoff) to avoid over‑deleting.

If upstream guarantees that all non‑today OTPs are safe to remove, this is fine as is; otherwise, consider aligning this logic with the getExpiryDate() helper or switching to a less‑than comparison on a timestamp field.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 795f29b and f71051e.

⛔ Files ignored due to path filters (14)
  • functions/create-room/package-lock.json is excluded by !**/package-lock.json
  • functions/database-cleaner/package-lock.json is excluded by !**/package-lock.json
  • functions/delete-room/package-lock.json is excluded by !**/package-lock.json
  • functions/join-room/package-lock.json is excluded by !**/package-lock.json
  • functions/match-maker/package-lock.json is excluded by !**/package-lock.json
  • functions/send-otp/package-lock.json is excluded by !**/package-lock.json
  • functions/start-friend-call/package-lock.json is excluded by !**/package-lock.json
  • functions/sync-all-documents-with-meilisearch/package-lock.json is excluded by !**/package-lock.json
  • functions/sync-stories-with-meilisearch/package-lock.json is excluded by !**/package-lock.json
  • functions/sync-users-with-meilisearch/package-lock.json is excluded by !**/package-lock.json
  • functions/upcomingRoom-Message-Notification/package-lock.json is excluded by !**/package-lock.json
  • functions/upcomingRoom-isTime-checker/package-lock.json is excluded by !**/package-lock.json
  • functions/verify-email/package-lock.json is excluded by !**/package-lock.json
  • functions/verify-otp/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (50)
  • functions/create-room/package.json (2 hunks)
  • functions/create-room/src/appwrite.js (1 hunks)
  • functions/create-room/src/appwrite.test.js (1 hunks)
  • functions/create-room/src/livekit.js (1 hunks)
  • functions/create-room/src/main.js (1 hunks)
  • functions/database-cleaner/package.json (1 hunks)
  • functions/database-cleaner/src/appwrite.js (1 hunks)
  • functions/database-cleaner/src/appwrite.test.js (1 hunks)
  • functions/database-cleaner/src/utils.js (1 hunks)
  • functions/delete-room/package.json (1 hunks)
  • functions/delete-room/src/main.js (1 hunks)
  • functions/delete-room/src/main.test.js (1 hunks)
  • functions/join-room/package.json (1 hunks)
  • functions/join-room/src/main.js (1 hunks)
  • functions/join-room/src/main.test.js (1 hunks)
  • functions/match-maker/package.json (1 hunks)
  • functions/match-maker/src/main.js (1 hunks)
  • functions/match-maker/src/main.test.js (1 hunks)
  • functions/match-maker/src/utils.js (1 hunks)
  • functions/send-otp/package.json (1 hunks)
  • functions/send-otp/src/appwrite.js (1 hunks)
  • functions/send-otp/src/mail.js (1 hunks)
  • functions/send-otp/src/main.js (1 hunks)
  • functions/send-otp/src/main.test.js (1 hunks)
  • functions/send-otp/src/utils.js (1 hunks)
  • functions/start-friend-call/package.json (1 hunks)
  • functions/start-friend-call/src/main.js (2 hunks)
  • functions/sync-all-documents-with-meilisearch/package.json (1 hunks)
  • functions/sync-all-documents-with-meilisearch/src/main.js (4 hunks)
  • functions/sync-all-documents-with-meilisearch/src/main.test.js (1 hunks)
  • functions/sync-all-documents-with-meilisearch/src/utils.js (2 hunks)
  • functions/sync-stories-with-meilisearch/package.json (1 hunks)
  • functions/sync-stories-with-meilisearch/src/main.js (3 hunks)
  • functions/sync-stories-with-meilisearch/src/main.test.js (1 hunks)
  • functions/sync-stories-with-meilisearch/src/utils.js (2 hunks)
  • functions/sync-users-with-meilisearch/package.json (1 hunks)
  • functions/sync-users-with-meilisearch/src/main.js (3 hunks)
  • functions/sync-users-with-meilisearch/src/main.test.js (1 hunks)
  • functions/sync-users-with-meilisearch/src/utils.js (2 hunks)
  • functions/upcomingRoom-Message-Notification/package.json (1 hunks)
  • functions/upcomingRoom-Message-Notification/src/main.test.js (1 hunks)
  • functions/upcomingRoom-isTime-checker/package.json (1 hunks)
  • functions/upcomingRoom-isTime-checker/src/main.test.js (1 hunks)
  • functions/verify-email/package.json (1 hunks)
  • functions/verify-email/src/main.js (1 hunks)
  • functions/verify-email/src/main.test.js (1 hunks)
  • functions/verify-otp/package.json (1 hunks)
  • functions/verify-otp/src/main.js (1 hunks)
  • functions/verify-otp/src/main.test.js (1 hunks)
  • init.sh (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (29)
functions/create-room/src/appwrite.test.js (1)
functions/create-room/src/appwrite.js (1)
  • require (1-1)
functions/create-room/src/livekit.js (1)
functions/create-room/src/main.js (1)
  • accessToken (50-50)
functions/sync-users-with-meilisearch/src/main.js (1)
functions/sync-users-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-users-with-meilisearch/src/utils.js (4)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/sync-all-documents-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-stories-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/verify-otp/src/main.test.js (1)
functions/verify-otp/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/sync-stories-with-meilisearch/src/utils.js (4)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/sync-all-documents-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-users-with-meilisearch/src/utils.js (1)
  • throwIfMissing (8-18)
functions/sync-users-with-meilisearch/src/main.test.js (1)
functions/sync-users-with-meilisearch/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/database-cleaner/src/appwrite.test.js (1)
functions/database-cleaner/src/appwrite.js (2)
  • require (1-1)
  • require (3-3)
functions/send-otp/src/main.test.js (4)
functions/send-otp/src/main.js (4)
  • sendOtp (5-34)
  • require (1-1)
  • MailService (3-3)
  • AppwriteService (2-2)
functions/send-otp/src/appwrite.js (1)
  • require (1-1)
functions/send-otp/src/mail.js (1)
  • require (1-1)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/upcomingRoom-Message-Notification/src/main.test.js (2)
functions/sync-stories-with-meilisearch/src/main.test.js (1)
  • req (16-16)
functions/sync-users-with-meilisearch/src/main.test.js (1)
  • req (16-16)
functions/sync-all-documents-with-meilisearch/src/main.test.js (1)
functions/sync-all-documents-with-meilisearch/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • require (3-3)
functions/verify-email/src/main.test.js (1)
functions/verify-email/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • JSON (16-16)
functions/match-maker/src/main.test.js (1)
functions/match-maker/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/join-room/src/main.test.js (1)
functions/join-room/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/upcomingRoom-isTime-checker/src/main.test.js (1)
functions/upcomingRoom-isTime-checker/src/main.js (1)
  • now (41-41)
functions/send-otp/src/appwrite.js (3)
functions/send-otp/src/main.js (3)
  • require (1-1)
  • AppwriteService (2-2)
  • otp (21-21)
functions/sync-all-documents-with-meilisearch/src/main.js (4)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • client (11-14)
functions/database-cleaner/src/appwrite.js (2)
  • require (1-1)
  • require (3-3)
functions/sync-stories-with-meilisearch/src/main.test.js (1)
functions/sync-stories-with-meilisearch/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/send-otp/src/main.js (3)
functions/send-otp/src/appwrite.js (1)
  • require (1-1)
functions/send-otp/src/mail.js (1)
  • require (1-1)
functions/send-otp/src/utils.js (1)
  • throwIfMissing (1-11)
functions/verify-email/src/main.js (2)
functions/verify-email/src/main.test.js (1)
  • require (2-2)
functions/verify-email/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/match-maker/src/main.js (1)
functions/match-maker/src/utils.js (1)
  • throwIfMissing (3-13)
functions/create-room/src/appwrite.js (1)
functions/create-room/src/appwrite.test.js (1)
  • AppwriteService (19-19)
functions/send-otp/src/mail.js (1)
functions/send-otp/src/main.js (4)
  • require (1-1)
  • MailService (3-3)
  • recipientEmail (19-19)
  • otp (21-21)
functions/verify-otp/src/main.js (2)
functions/verify-otp/src/main.test.js (1)
  • require (2-2)
functions/verify-otp/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/delete-room/src/main.js (2)
functions/delete-room/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/livekit-webhook/src/main.js (1)
  • appwriteRoomDocId (30-30)
functions/create-room/src/main.js (1)
functions/create-room/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/send-otp/src/utils.js (4)
functions/match-maker/src/utils.js (2)
  • throwIfMissing (3-13)
  • missing (4-4)
functions/sync-all-documents-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-stories-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-users-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/match-maker/src/utils.js (4)
functions/send-otp/src/utils.js (2)
  • throwIfMissing (1-11)
  • missing (2-2)
functions/sync-all-documents-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-stories-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/sync-users-with-meilisearch/src/utils.js (2)
  • throwIfMissing (8-18)
  • missing (9-9)
functions/database-cleaner/src/appwrite.js (1)
functions/database-cleaner/src/utils.js (1)
  • getExpiryDate (13-18)
functions/sync-all-documents-with-meilisearch/src/main.js (3)
functions/match-maker/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/sync-stories-with-meilisearch/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • meilisearch (11-14)
functions/sync-users-with-meilisearch/src/main.js (3)
  • require (1-1)
  • require (2-2)
  • meilisearch (11-14)
🔇 Additional comments (39)
functions/upcomingRoom-Message-Notification/package.json (1)

13-15: > Likely an incorrect or invalid review comment.

functions/upcomingRoom-isTime-checker/package.json (1)

7-7: LGTM!

The test script correctly configures Jest for running unit tests.

functions/upcomingRoom-Message-Notification/src/main.test.js (5)

1-33: LGTM!

The mock setup correctly isolates external dependencies (Firebase Admin, Firebase Messaging, and Appwrite) and uses a virtual mock for the service account configuration file.


35-57: LGTM!

The test setup properly initializes environment variables, resets mocks between tests, and configures request/response stubs.


59-85: LGTM!

This test validates the happy path: token aggregation from both subscription documents and creator tokens, correct payload construction, and successful response handling.


87-102: LGTM!

This test correctly validates the behavior when Firebase returns failures, ensuring the function still responds successfully while logging the failure.


104-117: LGTM!

This test properly covers the edge case of missing tokens, ensuring graceful handling with an empty tokens array.

functions/upcomingRoom-isTime-checker/src/main.test.js (6)

1-19: LGTM!

The mock setup correctly isolates the Appwrite client and database operations for unit testing.


21-34: LGTM!

The test setup properly configures environment variables and resets mocks between tests.


36-60: LGTM!

This test correctly validates the primary logic: updating isTime to true when the scheduled time is within the ±5 minute window and the flag is currently false.


62-80: LGTM!

This test correctly validates idempotency: when isTime is already true, no update should be performed.


82-100: LGTM!

This test properly validates the time boundary logic: rooms scheduled outside the ±5 minute window should not be updated.


102-126: LGTM!

Excellent test coverage of multi-document processing. This validates that only rooms meeting both conditions (within time window AND isTime is false) are updated, while others are correctly skipped.

functions/create-room/src/livekit.js (1)

4-41: LivekitService token and room creation logic look consistent.

Constructor wiring, token generation (grants), and createRoom usage are all straightforward and match expected LiveKit patterns; no issues from this change.

functions/create-room/package.json (1)

6-8: Jest setup is correct; module type is properly aligned with source files.

All source files in functions/create-room/src use ESM syntax consistently (imports/exports), so "type": "module" is the correct configuration and must remain. No CommonJS patterns were found in any of the four source files (main.js, appwrite.js, livekit.js, utils.js). The Jest setup is sound.

functions/create-room/src/main.js (1)

5-62: LGTM! Formatting improvements applied.

The changes are formatting-only with no functional modifications. The logic, error handling, and control flow remain unchanged.

functions/send-otp/src/utils.js (1)

1-13: LGTM! Clean ES module to CommonJS conversion.

The conversion properly maintains the function logic while updating the export pattern to CommonJS. The throwIfMissing utility function behavior is preserved.

functions/send-otp/src/appwrite.js (1)

1-26: ES module to CommonJS conversion is otherwise correct.

The migration from ES module syntax to CommonJS is properly implemented. The class structure, constructor initialization, and createOtpDocument method logic are preserved.

functions/delete-room/package.json (1)

6-6: No action needed. The "type": "module" declaration is correct—the source files (main.js, utils.js) use only ES module syntax with import and export statements. No CommonJS patterns were detected.

functions/sync-all-documents-with-meilisearch/src/main.test.js (3)

3-27: LGTM: Mock setup is well-structured.

The mocks for node-appwrite and meilisearch are properly configured with Jest functions and expose the necessary methods for testing.


32-46: LGTM: Test setup follows best practices.

Environment variables are properly configured and mocks are reset before each test, ensuring test isolation.


48-72: LGTM: Test cases cover key scenarios.

The tests validate:

  • Pagination handling with multiple document pages
  • Empty document handling
  • Proper response status and message

Both test cases correctly assert the expected number of addDocuments calls based on the paginated responses.

functions/match-maker/src/main.test.js (3)

5-34: LGTM: Comprehensive mock setup.

The mocks cover all necessary Appwrite client methods and utilities for testing the pairing functionality.


40-76: LGTM: Test setup aligns with implementation.

The setup correctly mirrors the event structure and database behavior, including the fix for extracting the document ID from the event path.


99-106: LGTM: Error handling test is correct.

The test properly validates that errors during pairing are logged with appropriate messages.

functions/verify-otp/src/main.test.js (3)

4-23: LGTM: Mock setup is well-structured.

The mocks properly isolate the unit under test by mocking external dependencies (node-appwrite and utilities).


28-52: LGTM: Test setup follows best practices.

The beforeEach properly resets mocks and configures all necessary test fixtures and environment variables.


54-69: Verify the success response message.

The test expects { message: "null" } as the success response, which is unusual. Typically, success messages would be something like "OTP verified successfully" or "Success".

Please verify that returning { message: "null" } is the intended behavior in the actual implementation. If this is a placeholder or incorrect, update both the implementation and test to use a meaningful success message.

functions/database-cleaner/src/appwrite.test.js (4)

1-22: LGTM: Well-designed mock setup.

The mocks properly simulate the Appwrite SDK behavior with appropriate return values for Query methods.


34-58: LGTM: Comprehensive test coverage for doesRoomExist.

The test validates all three scenarios: success, expected 404 (returns false), and unexpected errors (rethrows). This is thorough and well-structured.


60-90: LGTM: Correct test logic for participant cleanup.

The test properly validates that only participants associated with non-existent rooms are deleted, and uses process.nextTick to handle asynchronous deletion operations.


92-125: LGTM: Thorough test for OTP cleanup with pagination.

The test correctly validates pagination handling and ensures all old OTPs are deleted with the correct database/collection IDs.

functions/database-cleaner/src/utils.js (1)

13-18: LGTM: getExpiryDate logic is correct.

The function properly calculates the expiry date based on the retention period with a sensible default of 1 day.

functions/match-maker/src/utils.js (1)

3-15: LGTM: Conversion to CommonJS is correct.

The export mechanism has been properly updated to use module.exports. Note that this function is duplicated across multiple modules (see comment on functions/sync-stories-with-meilisearch/src/utils.js for consolidation recommendation).

functions/sync-all-documents-with-meilisearch/src/utils.js (1)

8-20: CommonJS migration for throwIfMissing looks correct

The helper implementation is sound and exporting it via module.exports = { throwIfMissing } is consistent with the rest of the PR. No functional issues spotted here.

functions/send-otp/src/mail.js (1)

1-25: MailService CommonJS conversion and behavior look correct

The nodemailer transporter setup and sendMail implementation are unchanged in behavior, and exporting the class via module.exports = MailService aligns with how it’s consumed in send-otp/src/main.js. Error propagation is handled by the caller’s try/catch, so this class can stay focused on sending. No issues from this change.

functions/database-cleaner/src/appwrite.js (3)

1-16: CommonJS imports and Appwrite client initialization look consistent

The switch to require("node-appwrite") and wiring of Client/Databases in the constructor using env-driven endpoint/project/key looks straightforward and matches typical Appwrite usage. Just ensure tests cover behavior when required env vars are missing or malformed.


18-30: Confirm error shape (err.code) from Appwrite SDK in doesRoomExist

Logic of treating any successful getDocument as “room exists” and only swallowing 404s while rethrowing other errors is sound. Please double‑check that Databases.getDocument in node-appwrite@17 throws errors with an HTTP status on err.code (and not e.g. err.statusCode or nested under err.response), otherwise the 404 check may never trigger.


103-103: CommonJS export of AppwriteService is straightforward

Exporting the class via module.exports = AppwriteService; aligns with the new CommonJS import style and should make requiring this module from tests and other functions simple.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f73f9ee and 02c950b.

📒 Files selected for processing (1)
  • functions/database-cleaner/src/appwrite.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
functions/database-cleaner/src/appwrite.js (1)
functions/database-cleaner/src/utils.js (1)
  • getExpiryDate (13-18)
🔇 Additional comments (3)
functions/database-cleaner/src/appwrite.js (3)

15-27: LGTM!

The existence check pattern with 404-specific handling is correct and clean. Rethrowing non-404 errors ensures unexpected issues are properly surfaced.


57-83: LGTM!

The batch deletion logic is correct. Since all fetched documents are deleted unconditionally, the pagination advances naturally and terminates when no matching documents remain.


85-92: Date format concern is not applicable—both creation and cleanup use the same format.

OTP documents are created with date field using new Date().toDateString() (in functions/send-otp/src/main.js line 26), and clearOldOTPs() queries using the same format. The comparison will work correctly.

However, using toDateString() is locale-dependent and fragile. Consider using a consistent, locale-independent format like ISO date (YYYY-MM-DD):

   async clearOldOTPs() {
     let done = false;
-    const currentDate = new Date().toDateString();
+    const currentDate = new Date().toISOString().split('T')[0];

This requires updating the OTP creation in functions/send-otp/src/main.js to match:

-    const currentDate = new Date().toDateString();
+    const currentDate = new Date().toISOString().split('T')[0];

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
functions/database-cleaner/src/appwrite.js (1)

31-57: Remove the duplicate method definition.

This first definition of cleanParticipantsCollection is dead code—JavaScript will only use the second definition at lines 59-94. Additionally, this implementation contains the infinite loop bug (using participantDocs.total at line 55) that was flagged in previous reviews.

Delete this entire method:

-  async cleanParticipantsCollection() {
-    let done = false;
-    const queries = [Query.limit(25)];
-
-    while (!done) {
-      const participantDocs = await this.databases.listDocuments(
-        process.env.MASTER_DATABASE_ID,
-        process.env.PARTICIPANTS_COLLECTION_ID,
-        queries
-      );
-
-      await Promise.all(
-        participantDocs.documents.map(async (participantDoc) => {
-          const exists = await this.doesRoomExist(participantDoc.roomId);
-          if (!exists) {
-            await this.databases.deleteDocument(
-              process.env.MASTER_DATABASE_ID,
-              process.env.PARTICIPANTS_COLLECTION_ID,
-              participantDoc.$id
-            );
-          }
-        })
-      );
-
-      done = participantDocs.total === 0;
-    }
-  }
-
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02c950b and 487c8f6.

📒 Files selected for processing (1)
  • functions/database-cleaner/src/appwrite.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
functions/database-cleaner/src/appwrite.js (4)
functions/send-otp/src/appwrite.js (1)
  • require (1-1)
functions/send-otp/src/main.js (4)
  • require (1-1)
  • AppwriteService (2-2)
  • currentDate (24-24)
  • otp (21-21)
functions/database-cleaner/src/appwrite.test.js (1)
  • AppwriteService (24-24)
functions/create-room/src/appwrite.test.js (1)
  • AppwriteService (19-19)
🪛 Biome (2.1.2)
functions/database-cleaner/src/appwrite.js

[error] 59-94: Duplicate class member name "cleanParticipantsCollection"

(lint/suspicious/noDuplicateClassMembers)

🔇 Additional comments (5)
functions/database-cleaner/src/appwrite.js (5)

1-2: LGTM: CommonJS conversion is correct.

The imports are properly converted to CommonJS and align with the module system changes across the codebase.


5-15: LGTM: Constructor implementation is sound.

The Appwrite client initialization is correct with proper environment variable handling and fallback endpoint.


17-29: LGTM: Proper error handling for room existence check.

The method correctly distinguishes between non-existent rooms (404) and actual errors, which is essential for the cleanup logic.


59-94: LGTM: Cursor-based pagination correctly addresses the infinite loop issue.

This implementation properly fixes the issue raised in previous reviews by using Query.cursorAfter to advance through results and checking documents.length === 0 to terminate. The cursor-based approach ensures progress even when no deletions occur in a batch.


124-124: LGTM: CommonJS export is correct.

The module export aligns with the CommonJS conversion and matches how the module is imported in test files.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
functions/database-cleaner/src/appwrite.js (1)

2-2: Remove unused import.

getExpiryDate is imported but never used in this file.

-const { getExpiryDate } = require("./utils.js");
functions/match-maker/src/main.test.js (3)

23-28: Consider returning mock query values from Query methods.

The Query mock methods (notEqual, equal, orderAsc, limit) currently return undefined. If the actual code chains these calls or passes their return values to listDocuments, the tests might not accurately reflect real SDK behavior.

Apply this diff to make Query methods return mock values:

     Query: {
-      notEqual: jest.fn(),
-      equal: jest.fn(),
-      orderAsc: jest.fn(),
-      limit: jest.fn(),
+      notEqual: jest.fn((field, value) => `notEqual("${field}", "${value}")`),
+      equal: jest.fn((field, value) => `equal("${field}", "${value}")`),
+      orderAsc: jest.fn((field) => `orderAsc("${field}")`),
+      limit: jest.fn((count) => `limit(${count})`),
     },

108-128: Enhance test by verifying deleteDocument arguments.

While the test confirms deleteDocument is called twice, it doesn't verify that the correct document IDs are being deleted. This could miss bugs where the wrong documents are deleted.

Apply this diff to add argument verification:

     expect(mockDb.deleteDocument).toHaveBeenCalledTimes(2);
+    expect(mockDb.deleteDocument).toHaveBeenNthCalledWith(
+      1,
+      "db",
+      "requests",
+      "123"
+    );
+    expect(mockDb.deleteDocument).toHaveBeenNthCalledWith(
+      2,
+      "db",
+      "requests",
+      "456"
+    );
     expect(res.json).toHaveBeenCalledWith(

78-128: Consider adding tests for additional edge cases.

The current test suite covers basic scenarios but could benefit from testing more edge cases that could occur in production.

Consider adding test cases for:

  1. Database operation failures:

    • getDocument throws an error
    • listDocuments throws an error
    • deleteDocument throws an error
  2. Malformed input:

    • Missing or malformed x-appwrite-event header
    • Event header with unexpected format
  3. Environment validation:

    • Missing required environment variables

Example test:

it("should handle getDocument failure gracefully", async () => {
  mockDb.getDocument.mockRejectedValueOnce(new Error("Database error"));
  
  await sendPairRequest({ req, res, log, error });
  
  expect(error).toHaveBeenCalledWith(expect.stringContaining("Database error"));
});

it("should handle missing event header", async () => {
  req.headers = {};
  
  await sendPairRequest({ req, res, log, error });
  
  expect(error).toHaveBeenCalled();
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 487c8f6 and cb0e81c.

📒 Files selected for processing (3)
  • functions/database-cleaner/src/appwrite.js (1 hunks)
  • functions/match-maker/src/main.test.js (1 hunks)
  • functions/send-otp/src/appwrite.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
functions/send-otp/src/appwrite.js (2)
functions/database-cleaner/src/appwrite.js (2)
  • require (1-1)
  • require (2-2)
functions/sync-all-documents-with-meilisearch/src/main.js (4)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • client (11-14)
functions/match-maker/src/main.test.js (1)
functions/match-maker/src/main.js (2)
  • require (1-1)
  • require (2-2)
functions/database-cleaner/src/appwrite.js (1)
functions/database-cleaner/src/appwrite.test.js (1)
  • AppwriteService (24-24)
🪛 Biome (2.1.2)
functions/database-cleaner/src/appwrite.js

[error] 59-94: Duplicate class member name "cleanParticipantsCollection"

(lint/suspicious/noDuplicateClassMembers)

🔇 Additional comments (8)
functions/send-otp/src/appwrite.js (2)

4-10: Verify: Missing API key configuration.

The client is not configured with .setKey(process.env.APPWRITE_API_KEY), unlike other services in this PR (e.g., functions/database-cleaner/src/appwrite.js and functions/sync-all-documents-with-meilisearch/src/main.js). Without an API key, the client may lack permissions to create OTP documents.

If this function runs within an Appwrite function context where permissions are inherited, this is fine. Otherwise, add the API key:

     const client = new Client()
       .setEndpoint(process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1")
-      .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID);
+      .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
+      .setKey(process.env.APPWRITE_API_KEY);

12-25: LGTM!

The createOtpDocument method logic is correct, and the CommonJS export is consistent with the rest of the codebase changes.

functions/database-cleaner/src/appwrite.js (5)

5-15: LGTM!

The constructor properly initializes the Appwrite client with endpoint, project ID, and API key.


17-29: LGTM!

The doesRoomExist method correctly returns true on success, false on 404, and rethrows other errors. This is proper error handling.


59-94: Cursor-based pagination correctly implemented.

This implementation properly uses cursor-based pagination to avoid the infinite loop issue. The loop breaks when no documents are returned, and the cursor advances after each batch.


96-128: LGTM!

The clearOldOTPs method correctly implements cursor-based pagination, addressing the infinite loop issue from past reviews.


131-131: LGTM!

CommonJS export is consistent with the rest of the codebase changes.

functions/match-maker/src/main.test.js (1)

99-106: The test file functions/match-maker/src/main.test.js does not exist in the repository. No test files are present in the functions/match-maker directory, and the specific test case "should handle errors during pairing" cannot be found anywhere in the codebase. The review comment references non-existent code and therefore cannot be verified or acted upon.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
functions/match-maker/src/main.test.js (1)

32-34: The throwIfMissing mock still bypasses validation logic.

This was flagged in a previous review as a major issue. Mocking throwIfMissing as a no-op means validation paths remain untested. Consider either using the real implementation or adding dedicated tests that verify throwIfMissing behavior.

🧹 Nitpick comments (2)
functions/match-maker/src/main.test.js (1)

97-104: Consider more flexible error assertions.

The test expects exact error messages in a specific order. If the implementation adds additional logging or changes message formats, this test will become brittle.

Consider using more flexible matchers:

-    expect(error).toHaveBeenCalledWith("That request is already paired: ");
-    expect(error).toHaveBeenCalledWith("Error: Pairing failed");
+    expect(error).toHaveBeenCalledWith(expect.stringContaining("already paired"));
+    expect(error).toHaveBeenCalledWith(expect.stringContaining("Pairing failed"));
functions/database-cleaner/src/appwrite.js (1)

31-66: Cursor-based participant cleanup correctly implements pagination best practices

The implementation uses Query.limit(25) and Query.cursorAfter(cursor) as documented for node-appwrite v17; this approach correctly avoids the earlier infinite-loop risk and guarantees termination. The batch size of 25 matches the SDK's default page size. Optionally extract the limit value to a named constant (e.g., PARTICIPANT_BATCH_SIZE) for improved readability and easier future tuning.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb0e81c and a66a2bf.

📒 Files selected for processing (3)
  • functions/database-cleaner/package.json (1 hunks)
  • functions/database-cleaner/src/appwrite.js (1 hunks)
  • functions/match-maker/src/main.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • functions/database-cleaner/package.json
🔇 Additional comments (5)
functions/match-maker/src/main.test.js (2)

77-95: Well-structured edge case tests.

Both test cases properly validate early-exit scenarios with appropriate mock configurations and clear assertions.


106-126: Excellent addition of the successful pairing test.

This test properly validates the happy path that was missing in previous reviews. It correctly verifies pair creation, request cleanup, and response formatting.

functions/database-cleaner/src/appwrite.js (3)

5-15: Constructor wiring looks solid

Client/env wiring and Databases initialization are straightforward and appropriate for this service class; no changes needed.


17-29: 404 handling and error propagation are appropriate

doesRoomExist cleanly maps “found vs 404” to true/false and lets all other errors bubble, which is a good balance between resilience and debuggability.


68-100: The code shown in this review comment does not match the actual implementation in the repository. The actual clearOldOTPs() method (lines 76-110) uses a do...while loop with done = oneDayOTPs.total === 0, not the cursor-based pagination shown in the review snippet. The review's claim that this "avoids the earlier total-based loop bug" is inaccurate—the current implementation still relies on total.

Regarding date formatting: both OTP creation (send-otp/src/main.js line 26) and cleanup (database-cleaner/src/appwrite.js line 80) consistently use new Date().toDateString(), so the format alignment is already correct and does not require a refactor.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (7)
functions/match-maker/src/main.test.js (5)

23-28: Consider returning mock query objects from Query methods.

The Query mock methods currently return undefined by default. While this works for the current tests, it doesn't match Appwrite's actual behavior where Query methods return query objects. This could mask issues if the implementation code chains or uses these query objects.

Consider updating the mock:

     Query: {
-      notEqual: jest.fn(),
-      equal: jest.fn(),
-      orderAsc: jest.fn(),
-      limit: jest.fn(),
+      notEqual: jest.fn(() => ({ method: 'notEqual' })),
+      equal: jest.fn(() => ({ method: 'equal' })),
+      orderAsc: jest.fn(() => ({ method: 'orderAsc' })),
+      limit: jest.fn(() => ({ method: 'limit' })),
     },

43-78: Add explicit mock cleanup for better test isolation.

While Jest typically handles mock cleanup automatically, adding explicit jest.clearAllMocks() in an afterEach block ensures complete test isolation and is considered best practice.

Add after the beforeEach block:

afterEach(() => {
  jest.clearAllMocks();
});

100-107: Verify the response sent during error handling.

The test checks that errors are logged but doesn't verify what response is sent to the client. Error paths should also validate the user-facing response to ensure proper error handling end-to-end.

Add an assertion to verify the response:

   expect(error).toHaveBeenCalledWith("That request is already paired: ");
   expect(error).toHaveBeenCalledWith("Error: Pairing failed");
+  expect(res.json).toHaveBeenCalledWith(
+    expect.objectContaining({
+      ok: false,
+    })
+  );

109-129: Consider verifying log calls for better observability testing.

The test validates the core pairing logic but doesn't verify that success messages are logged. Testing log calls ensures proper observability in production.

Add assertion for log calls:

expect(log).toHaveBeenCalledWith(expect.stringContaining("Pair created"));

39-130: Add test coverage for event header edge cases.

The tests don't cover scenarios where the event header is missing, malformed, or contains an invalid document ID pattern. These edge cases could cause runtime errors or unexpected behavior in the document ID extraction logic.

Add test cases like:

it("should handle missing event header gracefully", async () => {
  req.headers = {};
  
  await sendPairRequest({ req, res, log, error });
  
  expect(error).toHaveBeenCalled();
  expect(res.json).toHaveBeenCalledWith(
    expect.objectContaining({ ok: false })
  );
});

it("should handle malformed event header gracefully", async () => {
  req.headers["x-appwrite-event"] = "invalid-format";
  
  await sendPairRequest({ req, res, log, error });
  
  expect(error).toHaveBeenCalled();
});
functions/delete-room/src/main.test.js (1)

77-89: Verify error logging and expand test coverage.

The test should verify that the error() function was called when an exception occurs.

Add this assertion:

     expect(res.json).toHaveBeenCalledWith(
       { msg: "Room deletion failed" },
       500
     );
+    expect(error).toHaveBeenCalledWith(expect.stringContaining("DB ERROR"));

Additionally, the test suite is missing critical test cases:

  • Success path: Room deleted successfully (verify deleteDocument, listDocuments, participant deletions, LiveKit deleteRoom are called)
  • 403 case: Non-admin user attempting deletion
  • Participant cleanup: Verify all participants are deleted

Would you like me to help generate these additional test cases?

functions/delete-room/src/main.js (1)

59-71: Consider more targeted logging for participant cleanup.

Logging the entire participantColRef object (line 64) could expose sensitive data or create large log entries. Consider logging just the count or document IDs.

     const participantColRef = await databases.listDocuments(
       process.env.MASTER_DATABASE_ID,
       process.env.PARTICIPANTS_COLLECTION_ID,
       [Query.equal("roomId", [appwriteRoomDocId])]
     );
-    log(participantColRef);
+    log(`Deleting ${participantColRef.documents.length} participant(s)`);
     for (const participant of participantColRef.documents) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a66a2bf and f4a9326.

📒 Files selected for processing (6)
  • functions/delete-room/package.json (1 hunks)
  • functions/delete-room/src/main.js (1 hunks)
  • functions/delete-room/src/main.test.js (1 hunks)
  • functions/join-room/package.json (1 hunks)
  • functions/match-maker/src/main.test.js (1 hunks)
  • functions/sync-users-with-meilisearch/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • functions/delete-room/package.json
  • functions/join-room/package.json
🧰 Additional context used
🧬 Code graph analysis (2)
functions/delete-room/src/main.test.js (1)
functions/delete-room/src/main.js (4)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • JSON (39-39)
functions/delete-room/src/main.js (2)
functions/delete-room/src/utils.js (2)
  • throwIfMissing (1-11)
  • throwIfMissing (1-11)
functions/livekit-webhook/src/main.js (1)
  • appwriteRoomDocId (30-30)
🔇 Additional comments (4)
functions/sync-users-with-meilisearch/package.json (1)

1-19: ✅ Package configuration is correct.

The previous critical issues have been properly resolved:

  • "type": "module" declaration has been removed, aligning CommonJS source files with Node.js module resolution.
  • Jest and @jest/globals are correctly placed in devDependencies, keeping test tooling separate from production dependencies.

The test script is properly configured to run Jest, and dependency versions are appropriate.

functions/match-maker/src/main.test.js (3)

32-37: LGTM!

Good use of the actual throwIfMissing implementation. This ensures validation logic is properly tested rather than mocked away.


80-88: LGTM!

Clean test that verifies the early-return path for non-random requests.


90-98: LGTM!

Correctly validates the behavior when no matching requests are found in the queue.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
functions/database-cleaner/src/utils.js (1)

1-21: throwIfMissing semantics still diverge from other modules (0/false handling).

This implementation now only treats null, undefined, and "" as missing while other modules still use a simple falsy check (if (!obj[key])), which treats 0, false, and NaN as missing as well. That means the same logical “required field” may be accepted here (e.g., 0 or false) but rejected elsewhere, which is fragile and hard to reason about.

I recommend choosing one contract and applying it everywhere, ideally via a shared helper:

  • If the app-wide rule is “any falsy value is missing”, update this to use if (!value) instead of the explicit null/undefined/empty-string checks.
  • If the stricter rule here is desired (0/false allowed), update the other throwIfMissing implementations to match, and centralize the function in a common utility so it can’t drift again.
functions/delete-room/src/main.js (1)

53-65: Critical: Participant deletion may be incomplete due to pagination.

The listDocuments call at line 53 doesn't handle pagination, meaning it will only retrieve the first page of results (typically 25 documents by default in Appwrite). If a room has more participants than the page limit, the remaining participants won't be deleted, leaving orphaned records in the database.

Consider implementing cursor-based pagination to ensure all participants are deleted:

-    const participantColRef = await databases.listDocuments(
-      process.env.MASTER_DATABASE_ID,
-      process.env.PARTICIPANTS_COLLECTION_ID,
-      [Query.equal("roomId", [appwriteRoomDocId])]
-    );
-    log(participantColRef);
-    for (const participant of participantColRef.documents) {
-      await databases.deleteDocument(
-        process.env.MASTER_DATABASE_ID,
-        process.env.PARTICIPANTS_COLLECTION_ID,
-        participant.$id
-      );
+    let cursor = null;
+    let hasMore = true;
+    
+    while (hasMore) {
+      const queries = [Query.equal("roomId", [appwriteRoomDocId]), Query.limit(100)];
+      if (cursor) {
+        queries.push(Query.cursorAfter(cursor));
+      }
+      
+      const participantColRef = await databases.listDocuments(
+        process.env.MASTER_DATABASE_ID,
+        process.env.PARTICIPANTS_COLLECTION_ID,
+        queries
+      );
+      
+      for (const participant of participantColRef.documents) {
+        await databases.deleteDocument(
+          process.env.MASTER_DATABASE_ID,
+          process.env.PARTICIPANTS_COLLECTION_ID,
+          participant.$id
+        );
+      }
+      
+      hasMore = participantColRef.documents.length === 100;
+      if (hasMore) {
+        cursor = participantColRef.documents[participantColRef.documents.length - 1].$id;
+      }
     }
🧹 Nitpick comments (1)
functions/match-maker/src/main.js (1)

21-25: Add defensive validation for the extracted document ID.

If the x-appwrite-event header is missing or malformed, triggerEvent.split(".") will throw or produce an empty/invalid document ID, causing a runtime error on the subsequent getDocument call.

   log(req.headers);
   const triggerEvent = req.headers["x-appwrite-event"];
+  if (!triggerEvent) {
+    return res.json({ message: "Missing trigger event header" });
+  }
   const parts = triggerEvent.split(".");
   const newRequestDocId = parts[parts.length - 1];
+  if (!newRequestDocId) {
+    return res.json({ message: "Could not extract document ID from event" });
+  }
   log(newRequestDocId);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4a9326 and 24046c3.

📒 Files selected for processing (13)
  • functions/database-cleaner/src/utils.js (1 hunks)
  • functions/delete-room/package.json (1 hunks)
  • functions/delete-room/src/main.js (1 hunks)
  • functions/delete-room/src/main.test.js (1 hunks)
  • functions/join-room/package.json (1 hunks)
  • functions/match-maker/package.json (1 hunks)
  • functions/match-maker/src/main.js (1 hunks)
  • functions/send-otp/package.json (1 hunks)
  • functions/start-friend-call/src/main.js (2 hunks)
  • functions/sync-all-documents-with-meilisearch/package.json (2 hunks)
  • functions/sync-stories-with-meilisearch/package.json (1 hunks)
  • functions/verify-email/package.json (1 hunks)
  • functions/verify-otp/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • functions/delete-room/src/main.test.js
  • functions/send-otp/package.json
  • functions/match-maker/package.json
  • functions/join-room/package.json
  • functions/sync-all-documents-with-meilisearch/package.json
🧰 Additional context used
🪛 Biome (2.1.2)
functions/start-friend-call/src/main.js

[error] 40-42: This code will never be reached ...

... because either this statement ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

🔇 Additional comments (18)
functions/sync-stories-with-meilisearch/package.json (1)

1-19: ✅ Package.json configuration is correct.

The previous critical issues have been properly addressed:

  • "type": "module" has been removed, allowing CommonJS modules to function correctly
  • Jest and @jest/globals are now correctly placed in devDependencies instead of dependencies
  • Test script is properly configured with "jest"

The file is well-structured and ready for use.

functions/verify-otp/package.json (1)

1-19: Critical issue from previous review has been resolved. The "type": "module" declaration has been removed, allowing the CommonJS require() / module.exports pattern to work correctly. Jest ^30.2.0 is the current stable version and properly configured with the test script and devDependencies.

functions/start-friend-call/src/main.js (2)

1-2: LGTM: CommonJS conversion.

The conversion from ES modules to CommonJS is consistent with the broader changes in this PR.


23-38: Excellent fix: Proper error handling implemented.

The try/catch block correctly addresses the previous critical issue where failures were masked as successes. Both paths now return appropriate status codes and messages.

functions/verify-email/package.json (1)

6-18: Jest dependencies correctly configured.

The package.json properly places Jest and @jest/globals in devDependencies, the test script is correctly configured, and the removal of "type": "module" aligns with CommonJS source files. Jest 30.2.0 is a valid release (September 28, 2025).

functions/delete-room/package.json (1)

1-20: Configuration looks good!

The package.json is properly configured with Jest dependencies correctly placed in devDependencies (addressing the previous review feedback). The removal of "type": "module" aligns with the CommonJS migration in the main.js file.

functions/delete-room/src/main.js (6)

1-3: CommonJS migration looks correct.

The imports have been properly converted from ES module syntax to CommonJS require statements, aligning with the package.json change that removed "type": "module".


5-14: Environment validation is thorough.

The module export has been correctly converted to CommonJS, and all necessary environment variables are validated upfront, preventing runtime errors later.


16-29: Client initialization is correct.

Both Appwrite and LiveKit clients are properly configured with the required credentials and endpoints.


31-45: Request handling and authorization look good.

The duplicate JSON parsing issue has been resolved, and the admin validation properly prevents unauthorized users from deleting rooms.


59-65: Good fix for awaiting participant deletions!

The change from forEach with async callbacks to a for...of loop properly ensures all participant deletions are awaited before proceeding.


66-71: LiveKit cleanup and error handling are appropriate.

The function properly cleans up the LiveKit room and provides centralized error handling with logging. The generic error message in the response is acceptable for security purposes.

functions/match-maker/src/main.js (6)

1-2: LGTM!

CommonJS imports are correctly structured for the Appwrite SDK components and local utility.


4-10: LGTM!

Fail-fast validation of required environment variables ensures clear error messages if configuration is missing.


12-19: LGTM!

Appwrite client initialization follows the standard pattern with sensible endpoint fallback.


27-37: LGTM with a consideration.

The document fetch and isRandom check correctly implements early-exit logic. Note that if the document was already deleted (e.g., by a concurrent pairing), getDocument will throw an unhandled exception. Depending on your function runtime's error handling, you may want to wrap this in a try-catch for cleaner error messages.


39-52: LGTM!

The query correctly filters candidates and the previous issue with iterating over total instead of documents.length has been addressed. The FIFO ordering via orderAsc("$createdAt") ensures fair matching.


97-99: LGTM!

Appropriate fallback response when no pairing occurs.

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