Skip to content

Conversation

@robert-zaremba
Copy link
Contributor

@robert-zaremba robert-zaremba commented Dec 24, 2025

Description

Summary by Sourcery

Refactor the BTC indexer test helper into a stateful class to simplify test setup and usage.

Enhancements:

  • Encapsulate BTC indexer test helper logic in a TesSuiteHelper class instead of a factory function and interface.

Tests:

  • Update BTC indexer tests to construct and initialize the new TesSuiteHelper class for configuring the indexer and related mocks.

Signed-off-by: Robert Zaremba <robert@zaremba.ch>
@robert-zaremba robert-zaremba requested a review from a team as a code owner December 24, 2025 15:01
Copilot AI review requested due to automatic review settings December 24, 2025 15:01
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 24, 2025

Reviewer's Guide

Refactors the BTC indexer test suite helper from a factory function and interface into a stateful TesSuiteHelper class that encapsulates environment setup, mocks, and utility methods, and updates btcindexer tests to use the new class-based helper lifecycle.

Sequence diagram for TesSuiteHelper-based btcindexer test setup

sequenceDiagram
    actor JestRunner
    participant BeforeAll as beforeAll
    participant BeforeEach as beforeEach
    participant Miniflare as Miniflare
    participant Suite as TesSuiteHelper
    participant DB as D1Database
    participant KVBlocks as KVNamespace_BtcBlocks
    participant KVTxs as KVNamespace_nbtc_txs
    participant Storage as CFStorage
    participant Indexer as Indexer
    participant MockSui as MockSuiClient
    participant Electrs as Electrs

    JestRunner->>BeforeAll: configure Miniflare
    BeforeAll->>Miniflare: new Miniflare(options)
    Miniflare-->>BeforeAll: mf instance

    JestRunner->>BeforeEach: start test setup
    BeforeEach->>Suite: new TesSuiteHelper(options)
    Suite-->>BeforeEach: suite instance

    BeforeEach->>Suite: init(mf)
    activate Suite
    Suite->>Miniflare: getD1Database(DB)
    Miniflare-->>Suite: DB
    Suite->>DB: initDb()

    Suite->>Miniflare: getBindings()
    Miniflare-->>Suite: MfBindings
    Suite->>Storage: new CFStorage(DB, BtcBlocks, nbtc_txs)
    Suite->>Suite: set blocksKV, txsKV

    Suite->>Suite: resolve packageConfig from options or TEST_PACKAGE_CONFIG
    Suite->>DB: INSERT setups row
    DB-->>Suite: ok

    Suite->>DB: INSERT nbtc_deposit_addresses for each deposit address
    DB-->>Suite: ok

    Suite->>Suite: create MockSuiClient or use customSuiClient
    Suite->>Suite: create Electrs mock

    Suite->>Indexer: new Indexer(storage, packageConfigs, suiClients, nbtcAddressesMap, confirmationDepth, maxRetries, electrsClients)
    Indexer-->>Suite: indexer instance
    deactivate Suite

    BeforeEach->>Suite: access indexer
    Suite-->>BeforeEach: indexer
    BeforeEach->>JestRunner: tests run with suite and indexer

    JestRunner->>Suite: setupBlock, insertTx, expectMintingCount, expectTxStatus
    Suite->>DB: read and write test data
    Suite->>KVBlocks: put raw block
    Suite->>KVTxs: use txs namespace as needed
    Suite->>MockSui: mockSuiMintBatch
    Suite->>Electrs: mockElectrsSender, mockElectrsError
    Suite-->>JestRunner: helper assertions complete
Loading

Class diagram for refactored TesSuiteHelper test utility

classDiagram
    class TesSuiteHelper {
      +Indexer indexer
      +D1Database db
      +KVNamespace blocksKV
      +KVNamespace txsKV
      +CFStorage storage
      +MockSuiClient mockSuiClient
      +Electrs mockElectrs
      -TestBlocks testData
      -SetupOptions options
      +TesSuiteHelper(options SetupOptions)
      +init(mf Miniflare) void
      +setupBlock(height number) void
      +getBlock(height number) Block
      +getTx(height number, txIndex number) GetTxResult
      +createBlockQueueRecord(height number, options BlockQueueRecordOptions) BlockQueueRecord
      +mockElectrsSender(address string) void
      +mockElectrsError(error Error) void
      +mockSuiMintBatch(result MintBatchResult) void
      +insertTx(options InsertTxOptions) void
      +expectMintingCount(count number) void
      +expectSenderCount(count number, expectedAddress string) void
      +expectTxStatus(txId string, expectedStatus MintTxStatusOrString) void
    }

    class SetupOptions {
      +NbtcPkgCfg packageConfig
      +string[] depositAddresses
      +number confirmationDepth
      +number maxRetries
      +TestBlocks testData
      +MockSuiClient customSuiClient
    }

    class TestBlocks {
    }

    class GetTxResult {
      +TestBlock blockData
      +Block block
      +Transaction targetTx
      +TxInfo txInfo
    }

    class BlockQueueRecordOptions {
      +number height
      +string hash
      +boolean is_processed
      +number retries
    }

    class InsertTxOptions {
      +string txId
      +MintTxStatusOrString status
      +number retryCount
      +number blockHeight
      +string blockHash
      +string suiRecipient
      +number amountSats
      +string depositAddress
      +string sender
      +number vout
    }

    class MintBatchResult {
      +boolean success
      +string message
    }

    class MintTxStatusOrString {
    }

    class Indexer {
    }

    class CFStorage {
    }

    class MockSuiClient {
      +tryMintNbtcBatch(result MintBatchResult) void
    }

    class Electrs {
      +getTx(txId string) Response
    }

    class Miniflare {
      +getD1Database(name string) D1Database
      +getBindings() MfBindings
    }

    class MfBindings {
      +D1Database DB
      +KVNamespace BtcBlocks
      +KVNamespace nbtc_txs
    }

    class NbtcPkgCfg {
      +string id
      +string btc_network
      +string sui_network
      +string nbtc_pkg
      +string nbtc_contract
      +string lc_pkg
      +string lc_contract
      +string sui_fallback_address
      +boolean is_active
    }

    class NbtcDepositAddrsMap {
    }

    class SuiClientI {
    }

    class SuiNet {
    }

    class BtcNet {
    }

    TesSuiteHelper --> SetupOptions
    TesSuiteHelper --> TestBlocks
    TesSuiteHelper --> GetTxResult
    TesSuiteHelper --> BlockQueueRecordOptions
    TesSuiteHelper --> InsertTxOptions
    TesSuiteHelper --> MintBatchResult
    TesSuiteHelper --> MintTxStatusOrString
    TesSuiteHelper --> Indexer
    TesSuiteHelper --> CFStorage
    TesSuiteHelper --> MockSuiClient
    TesSuiteHelper --> Electrs
    TesSuiteHelper --> Miniflare
    TesSuiteHelper --> NbtcDepositAddrsMap
    TesSuiteHelper --> NbtcPkgCfg
    TesSuiteHelper --> MfBindings
    MockSuiClient ..|> SuiClientI
    Electrs --> BtcNet
    MockSuiClient --> SuiNet
    CFStorage --> D1Database
    CFStorage --> KVNamespace
Loading

File-Level Changes

Change Details Files
Refactor test indexer helper from function/interface pattern to a class encapsulating setup, dependencies, and helper methods.
  • Introduce TesSuiteHelper class with constructor storing SetupOptions and TestBlocks, and instance properties for db, KV namespaces, storage, mocks, and indexer.
  • Add init(mf: Miniflare) async method to perform DB initialization, environment binding, setup record insertion, deposit address seeding, client/mock construction, and Indexer instantiation.
  • Convert previously returned helper functions (setupBlock, getBlock, getTx, createBlockQueueRecord, mockElectrsSender, mockElectrsError, mockSuiMintBatch, insertTx, expectMintingCount, expectSenderCount, expectTxStatus) into instance methods using this.* fields and this.testData.
  • Introduce a typed MfBindings interface instead of using any for Miniflare bindings.
  • Replace direct uses of local variables (db, blocksKV, txsKV, mockSuiClient, mockElectrs, testData) with the corresponding TesSuiteHelper instance properties.
packages/btcindexer/src/btcindexer.helpers.test.ts
Adapt btcindexer tests to use the new TesSuiteHelper class-based API instead of the async setup function.
  • Update imports to bring in TesSuiteHelper class instead of setupTestIndexerSuite and TestIndexerHelper interface.
  • Change the suite lifecycle to construct a new TesSuiteHelper in beforeEach with desired options and call suite.init(mf) to initialize it.
  • Keep using suite.indexer as the Indexer under test, now sourced from the TesSuiteHelper instance.
  • Simplify beforeAll to synchronous Miniflare construction, moving async setup into TesSuiteHelper.init.
packages/btcindexer/src/btcindexer.test.ts

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 found 2 issues, and left some high level feedback:

  • The new TesSuiteHelper class appears to have a typo in its name (missing the second 't' in Test), which will make usage less clear and inconsistent with the file name and previous helper naming.
  • In beforeEach, suite.init(mf) is an async method but is not awaited, and beforeAll was changed to non-async; this can lead to tests running before initialization completes, so make beforeEach async and await suite.init(mf) (and adjust beforeAll if needed).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `TesSuiteHelper` class appears to have a typo in its name (missing the second 't' in `Test`), which will make usage less clear and inconsistent with the file name and previous helper naming.
- In `beforeEach`, `suite.init(mf)` is an async method but is not awaited, and `beforeAll` was changed to non-async; this can lead to tests running before initialization completes, so make `beforeEach` `async` and `await suite.init(mf)` (and adjust `beforeAll` if needed).

## Individual Comments

### Comment 1
<location> `packages/btcindexer/src/btcindexer.test.ts:69-72` </location>
<code_context>
+let suite: TesSuiteHelper;

-beforeAll(async () => {
+beforeAll(() => {
 	mf = new Miniflare({
 		script: "",
</code_context>

<issue_to_address>
**issue (bug_risk):** The async `init` method on `TesSuiteHelper` is not awaited in `beforeEach`, which can leave `indexer` uninitialized when tests run.

`TesSuiteHelper.init` is `async`, but `beforeEach` calls `suite.init(mf);` without `await`. Because `init` sets `suite.indexer`, statements like `indexer = suite.indexer;` can still see the initial `null!` value. Please either make `beforeEach` async and `await suite.init(mf);`, or change `init` to be synchronous if it does not need to be async.
</issue_to_address>

### Comment 2
<location> `packages/btcindexer/src/btcindexer.helpers.test.ts:63-66` </location>
<code_context>
+	nbtc_txs: KVNamespace;
+}
+
+export class TesSuiteHelper {
 	indexer: Indexer;
 	db: D1Database;
</code_context>

<issue_to_address>
**suggestion (typo):** The class name `TesSuiteHelper` looks like a typo and may cause confusion.

Since this is an exported class used by the test suite, the misspelling in the name can be misleading. Please rename it to the intended name (for example, `TestSuiteHelper`) and update the corresponding import in `btcindexer.test.ts`.

Suggested implementation:

```typescript
export class TestSuiteHelper {

```

1. In `packages/btcindexer/src/btcindexer.helpers.test.ts`, update any other references to `TesSuiteHelper` (instantiations, type annotations, etc.) to `TestSuiteHelper`.
2. In `packages/btcindexer/src/btcindexer.test.ts` (and any other test files), update the import to use the new class name. For example:
   - Change `import { TesSuiteHelper } from './btcindexer.helpers.test';` to `import { TestSuiteHelper } from './btcindexer.helpers.test';`
   - Update any usages of `TesSuiteHelper` to `TestSuiteHelper`.
</issue_to_address>

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.

Comment on lines +69 to 72
beforeAll(() => {
mf = new Miniflare({
script: "",
modules: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): The async init method on TesSuiteHelper is not awaited in beforeEach, which can leave indexer uninitialized when tests run.

TesSuiteHelper.init is async, but beforeEach calls suite.init(mf); without await. Because init sets suite.indexer, statements like indexer = suite.indexer; can still see the initial null! value. Please either make beforeEach async and await suite.init(mf);, or change init to be synchronous if it does not need to be async.

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

This PR refactors the test suite helper from a function-based approach to a class-based approach. The main change converts setupTestIndexerSuite function and TestIndexerHelper interface into a TesSuiteHelper class (note: appears to be a typo). However, the PR title "TesSuiteHelper" suggests this typo may be intentional, though it's likely an error.

Key changes:

  • Converted test helper from async function returning an object to a class with constructor and init method
  • Changed test setup to instantiate the class and call init() separately
  • Converted helper methods from standalone functions to class methods

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
packages/btcindexer/src/btcindexer.helpers.test.ts Refactored setupTestIndexerSuite function into TesSuiteHelper class with constructor and async init method, converting all helper functions to class methods
packages/btcindexer/src/btcindexer.test.ts Updated to import and instantiate the new TesSuiteHelper class instead of calling the setup function, modified test lifecycle hooks

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


beforeEach(async () => {
suite = await setupTestIndexerSuite(mf, {
suite = new TesSuiteHelper({
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

The constructor call uses "TesSuiteHelper" which contains a typo. It should be "TestSuiteHelper" instead of "TesSuiteHelper" (missing 't' in "Test").

Copilot uses AI. Check for mistakes.
testData: REGTEST_DATA,
});

suite.init(mf);
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

The beforeEach hook should be kept async since it calls suite.init(mf) which is an async method. The init() call should be awaited to ensure proper initialization before tests run.

Suggested change
suite.init(mf);
await suite.init(mf);

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +83
this.db = null!;
this.blocksKV = null!;
this.txsKV = null!;
this.storage = null!;
this.mockSuiClient = null!;
this.mockElectrs = null!;
this.indexer = null!;
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

Initializing properties with null! (non-null assertion) is unsafe. These properties are not actually initialized until init() is called, which could lead to runtime errors if any class methods are called before init(). Consider making init() required in the constructor, or use optional types (e.g., indexer?: Indexer) to better reflect the actual state.

Copilot uses AI. Check for mistakes.
let suite: TesSuiteHelper;

beforeAll(async () => {
beforeAll(() => {
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

The removal of the beforeAll async modifier appears incorrect. While the beforeAll function body itself doesn't contain any await statements, it's generally a good practice to keep async/await consistency with lifecycle hooks, and it's harmless to have it marked as async even if not strictly necessary in this case.

Suggested change
beforeAll(() => {
beforeAll(async () => {

Copilot uses AI. Check for mistakes.
nbtc_txs: KVNamespace;
}

export class TesSuiteHelper {
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

The class name "TesSuiteHelper" contains a typo. It should be "TestSuiteHelper" instead of "TesSuiteHelper" (missing 't' in "Test").

Suggested change
export class TesSuiteHelper {
export class TestSuiteHelper {

Copilot uses AI. Check for mistakes.
import { MintTxStatus } from "./models";
import { BtcNet, type BlockQueueRecord } from "@gonative-cc/lib/nbtc";
import { setupTestIndexerSuite, type TestIndexerHelper } from "./btcindexer.helpers.test";
import { TesSuiteHelper } from "./btcindexer.helpers.test";
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

The imported class name "TesSuiteHelper" contains a typo. It should be "TestSuiteHelper" instead of "TesSuiteHelper" (missing 't' in "Test").

Copilot uses AI. Check for mistakes.
let mf: Miniflare;
let indexer: Indexer;
let suite: TestIndexerHelper;
let suite: TesSuiteHelper;
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

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

The variable type "TesSuiteHelper" contains a typo. It should be "TestSuiteHelper" instead of "TesSuiteHelper" (missing 't' in "Test").

Copilot uses AI. Check for mistakes.
Signed-off-by: Robert Zaremba <robert@zaremba.ch>
@robert-zaremba
Copy link
Contributor Author

let's keep original code - this one is failing

@robert-zaremba robert-zaremba deleted the robert/suite-helper branch December 24, 2025 15:12
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