Skip to content

Add settled_by_admin field in order.ts model and improve getDetailedOrder output.#747

Open
Luquitasjeffrey wants to merge 10 commits intomainfrom
add-settled_by_admin-field
Open

Add settled_by_admin field in order.ts model and improve getDetailedOrder output.#747
Luquitasjeffrey wants to merge 10 commits intomainfrom
add-settled_by_admin-field

Conversation

@Luquitasjeffrey
Copy link
Collaborator

@Luquitasjeffrey Luquitasjeffrey commented Feb 16, 2026

This PR removes the 'COMPLETED_BY_ADMIN' status in an Order and adds a new boolean field 'settled_by_admin' that, as the name suggests, denotes if an order was indeed completed or not by an admin.
Also, now when an admin uses the checkorder command, it will display if the order was completed by an admin

Summary by CodeRabbit

  • New Features

    • Order details now show whether an order was settled by an admin, in all supported languages.
  • Updates

    • Settlement and order-finalization behavior refined: admin-settled orders are tracked explicitly, affecting payment release timing and block checks.
  • Chores

    • Existing orders migrated to align with the new admin-settled tracking.

…d_by_admin' field denoting that, and modified the 'getDetailedOrder' function output to show if an order was completed by an admin or not
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Replaced the COMPLETED_BY_ADMIN status with a boolean settled_by_admin, tightened status checks to only allow PAID_HOLD_INVOICE in relevant flows, set settled_by_admin = true when admin settles an order, added a migration to convert old COMPLETED_BY_ADMIN orders, and surfaced settledByAdmin in order detail translations across locales.

Changes

Cohort / File(s) Summary
Command & flow logic
bot/commands.ts, bot/modules/block/commands.ts, bot/start.ts
Removed COMPLETED_BY_ADMIN from status checks and queries; narrowed filters to PAID_HOLD_INVOICE; when admin settles an order set order.settled_by_admin = true and removed setting status to COMPLETED_BY_ADMIN.
Data model
models/order.ts
Added settled_by_admin boolean to schema and IOrder; removed 'COMPLETED_BY_ADMIN' from the status enum.
Migration script
scripts/migrate_completed_by_admin_orders.ts
New script updating orders with status: 'COMPLETED_BY_ADMIN' to set status: 'SUCCESS' and settled_by_admin: true.
Order detail rendering
util/index.ts
Added settledByAdmin derived from order.settled_by_admin (translated yes/no) to the order_detail payload.
Localization
locales/{de,en,es,fa,fr,it,ko,pt,ru,uk}.yaml
Inserted localized Settled by admin: ${settledByAdmin} line into order_detail templates.
Tests
tests/** (multiple)
Added/updated tests covering admin settle flow, block command queries, migration script invocation, and getDetailedOrder output for settledByAdmin.
Project config / misc
tsconfig.json, package.json
Formatting and include/exclude updates; added scripts/**/* to TS include.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • grunch
  • Catrya

Poem

🐰 I hopped through code where statuses changed,
A tiny flag now marks the end,
Admin settles, no enum strained,
Locales whisper — "Yes" or "No" — my friend. 🥕

🚥 Pre-merge checks | ✅ 3
✅ 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 describes the primary changes: adding a settled_by_admin field to the order model and improving the getDetailedOrder output to display this information across multiple locales.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-settled_by_admin-field

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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@bot/start.ts`:
- Around line 547-551: The code currently sets order.settled_by_admin = true and
calls await order.save() before calling settleHoldInvoice, which can leave the
flag persisted if settleHoldInvoice throws; change the flow so you first call
await settleHoldInvoice({ secret: order.secret }) and only after it succeeds set
order.settled_by_admin = true and call await order.save(), or wrap both calls in
a try/catch and roll back (reset) order.settled_by_admin and avoid saving if
settleHoldInvoice fails; refer to the order object, its save() method, and the
settleHoldInvoice function when updating the logic.

In `@locales/fa.yaml`:
- Around line 183-184: Replace the English label "Settled by admin:
${settledByAdmin}" in the Persian locale with a Persian translation to keep
localization consistent; update the entry in locales/fa.yaml (the string
"Settled by admin: ${settledByAdmin}") to a Persian equivalent such as "تسویه
شده توسط ادمین: ${settledByAdmin}" ensuring the placeholder ${settledByAdmin}
remains unchanged.

@mostronatorcoder
Copy link
Contributor

🚨 Request Changes - Critical Issues Found

I need to request changes before this PR can be merged. There are critical security and data integrity issues that must be addressed:

1. BREAKING CHANGE without Data Migration (CRITICAL)

Removing "COMPLETED_BY_ADMIN" from the status enum is a breaking change that will break existing orders in production.

Problem: Orders currently in "COMPLETED_BY_ADMIN" status will be in an invalid state after deployment.

Required: Add a database migration script that:

  • Finds all orders with status: "COMPLETED_BY_ADMIN"
  • Updates them to status: "PAID_HOLD_INVOICE"
  • Sets settled_by_admin: true

Without this migration, existing orders will be unreachable and potentially cause runtime errors.

2. Data Integrity Issue in bot/start.ts (SECURITY)

Current (WRONG):

order.settled_by_admin = true;
await order.save();
await settleHoldInvoice({ secret: order.secret });

Problem: If settleHoldInvoice() fails, the order is already marked as settled by admin but the hold invoice was NOT settled. This creates an inconsistent state where the database says "settled by admin" but the Lightning payment did not complete.

Required (CORRECT):

try {
  await settleHoldInvoice({ secret: order.secret });
  order.settled_by_admin = true;
  await order.save();
} catch (error) {
  // Handle error - do NOT mark as settled_by_admin
  throw error;
}

The settlement must succeed BEFORE marking the order as settled by admin.

3. Schema Issue - Optional Field

In models/order.ts:

settled_by_admin?: boolean;  // Optional - BAD

Problem: Optional fields can be undefined, leading to inconsistent data.

Required:

settled_by_admin: { type: Boolean, default: false, required: true }

4. Missing CodeRabbit Review

This PR has been pending CodeRabbit review. I should have waited for that automated analysis before reviewing. Please ensure all automated checks pass.


Summary of Required Changes

  1. Add database migration for existing "COMPLETED_BY_ADMIN" orders
  2. Fix order of operations in bot/start.ts - settle first, then mark
  3. Make field required with default false in schema
  4. Verify all existing orders with "COMPLETED_BY_ADMIN" are handled

This is financial software handling real Bitcoin. We cannot merge breaking changes without proper migration and data integrity guarantees.

Status: 🔴 Request Changes - DO NOT MERGE until issues are resolved.

@mostronatorcoder
Copy link
Contributor

Hey @Luquitasjeffrey, friendly reminder — the issues from my previous review are still unresolved. The last two commits are upstream merges, no fixes.

Here's a quick recap:

1. 🔴 Missing database migration (CRITICAL)

You're removing COMPLETED_BY_ADMIN from the status enum, but existing orders in production with that status will become invalid. You need a migration script that:

// Find orders with the old status and migrate them
db.orders.updateMany(
  { status: "COMPLETED_BY_ADMIN" },
  { $set: { status: "PAID_HOLD_INVOICE", settled_by_admin: true } }
)

2. 🔴 Order of operations in bot/start.ts (DATA INTEGRITY)

Still persisting settled_by_admin = true BEFORE calling settleHoldInvoice(). If the LN call fails, the DB says "settled by admin" but the invoice was never settled.

Current (wrong):

order.settled_by_admin = true;
await order.save();                              // persisted
await settleHoldInvoice({ secret: order.secret }); // can fail

I understand your concern about race conditions (from your earlier reply to CodeRabbit), but the current approach trades one race condition for a worse one — a false positive where the DB says the invoice was settled when it wasn't. If you're worried about async event handlers, you could set the flag in memory first and save after settlement succeeds:

await settleHoldInvoice({ secret: order.secret });
order.settled_by_admin = true;
await order.save();

3. 🟡 TypeScript interface vs Mongoose schema mismatch

The Mongoose schema has { type: Boolean, default: false } (good), but the TypeScript interface still declares settled_by_admin?: boolean (optional). For consistency, consider adding required: true to the schema or at minimum document why it's optional.


This PR handles real Bitcoin orders — we need these data integrity issues resolved before merging. Let me know if you have questions or need help with any of these.

…correcting order of operations in bot/start.ts due to a potential failure while settling the hold invoice
Copy link
Contributor

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

♻️ Duplicate comments (1)
bot/start.ts (1)

547-551: ⚠️ Potential issue | 🔴 Critical

Set settled_by_admin before settleHoldInvoice to avoid a race.

At Line 548, settlement can trigger async order-update handlers before Line 549–550 runs, which can leave inconsistent state for admin-settled orders.

Suggested fix
       if (order.secret) {
-        await settleHoldInvoice({ secret: order.secret });
         order.settled_by_admin = true;
         await order.save();
+        await settleHoldInvoice({ secret: order.secret });
       }

Based on learnings: In bot/start.ts /settleorder command, order.settled_by_admin = true must be set and saved before calling settleHoldInvoice({ secret: order.secret }) to prevent a race condition.

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

In `@bot/start.ts` around lines 547 - 551, The order settlement code can race
because settleHoldInvoice(...) may trigger async handlers that read order state
before we mark it as admin-settled; update the sequence so you set
order.settled_by_admin = true and await order.save() before calling
settleHoldInvoice({ secret: order.secret }) (refer to the settleorder command,
order.settled_by_admin, order.save, and settleHoldInvoice symbols) so the
persisted flag exists for any downstream handlers.
🧹 Nitpick comments (1)
scripts/migrate_completed_by_admin_orders.ts (1)

30-35: Prefer finally for deterministic connection cleanup.

Line 33–35 exits on error without guaranteed close. Consider using finally to always close the Mongo connection and set process.exitCode instead of immediate process.exit(...).

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

In `@scripts/migrate_completed_by_admin_orders.ts` around lines 30 - 35, The
migration currently calls mongoose.connection.close() only on success and uses
process.exit(...) inside the catch which can leave the DB open; refactor the
try/catch to a try/catch/finally where you always await
mongoose.connection.close() in the finally block (handling a potentially
undefined connection) and replace direct calls to
process.exit(0)/process.exit(1) with setting process.exitCode = 0 or 1 so
shutdown can occur deterministically; update references in this file to remove
immediate process.exit usage and ensure logger.error(`Migration failed:
${error}`) remains in the catch before setting process.exitCode.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@bot/start.ts`:
- Around line 547-551: The order settlement code can race because
settleHoldInvoice(...) may trigger async handlers that read order state before
we mark it as admin-settled; update the sequence so you set
order.settled_by_admin = true and await order.save() before calling
settleHoldInvoice({ secret: order.secret }) (refer to the settleorder command,
order.settled_by_admin, order.save, and settleHoldInvoice symbols) so the
persisted flag exists for any downstream handlers.

---

Nitpick comments:
In `@scripts/migrate_completed_by_admin_orders.ts`:
- Around line 30-35: The migration currently calls mongoose.connection.close()
only on success and uses process.exit(...) inside the catch which can leave the
DB open; refactor the try/catch to a try/catch/finally where you always await
mongoose.connection.close() in the finally block (handling a potentially
undefined connection) and replace direct calls to
process.exit(0)/process.exit(1) with setting process.exitCode = 0 or 1 so
shutdown can occur deterministically; update references in this file to remove
immediate process.exit usage and ensure logger.error(`Migration failed:
${error}`) remains in the catch before setting process.exitCode.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: aae5b76a-95fe-43af-8241-9fac1e0f31ae

📥 Commits

Reviewing files that changed from the base of the PR and between a07edbe and 7e9a117.

📒 Files selected for processing (3)
  • bot/start.ts
  • scripts/migrate_completed_by_admin_orders.ts
  • tsconfig.json
✅ Files skipped from review due to trivial changes (1)
  • tsconfig.json

@Luquitasjeffrey
Copy link
Collaborator Author

Hey @Luquitasjeffrey, friendly reminder — the issues from my previous review are still unresolved. The last two commits are upstream merges, no fixes.

Here's a quick recap:

1. 🔴 Missing database migration (CRITICAL)

You're removing COMPLETED_BY_ADMIN from the status enum, but existing orders in production with that status will become invalid. You need a migration script that:

// Find orders with the old status and migrate them
db.orders.updateMany(
  { status: "COMPLETED_BY_ADMIN" },
  { $set: { status: "PAID_HOLD_INVOICE", settled_by_admin: true } }
)

2. 🔴 Order of operations in bot/start.ts (DATA INTEGRITY)

Still persisting settled_by_admin = true BEFORE calling settleHoldInvoice(). If the LN call fails, the DB says "settled by admin" but the invoice was never settled.

Current (wrong):

order.settled_by_admin = true;
await order.save();                              // persisted
await settleHoldInvoice({ secret: order.secret }); // can fail

I understand your concern about race conditions (from your earlier reply to CodeRabbit), but the current approach trades one race condition for a worse one — a false positive where the DB says the invoice was settled when it wasn't. If you're worried about async event handlers, you could set the flag in memory first and save after settlement succeeds:

await settleHoldInvoice({ secret: order.secret });
order.settled_by_admin = true;
await order.save();

3. 🟡 TypeScript interface vs Mongoose schema mismatch

The Mongoose schema has { type: Boolean, default: false } (good), but the TypeScript interface still declares settled_by_admin?: boolean (optional). For consistency, consider adding required: true to the schema or at minimum document why it's optional.

This PR handles real Bitcoin orders — we need these data integrity issues resolved before merging. Let me know if you have questions or need help with any of these.

I addressed your review.

  1. I added the script scripts/migrate_completed_by_admin_orders.ts, to migrate those orders it use the following commands:
    npx tsc node dist/scripts/migrate_completed_by_admin_orders.js

  2. Fixed the order of operations, you were correct, settleHoldInvoice now is called before setting the settled_by_admin field to true

  3. I dismissed your third comment about the "inconsistency" between the Order interface and the model because in mongoose all fields can be nullable unless explicitly required, so your 3rd comment is dismissed.

Copy link
Contributor

@mostronatorcoder mostronatorcoder bot left a comment

Choose a reason for hiding this comment

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

Summary

This PR removes the COMPLETED_BY_ADMIN status and replaces it with a settled_by_admin boolean field. The design is architecturally correct and includes a critical fix for operation ordering.


✅ Strengths

1. Correct architectural design

  • Removing COMPLETED_BY_ADMIN status and replacing it with a settled_by_admin boolean is the right approach
  • Status should reflect workflow state, not who completed it
  • Boolean flag captures metadata without polluting the state machine

2. Migration script included

  • scripts/migrate_completed_by_admin_orders.ts migrates existing orders
  • Transforms status: COMPLETED_BY_ADMINstatus: PAID_HOLD_INVOICE + settled_by_admin: true
  • Safe logic using updateMany

3. Complete status cleanup

  • All COMPLETED_BY_ADMIN references removed except:
    • Migration script (correct, necessary)
    • Locale files (message translations, OK)

4. Critical fix: Operation ordering in bot/start.ts

Before:

if (order.secret) await settleHoldInvoice({ secret: order.secret });

order.status = 'COMPLETED_BY_ADMIN';  // ❌ Status changes even if settle fails
await order.save();

Problem: If settleHoldInvoice() throws, order ends up in COMPLETED_BY_ADMIN status but the invoice is NOT settled in LND → critical inconsistency.

Now:

if (order.secret) {
  await settleHoldInvoice({ secret: order.secret });  // ✅ If fails, exception exits here
  order.settled_by_admin = true;                      // ✅ Only executes if settle OK
  await order.save();
}

Analysis:CORRECT

  • If settleHoldInvoice() fails → exception exits, nothing modified
  • If settle OK → marks settled_by_admin = true and saves
  • Status remains PAID_HOLD_INVOICE (correct state after settle)

This is a real bug fix.

5. Display in checkorder

  • Now shows "Settled by admin: Yes/No" in /checkorder output
  • Useful for audit and debugging

⚠️ Issues Found

🔴 CRITICAL: Missing tests

The PR modifies:

  • Critical business logic (settle flow in bot/start.ts)
  • Data model (models/order.ts)
  • Order queries (bot/commands.ts, bot/modules/block/commands.ts)

But:

git diff cb1838e..7e9a117 -- tests/
(no output)

No new or updated tests.

Test cases that SHOULD exist:

1. Migration script

it('should migrate COMPLETED_BY_ADMIN orders to PAID_HOLD_INVOICE + settled_by_admin', async () => {
  // Given: order with status COMPLETED_BY_ADMIN
  // When: run migration
  // Then: status = PAID_HOLD_INVOICE && settled_by_admin = true
});

2. Admin settle flow (bot/start.ts)

it('should set settled_by_admin when admin settles with secret', async () => {
  // Given: order with secret
  // When: admin executes settle
  // Then: settled_by_admin = true
});

it('should not modify order if settleHoldInvoice fails', async () => {
  // Given: order with invalid secret
  // When: admin executes settle (LND fails)
  // Then: order status/flags unchanged
});

3. Display in checkorder

it('should show "Settled by admin: Yes" when settled_by_admin = true', async () => {
  // Given: order with settled_by_admin = true
  // When: /checkorder
  // Then: output contains "Settled by admin: Yes"
});

4. Block queries

it('should exclude settled orders from pending count', async () => {
  // Given: orders with settled_by_admin = true
  // When: user tries /block
  // Then: those orders do NOT count as pending
});

Impact: Without tests, any regression in this logic will go unnoticed until production.


🟡 Minor: Unnecessary tsconfig.json change

+    "scripts/**/*"

Adding scripts/ to tsconfig include is fine for compiling the migration script, BUT:

  • Migration script runs once manually
  • Doesn't need to be in production build
  • Better approach: Run migration with ts-node directly, without modifying tsconfig

Not critical, but unnecessary noise in the PR.


🟡 Minor: Unrelated formatting changes

-    "lib":["ES2021", "DOM"],
+    "lib": [
+      "ES2021",
+      "DOM"
+    ],

This is just formatting. Better to do it in a separate "chore: prettier" commit to avoid contaminating the functional diff.


🔍 Logic Validation

Query in /setinvoice

Before:

const where = {
  $and: [
    { buyer_id: user._id },
    {
      $or: [
        { status: 'PAID_HOLD_INVOICE' },
        { status: 'COMPLETED_BY_ADMIN' },  // ❌ No longer exists
      ],
    },
  ],
};

Now:

const where = {
  buyer_id: user._id,
  status: 'PAID_HOLD_INVOICE',  // ✅ Simplified
};

Analysis:CORRECT

  • Previously searched for orders in PAID_HOLD_INVOICE OR COMPLETED_BY_ADMIN
  • Now only searches PAID_HOLD_INVOICE (the only valid status after migration)
  • Simpler and correct query

📊 Findings Summary

Severity Issue Impact
🔴 Critical No tests Risk of regression in settle logic and queries
🟡 Minor tsconfig include scripts/ Noise in build config
🟡 Minor Formatting changes Contaminated diff

✅ Recommendations

BEFORE MERGE:

  • Add tests for migration script
  • Add tests for settle flow with settled_by_admin
  • Add tests for checkorder/block queries
  • Verify in staging that migration script works correctly

NICE TO HAVE:

  • Separate formatting changes in separate commit
  • Evaluate if tsconfig change is necessary (alternative: ts-node for migration)

POST-MERGE:

  • Add grep in CI to prevent accidental use of COMPLETED_BY_ADMIN in the future
  • Document in README or CHANGELOG the model change (breaking change for external integrations reading the DB)

🎯 Verdict

Status: ⚠️ REQUEST CHANGES

Reasons:

  1. Lack of tests is critical for settle logic and migration
  2. The code itself is correct and improves the system (fixes operation ordering bug)
  3. The architectural design is solid

Required action:

  • Add minimum tests for migration and settle flow
  • Validate migration in staging before merge

Once tests are added:APPROVE


Additional Notes

I verified all COMPLETED_BY_ADMIN references have been removed from queries:

git grep -n "COMPLETED_BY_ADMIN" | grep -v "locales/"
# Only found in migration script (correct)

I also checked for potential broken queries:

git grep -n "status.*:" bot/ util/ models/ | grep -i "success\|paid_hold\|canceled\|expired"
# All queries updated correctly

The PR is well-structured and the logic is sound. The only blocker is the missing test coverage for critical business logic.

Copy link
Contributor

@mostronatorcoder mostronatorcoder bot left a comment

Choose a reason for hiding this comment

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

Issues from previous review addressed ✅

All test cases I requested have been implemented:

  1. Migration script test

    • Verifies COMPLETED_BY_ADMINPAID_HOLD_INVOICE + settled_by_admin: true
    • Checks correct query and update structure
  2. Admin settle flow test

    • should set settled_by_admin when admin settles with secret
    • Verifies settleHoldInvoice is called and settled_by_admin = true
  3. Settle failure test

    • should not modify order if settleHoldInvoice fails
    • Verifies order.save() is NOT called when settle throws
  4. Block query test

    • Verifies query excludes PAID_HOLD_INVOICE but NOT COMPLETED_BY_ADMIN
    • Ensures status cleanup is correct
  5. getDetailedOrder tests

    • Shows "Yes" when settled_by_admin = true
    • Shows "No" when settled_by_admin = false

Architecture ✅

  • Correct design: status reflects workflow state, boolean captures metadata
  • Migration script included for existing data
  • Critical bug fix: operation ordering now prevents inconsistent state

CI ✅

All checks passing.

Summary

LGTM. The refactor is clean, properly tested, and includes an important bug fix for the settle operation ordering.

Copy link
Contributor

@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

🧹 Nitpick comments (4)
tests/scripts/migrate_completed_by_admin_orders.spec.ts (2)

1-3: Consider using ES imports for consistency.

The file uses CommonJS require (lines 1-3) but ends with export {} (line 81). For consistency across a TypeScript codebase, consider using ES imports:

import { expect } from 'chai';
import sinon from 'sinon';
import proxyquire from 'proxyquire';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/scripts/migrate_completed_by_admin_orders.spec.ts` around lines 1 - 3,
Replace CommonJS requires with ES module imports to match the file's trailing
"export {}" and TypeScript style: change the top-level requires for expect,
sinon, and proxyquire to ES imports (referencing expect, sinon, proxyquire) so
the file consistently uses "import { expect } from 'chai';", "import sinon from
'sinon';" and "import proxyquire from 'proxyquire';" and remove the
corresponding require statements.

59-60: Arbitrary timeout is a flaky test pattern.

Using setTimeout(resolve, 10) to wait for the migration's async IIFE is unreliable—the 10ms may not be enough under CI load, or it may be wastefully slow in other cases. If the migration script exports its migrate() function (or a promise), the test could await it directly for deterministic behavior.

If refactoring the migration script isn't feasible, consider increasing the timeout and adding a comment explaining the trade-off, or poll for updateManyStub.called with a short interval.

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

In `@tests/scripts/migrate_completed_by_admin_orders.spec.ts` around lines 59 -
60, The test uses an arbitrary sleep (await new Promise(resolve =>
setTimeout(resolve, 10))) which is flaky; instead make the migration
deterministic by awaiting the migration's exported promise or function (await
migrate()) if the migration script can export migrate(), or poll/wait for the
spy/stub state (e.g., loop with short intervals until updateManyStub.called is
true or timeout) to assert completion; update the test in
migrate_completed_by_admin_orders.spec.ts to call the exported migrate() or
replace the setTimeout with a short polling loop checking updateManyStub.called
to reliably await the async IIFE.
tests/bot/modules/block.spec.ts (1)

69-74: Broaden the settled-status assertion to reduce false positives.

On Line 72, the test only checks PAID_HOLD_INVOICE. If another terminal status (like SUCCESS) is accidentally dropped from $nin, this test would still pass.

Suggested assertion hardening
-    expect(queryArgs.status.$nin).to.include('PAID_HOLD_INVOICE');
+    expect(queryArgs.status.$nin).to.include.members([
+      'PAID_HOLD_INVOICE',
+      'SUCCESS',
+    ]);
     expect(queryArgs.status.$nin).to.not.include('COMPLETED_BY_ADMIN');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bot/modules/block.spec.ts` around lines 69 - 74, The test currently
only asserts that queryArgs.status.$nin includes 'PAID_HOLD_INVOICE' which can
miss other terminal statuses; update the assertion to ensure
queryArgs.status.$nin contains the full set of settled/terminal statuses (e.g.,
PAID_HOLD_INVOICE, SUCCESS, COMPLETED_BY_ADMIN) by either importing the
canonical settled-statuses constant from the production code and asserting each
element is present, or explicitly asserting inclusion of every known terminal
status in queryArgs.status.$nin.
tests/bot/bot.spec.ts (1)

627-643: Missing logger stub in proxyquire setup.

Both new tests omit the '../logger' stub that the beforeEach block at lines 437-442 includes. When settleHoldInvoice rejects (test 2), the handler's catch block calls logger.error(error). Without a stub, the real logger module is invoked, which could cause console output or unexpected side effects during test runs.

Proposed fix

Add the logger stub to both proxyquire setups:

     const startModule = proxyquire('../../bot/start', {
       telegraf: { Telegraf: sinon.stub().returns(botStub) },
       '../models': {
         Order: { findOne: OrderFindOneStub },
         User: { findOne: sinon.stub().resolves({ id: 'user' }) },
         Dispute: { findOne: sinon.stub().resolves(null) },
       },
       './validations': {
         validateParams: sinon.stub().resolves(['orderId']),
         validateObjectId: sinon.stub().resolves(true),
       },
       '../ln': { settleHoldInvoice: settleHoldInvoiceStub },
       './messages': {
         successCompleteOrderMessage: sinon.stub().resolves(),
         successCompleteOrderByAdminMessage: sinon.stub().resolves(),
       },
+      '../logger': {
+        error: sinon.stub(),
+        notice: sinon.stub(),
+        debug: sinon.stub(),
+        info: sinon.stub(),
+      },
     });

Also applies to: 682-698

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

In `@tests/bot/bot.spec.ts` around lines 627 - 643, The proxyquire test setups for
startModule are missing a stub for the logger module, so when
settleHoldInvoiceStub rejects the code path calls logger.error and invokes the
real logger; add a logger stub entry to the proxyquire mocks (e.g., include
'../logger': { error: sinon.stub(), info: sinon.stub(), warn: sinon.stub() } or
the same stub used in beforeEach) for both proxyquire calls that create
startModule so the handler's catch block uses the stubbed logger instead of the
real module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/bot/bot.spec.ts`:
- Around line 668-717: The test currently stubs settleHoldInvoice to reject,
which doesn't match the real function (ln/hold_invoice.ts::settleHoldInvoice)
that catches errors and does not reject; update the test in bot.spec.ts to stub
../ln.settleHoldInvoice to resolve (e.g., resolves()), then adjust assertions to
expect orderMock.save to have been called and orderMock.settled_by_admin to be
true to reproduce the real-world "silent failure" false-positive; alternatively,
if you intend to change production behavior to propagate errors, add a TODO in
ln/hold_invoice.ts near settleHoldInvoice noting tests rely on its swallowing
errors and add a new test after refactor to assert rejection behavior.

---

Nitpick comments:
In `@tests/bot/bot.spec.ts`:
- Around line 627-643: The proxyquire test setups for startModule are missing a
stub for the logger module, so when settleHoldInvoiceStub rejects the code path
calls logger.error and invokes the real logger; add a logger stub entry to the
proxyquire mocks (e.g., include '../logger': { error: sinon.stub(), info:
sinon.stub(), warn: sinon.stub() } or the same stub used in beforeEach) for both
proxyquire calls that create startModule so the handler's catch block uses the
stubbed logger instead of the real module.

In `@tests/bot/modules/block.spec.ts`:
- Around line 69-74: The test currently only asserts that queryArgs.status.$nin
includes 'PAID_HOLD_INVOICE' which can miss other terminal statuses; update the
assertion to ensure queryArgs.status.$nin contains the full set of
settled/terminal statuses (e.g., PAID_HOLD_INVOICE, SUCCESS, COMPLETED_BY_ADMIN)
by either importing the canonical settled-statuses constant from the production
code and asserting each element is present, or explicitly asserting inclusion of
every known terminal status in queryArgs.status.$nin.

In `@tests/scripts/migrate_completed_by_admin_orders.spec.ts`:
- Around line 1-3: Replace CommonJS requires with ES module imports to match the
file's trailing "export {}" and TypeScript style: change the top-level requires
for expect, sinon, and proxyquire to ES imports (referencing expect, sinon,
proxyquire) so the file consistently uses "import { expect } from 'chai';",
"import sinon from 'sinon';" and "import proxyquire from 'proxyquire';" and
remove the corresponding require statements.
- Around line 59-60: The test uses an arbitrary sleep (await new Promise(resolve
=> setTimeout(resolve, 10))) which is flaky; instead make the migration
deterministic by awaiting the migration's exported promise or function (await
migrate()) if the migration script can export migrate(), or poll/wait for the
spy/stub state (e.g., loop with short intervals until updateManyStub.called is
true or timeout) to assert completion; update the test in
migrate_completed_by_admin_orders.spec.ts to call the exported migrate() or
replace the setTimeout with a short polling loop checking updateManyStub.called
to reliably await the async IIFE.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7b2131be-c880-4513-9f03-461c9e098ba8

📥 Commits

Reviewing files that changed from the base of the PR and between 7e9a117 and 80bbedb.

📒 Files selected for processing (5)
  • scripts/migrate_completed_by_admin_orders.ts
  • tests/bot/bot.spec.ts
  • tests/bot/modules/block.spec.ts
  • tests/scripts/migrate_completed_by_admin_orders.spec.ts
  • tests/util/index.spec.ts
✅ Files skipped from review due to trivial changes (1)
  • scripts/migrate_completed_by_admin_orders.ts

Comment on lines +668 to +717
it('should not modify order if settleHoldInvoice fails', async () => {
const orderMock = {
_id: 'orderId',
status: 'DISPUTE',
secret: 'invalidsecret',
community_id: null,
buyer_id: 'buyer',
seller_id: 'seller',
save: sinon.stub().resolves(),
} as any;

const OrderFindOneStub = sinon.stub().resolves(orderMock);
const settleHoldInvoiceStub = sinon.stub().rejects(new Error('LND failed'));

const startModule = proxyquire('../../bot/start', {
telegraf: { Telegraf: sinon.stub().returns(botStub) },
'../models': {
Order: { findOne: OrderFindOneStub },
User: { findOne: sinon.stub().resolves({ id: 'user' }) },
Dispute: { findOne: sinon.stub().resolves(null) },
},
'./validations': {
validateParams: sinon.stub().resolves(['orderId']),
validateObjectId: sinon.stub().resolves(true),
},
'../ln': { settleHoldInvoice: settleHoldInvoiceStub },
'./messages': {
successCompleteOrderMessage: sinon.stub().resolves(),
successCompleteOrderByAdminMessage: sinon.stub().resolves(),
},
});

startModule.initialize('dummy-token', {});
const settleOrderCall = botStub.command
.getCalls()
.find((c: any) => c.args[0] === 'settleorder');
const handler = settleOrderCall.args[2];

const ctx = {
admin: { admin: true },
match: ['/settleorder orderId', 'orderId'],
reply: sinon.stub().resolves(),
i18n: { t: sinon.stub().returns('Success') },
};

await handler(ctx);

expect(orderMock.save.called).to.be.equal(false);
expect(orderMock.settled_by_admin).to.equal(undefined);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Test stub behavior doesn't match production settleHoldInvoice implementation.

Per the relevant code snippet from ln/hold_invoice.ts:43-49, the real settleHoldInvoice catches errors internally and logs them—it never rejects. This test stubs it to reject, which verifies handler behavior for a scenario that can't occur in production.

In practice, if LN settlement fails:

  1. settleHoldInvoice catches the error, logs it, and returns normally
  2. Handler continues to set settled_by_admin = true and calls save()
  3. Order is incorrectly marked as settled by admin (false positive)

Consider either:

  • Adding a test that stubs settleHoldInvoice to resolve (mimicking silent failure) and verifying the false-positive scenario, or
  • If the intent is to refactor settleHoldInvoice to propagate errors, add a TODO comment noting this dependency
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bot/bot.spec.ts` around lines 668 - 717, The test currently stubs
settleHoldInvoice to reject, which doesn't match the real function
(ln/hold_invoice.ts::settleHoldInvoice) that catches errors and does not reject;
update the test in bot.spec.ts to stub ../ln.settleHoldInvoice to resolve (e.g.,
resolves()), then adjust assertions to expect orderMock.save to have been called
and orderMock.settled_by_admin to be true to reproduce the real-world "silent
failure" false-positive; alternatively, if you intend to change production
behavior to propagate errors, add a TODO in ln/hold_invoice.ts near
settleHoldInvoice noting tests rely on its swallowing errors and add a new test
after refactor to assert rejection behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants