Skip to content

feat: add missing tests to the sui-indexer storage#361

Open
rmeena840 wants to merge 1 commit intomasterfrom
348-add-missing-tests-to-sui-indexer-storage
Open

feat: add missing tests to the sui-indexer storage#361
rmeena840 wants to merge 1 commit intomasterfrom
348-add-missing-tests-to-sui-indexer-storage

Conversation

@rmeena840
Copy link
Contributor

@rmeena840 rmeena840 commented Feb 16, 2026

Description

Closes: #348


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • added ! to the type prefix if API or client breaking change
  • added appropriate labels to the PR
  • provided a link to the relevant issue or specification
  • added a changelog entry to CHANGELOG.md
  • included doc comments for public functions
  • updated the relevant documentation or specification
  • reviewed "Files changed" and left comments if necessary

Summary by Sourcery

Add test coverage for additional indexer storage operations related to Sui GraphQL cursors and redeem handling.

Tests:

  • Add tests for reading and writing multiple Sui GraphQL cursors in indexer state storage.
  • Add tests covering redeem request existence checks, data retrieval, and signed redeem queries.
  • Add tests for redeem-related storage behaviors including setup retrieval, sign ID management, broadcast marking, and finalization with UTXO status updates.

@rmeena840 rmeena840 self-assigned this Feb 16, 2026
@rmeena840 rmeena840 requested a review from a team as a code owner February 16, 2026 13:52
@rmeena840 rmeena840 linked an issue Feb 16, 2026 that may be closed by this pull request
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 16, 2026

Reviewer's Guide

Adds comprehensive unit tests around Sui indexer storage behavior for Sui GraphQL cursors and NBTC redeem flows, covering cursor persistence, redeem existence and retrieval, signing metadata, broadcasting, and finalization side-effects on UTXOs.

ER diagram for Sui indexer state and NBTC redeem tables tested

erDiagram
    INDEXER_STATE {
        int setup_id PK
        string nbtc_cursor
        int updated_at
    }

    NBTC_REDEEM_REQUESTS {
        int redeem_id PK
        int setup_id FK
        string redeemer
        string recipient_script
        int amount
        int fee
        string sui_tx
        string status
        string btc_tx
    }

    NBTC_UTXOS {
        int nbtc_utxo_id PK
        int setup_id FK
        string script_pubkey
        string dwallet_id
        string txid
        int vout
        int amount
        string status
    }

    REDEEM_INPUTS {
        int redeem_id FK
        int utxo_id FK
        int input_index
        string dwallet_id
        int created_at
        string sign_id
    }

    INDEXER_STATE ||--o{ NBTC_REDEEM_REQUESTS : has_redeem_requests
    INDEXER_STATE ||--o{ NBTC_UTXOS : has_utxos
    NBTC_REDEEM_REQUESTS ||--o{ REDEEM_INPUTS : has_inputs
    NBTC_UTXOS ||--o{ REDEEM_INPUTS : referenced_by_inputs
Loading

Class diagram for IndexerStorage redeem and cursor operations

classDiagram
    class IndexerStorage {
        getMultipleSuiGqlCursors(setupIds)
        saveMultipleSuiGqlCursors(entries)
        getSuiGqlCursor(setupId)

        hasRedeemRequest(redeemId)
        getRedeemUtxosWithDetails(redeemId)
        getRedeemRequestData(redeemId)
        getSignedRedeems()
        getRedeemWithSetup(redeemId)

        saveRedeemInputs(inputs)
        getRedeemInputs(redeemId)
        updateRedeemInputSig(redeemId, utxoId, signId)
        clearRedeemInputSignId(redeemId, utxoId)
        getRedeemInfoBySignId(signId)

        markRedeemBroadcasted(redeemId, btcTxId)
        setRedeemFinalized(redeemId)
    }

    class IndexerState {
        int setup_id
        string nbtc_cursor
        int updated_at
    }

    class RedeemRequest {
        int redeem_id
        int setup_id
        string redeemer
        string recipient_script
        int amount
        int fee
        string sui_tx
        RedeemRequestStatus status
        string btc_tx
    }

    class RedeemInput {
        int redeem_id
        int utxo_id
        int input_index
        string dwallet_id
        int created_at
        string sign_id
    }

    class NbtcUtxo {
        int nbtc_utxo_id
        int setup_id
        string script_pubkey
        string dwallet_id
        string txid
        int vout
        int amount
        UtxoStatus status
    }

    class RedeemRequestStatus {
        <<enumeration>>
        Pending
        Signed
        Broadcasting
        Finalized
    }

    class UtxoStatus {
        <<enumeration>>
        Available
        Spent
    }

    IndexerStorage --> IndexerState : uses
    IndexerStorage --> RedeemRequest : manages
    IndexerStorage --> RedeemInput : manages
    IndexerStorage --> NbtcUtxo : manages

    RedeemRequestStatus <.. RedeemRequest : status
    UtxoStatus <.. NbtcUtxo : status

    RedeemRequest "1" --> "*" RedeemInput : has
    RedeemInput "*" --> "1" NbtcUtxo : references
    IndexerState "1" --> "*" RedeemRequest : setup_for
    IndexerState "1" --> "*" NbtcUtxo : setup_for
Loading

File-Level Changes

Change Details Files
Add tests for Sui GraphQL cursor read/write operations in indexer_state
  • Insert indexer_state rows and assert getMultipleSuiGqlCursors returns a cursor map keyed by setup id
  • Persist multiple Sui GraphQL cursors via saveMultipleSuiGqlCursors and verify getSuiGqlCursor reads the stored value
packages/sui-indexer/src/storage.test.ts
Add tests for redeem request lifecycle and related UTXO handling
  • Verify hasRedeemRequest correctly detects existing and non-existing redeem IDs
  • Ensure getRedeemUtxosWithDetails returns associated UTXOs including input_index metadata
  • Assert getRedeemRequestData exposes redeem recipient_script and amount
  • Verify getSignedRedeems returns only Signed redeems
  • Check getRedeemWithSetup joins redeem data with setup configuration (e.g., nbtc_pkg)
packages/sui-indexer/src/storage.test.ts
Add tests for redeem signing metadata, broadcasting, and finalization side effects
  • Test clearRedeemInputSignId removes sign_id on specific redeem input rows
  • Validate getRedeemInfoBySignId resolves redeem info from a sign_id
  • Confirm markRedeemBroadcasted updates redeem status to Broadcasting and sets btc_tx
  • Confirm setRedeemFinalized updates redeem status to Finalized and marks associated UTXOs as Spent
packages/sui-indexer/src/storage.test.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#348 Add missing tests so that all SQL queries used by the sui-indexer storage module are covered by tests.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@rmeena840 rmeena840 force-pushed the 348-add-missing-tests-to-sui-indexer-storage branch 2 times, most recently from a1bbafc to ebe72cf Compare February 16, 2026 14:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds additional unit test coverage for D1Storage behaviors in the Sui indexer, focusing on Sui GraphQL cursor storage and redeem-related read/write helpers to ensure SQL queries are exercised.

Changes:

  • Add tests for reading/saving Sui GraphQL cursors in indexer_state.
  • Add tests for redeem-request existence checks and redeem data retrieval helpers.
  • Add tests for redeem input sign-id clearing, sign-id lookup, broadcast marking, and finalization (including UTXO status updates).

Comment on lines +608 to +609
const cursors = await storage.getMultipleSuiGqlCursors([1]);
expect(cursors[1]).toBe("cursor1");
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

This test claims to cover multiple setups, but it only queries a single setupId. To validate the intended behavior of getMultipleSuiGqlCursors, pass at least two setupIds and assert that missing setups are present in the result with a null cursor (the implementation pre-seeds the map with nulls).

Suggested change
const cursors = await storage.getMultipleSuiGqlCursors([1]);
expect(cursors[1]).toBe("cursor1");
const cursors = await storage.getMultipleSuiGqlCursors([1, 2]);
expect(cursors[1]).toBe("cursor1");
expect(cursors[2]).toBeNull();

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +612 to +615
it("saveMultipleSuiGqlCursors should save cursors", async () => {
await storage.saveMultipleSuiGqlCursors([{ setupId: 1, cursor: "newCursor" }]);
const cursor = await storage.getSuiGqlCursor(1);
expect(cursor).toBe("newCursor");
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The test name says "save ... cursors" but only exercises a single cursor insert. Consider saving multiple cursors and also covering the ON CONFLICT update path (e.g., save an initial cursor then save again for the same setupId) so this test actually verifies the multi-save/upsert behavior.

Suggested change
it("saveMultipleSuiGqlCursors should save cursors", async () => {
await storage.saveMultipleSuiGqlCursors([{ setupId: 1, cursor: "newCursor" }]);
const cursor = await storage.getSuiGqlCursor(1);
expect(cursor).toBe("newCursor");
it("saveMultipleSuiGqlCursors should save cursors and update existing ones", async () => {
// Initial save with multiple cursors
await storage.saveMultipleSuiGqlCursors([
{ setupId: 1, cursor: "cursor1-initial" },
{ setupId: 2, cursor: "cursor2-initial" },
]);
let cursors = await storage.getMultipleSuiGqlCursors([1, 2]);
expect(cursors[1]).toBe("cursor1-initial");
expect(cursors[2]).toBe("cursor2-initial");
// Save again for an existing setupId to exercise ON CONFLICT/update,
// and add a new setupId at the same time.
await storage.saveMultipleSuiGqlCursors([
{ setupId: 1, cursor: "cursor1-updated" },
{ setupId: 3, cursor: "cursor3-initial" },
]);
cursors = await storage.getMultipleSuiGqlCursors([1, 2, 3]);
expect(cursors[1]).toBe("cursor1-updated");
expect(cursors[2]).toBe("cursor2-initial");
expect(cursors[3]).toBe("cursor3-initial");
const singleCursor = await storage.getSuiGqlCursor(1);
expect(singleCursor).toBe("cursor1-updated");

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

it has been merged

it("getRedeemRequestData should return recipient_script and amount", async () => {
await insertRedeemRequest(storage, 1, "redeemer1", recipientScript, 3000, 1000, "0xSuiTx1");
const data = await storage.getRedeemRequestData(1);
expect(data).not.toBeNull();
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

This test description says it should return both recipient_script and amount, but it only asserts amount. To fully verify the query/mapping, also assert that recipient_script matches the inserted recipientScript (byte-for-byte).

Suggested change
expect(data).not.toBeNull();
expect(data).not.toBeNull();
expect(data!.recipient_script).toEqual(recipientScript);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

merged

Copy link
Contributor

@robert-zaremba robert-zaremba left a comment

Choose a reason for hiding this comment

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

too much boilerplate / repeated code. Please simplify

"INSERT INTO indexer_state (setup_id, nbtc_cursor, updated_at) VALUES (?, ?, ?)",
)
.bind(1, "cursor1", Date.now())
.run();
Copy link
Contributor

Choose a reason for hiding this comment

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

we have funciton in storage for that. You don't need to make a raw SQL here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +658 to +661
});

it("getSignedRedeems should return signed redeems", async () => {
await insertRedeemRequest(storage, 1, "redeemer1", recipientScript, 3000, 1000, "0xSuiTx1");
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
});
it("getSignedRedeems should return signed redeems", async () => {
await insertRedeemRequest(storage, 1, "redeemer1", recipientScript, 3000, 1000, "0xSuiTx1");

Copy link
Contributor

Choose a reason for hiding this comment

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

you can merge the tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

await insertRedeemRequest(storage, 1, "redeemer1", recipientScript, 3000, 1000, "0xSuiTx1");
const redeem = await storage.getRedeemWithSetup(1);
expect(redeem).not.toBeNull();
expect(redeem!.nbtc_pkg).toBe("0xPkg1");
Copy link
Contributor

Choose a reason for hiding this comment

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

same here , you can merge this with the test above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

},
]);
await storage.updateRedeemInputSig(1, 1, "signId123");
const info = await storage.getRedeemInfoBySignId("signId123");
Copy link
Contributor

Choose a reason for hiding this comment

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

you can merge this test with the test above, and run the check just before updateRedeemStatus

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

.first<{ status: string }>();
expect(redeem!.status).toBe(RedeemRequestStatus.Finalized);
expect(utxo!.status).toBe(UtxoStatus.Spent);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

this test can be also merged with one of the tests above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@rmeena840 rmeena840 force-pushed the 348-add-missing-tests-to-sui-indexer-storage branch from ebe72cf to 20d0907 Compare February 23, 2026 07:26
Signed-off-by: rmeena840 <rmeena840@gmail.com>
@rmeena840 rmeena840 force-pushed the 348-add-missing-tests-to-sui-indexer-storage branch from 20d0907 to cdf6a91 Compare February 23, 2026 09:10
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.

Add missing tests to sui-indexer storage

3 participants