Skip to content

Remove "action" subpackages for consistent testing across the repo #2454

@Unique-Divine

Description

@Unique-Divine

Remove action Subpackage for Consistent Testing (2026-02-09)

Intent

The repository previously included a custom testing abstraction under x/nutil/testutil/action. This abstraction defined an Action interface and a Given / When / Then style test composition mechanism.

This pattern is no longer aligned with current testing practices in the codebase. Most modules now rely on:

  • Table-driven tests
  • testify suites
  • Direct use of testapp, testnetwork, and testutil/assertion

The goal of this change is to remove the action subpackage entirely and ensure all tests follow a consistent, idiomatic Go testing style. No test behavior should change. Only test structure and helpers are affected.


1. What the action subpackage does today

Location: x/nutil/testutil/action

This package defines a small domain-specific testing abstraction. Key components:

Account helpers (account.go)

  • Funds accounts or modules via the BankKeeper
  • Internally mints from the inflation module and transfers funds
  • Exposed as:
    • FundAccount(account, amount) Action
    • FundModule(module, amount) Action

Block and time helpers (block.go)

  • Mutates block height and block time
  • Optionally runs EndBlock, Commit, and BeginBlock
  • Exposed as Action constructors such as:
    • IncreaseBlockNumberBy
    • IncreaseBlockTimeBy
    • SetBlockTime
    • MoveToNextBlock
    • Variants that accept a duration or explicit timestamp

Test orchestration (testcase.go)

  • Defines:
    • Action interface:
      Do(app *NibiruApp, ctx sdk.Context) (sdk.Context, error)
      
    • TestCase with Given, When, and Then action lists
    • TestSuite that executes multiple test cases
  • Each test case:
    • Creates a fresh app and context
    • Executes actions sequentially: given → when → then

Conclusion

This abstraction is redundant. The same behavior already exists elsewhere in simpler and more idiomatic forms.


2. Inventory of remaining usage

Before removal, confirm whether the action subpackage is still used.

What to search for

  • Imports of:
    x/nutil/testutil/action
    
  • Usage of:
    • action.TC(...)
    • .Given(...), .When(...), .Then(...)
    • Any references to the Action interface

Current state

As of this document:

  • There are no remaining imports or call sites.
  • A repository-wide search finds no usage outside the package itself.
  • The action subpackage is therefore dead code.

If future work uncovers usage, document it explicitly before proceeding, for example:

Remaining usage

  • path/to/file_test.go: uses action.TC for integration tests

3. Standard testing pattern going forward

Tests must not use the Action interface or TestCase abstraction.

Required building blocks

  • App setup
    • Use x/nutil/testutil/testapp
    • Use x/nutil/testutil/testnetwork for multi-node tests
  • Funding
    • Use:
      • testapp.FundAccount
      • testapp.FundModuleAccount
  • Assertions
    • Use helpers from x/nutil/testutil/assertion
  • Structure
    • Table-driven tests or testify suites
    • Explicit steps written directly in the test body

Guideline

If a test step cannot be expressed clearly as plain Go code, extract a small helper function. Do not reintroduce a DSL or test orchestration framework.


4. Replacement for helpers in action

Account funding

No changes required.

The following already exist and are equivalent:

  • testapp.FundAccount
  • testapp.FundModuleAccount

The action equivalents are redundant.

Block and time progression

The block progression logic in action/block.go is still useful, but it should be exposed as plain helper functions, not as Action implementations.

Required helpers

Extract the logic into regular functions, for example:

  • IncreaseBlockNumberBy(ctx sdk.Context, numBlocks int64) sdk.Context
  • IncreaseBlockTimeBy(ctx sdk.Context, delta time.Duration) sdk.Context
  • SetBlockTime(ctx sdk.Context, t time.Time) sdk.Context
  • SetBlockNumber(ctx sdk.Context, height int64) sdk.Context
  • MoveToNextBlock(app *NibiruApp, ctx sdk.Context) (sdk.Context, error)

Optional variants should only be added if they are actively needed.

Implementation rules

  • Reuse the existing logic from action/block.go
  • Preserve behavior exactly:
    • Same block lifecycle calls
    • Same context creation semantics
  • Place helpers in a clear location, such as:
    • x/nutil/testutil/block.go
    • or another focused testutil file

TestCase and TestSuite

Do not preserve these abstractions.

If any usage is discovered later, rewrite tests using:

  • t.Run(...)
  • Direct app and context setup
  • Explicit sequencing of steps

5. Refactoring existing tests (if any are found)

If the inventory step reveals usage:

  1. Replace:
    action.TC("name").
      Given(...).
      When(...).
      Then(...)
    
    with:
    t.Run("name", func(t *testing.T) {
      ...
    })
    
  2. Inline the test steps in order.
  3. Use testapp and the new block helpers directly.
  4. Remove all action imports.
  5. Keep behavior identical.

Refactor incrementally, one file at a time.


6. Remove the action package

Once no usage remains:

  • Delete the directory:
    x/nutil/testutil/action
    
  • Run:
    go test ./...
    
    or the equivalent test command used by the repo.

7. Documentation updates

  • Review x/nutil/testutil/README.md
    • Remove any references to the action DSL if present
    • Add a short example showing the preferred testing pattern
  • Optionally add a brief note to contributor or testing guidelines stating that the action DSL has been removed and should not be reintroduced.

8. Validation checklist

Before closing the issue, confirm:

  • No imports of x/nutil/testutil/action remain
  • The action directory is fully deleted
  • go test ./x/nutil/... passes
  • Full test suite passes
  • Tests that depend on block progression still behave correctly

Once complete, issue #2454 is resolved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    S-triageStatus: This issue is waiting on initial triage. More Info: https://tinyurl.com/25uty9w5

    Type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions