Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb7e421
feat(tests): add ERC-20 bridge testing utilities and integration tests
outerlook Sep 8, 2025
d6114a6
feat(tests): enhance ERC-20 bridge tests with mock listener and path …
outerlook Sep 9, 2025
a27d905
feat(migrations): enhance seed script handling and add test-only SQL …
outerlook Sep 9, 2025
2f20768
feat(tests): add comprehensive ERC-20 bridge tests for epoch flow and…
outerlook Sep 10, 2025
788a71f
refactor(tests): streamline ERC-20 bridge tests and remove mock liste…
outerlook Sep 10, 2025
aa73919
feat(tests): add comprehensive ERC-20 bridge tests for various functi…
outerlook Sep 10, 2025
d59bef3
feat(tests): enhance ERC-20 bridge tests with new consecutive test ca…
outerlook Sep 11, 2025
434ea77
refactor(tests): streamline ERC-20 bridge tests by removing seed scri…
outerlook Sep 11, 2025
a65927b
fix(ci): update Go test command to include 'kwiltest' build tag for i…
outerlook Sep 11, 2025
a7e4bb4
refactor(tests): update CallLockAdmin function signature and improve …
outerlook Sep 11, 2025
354986c
Merge branch 'main' into chore/bridge-config
outerlook Sep 11, 2025
c07094c
chore(deps): update kwil-db and go-ethereum dependencies in go.mod an…
outerlook Sep 12, 2025
f9950ae
docs(migrations): add comment to bootstrap_erc20.sql for test clarity
outerlook Sep 12, 2025
d6e74dd
refactor(tests): unify ERC-20 bridge test instance activation with ex…
outerlook Sep 12, 2025
cede833
fix(tests): improve error handling in ERC-20 test setup and admin loc…
outerlook Sep 12, 2025
d148bea
refactor(tests): replace kwilTesting.RunSchemaTest with testutils.Run…
outerlook Sep 12, 2025
42babb5
refactor(tests): update test files to use testutils.RunSchemaTest for…
outerlook Sep 12, 2025
3a2c9be
refactor(tests): enhance RunSchemaTest to include SeedStatements, Tes…
outerlook Sep 12, 2025
4e26a93
fix(tests): make test case handling a fatal error in RunSchemaTest
outerlook Sep 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
sleep 5
fi

if go test -short -p 1 -timeout=15m -count=1 ./...; then
if go test -short -p 1 -timeout=15m -count=1 -tags=kwiltest ./...; then
echo "✅ Tests passed on attempt $attempt"
break
else
Expand Down
8 changes: 4 additions & 4 deletions extensions/tn_cache/internal/engine_ops_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func testEngineOperationsIntegration(t *testing.T) func(ctx context.Context, pla
if err != nil {
return errors.Wrap(err, "error registering data provider")
}

logger := log.NewStdoutLogger().New("tn_ops_test")

// Test 1: ListComposedStreams
Expand Down Expand Up @@ -329,13 +329,13 @@ func testEngineOperationsIntegration(t *testing.T) func(ctx context.Context, pla

// TestEngineOperations_RealTimeData tests engine operations with streams that have real-time data updates
func TestEngineOperations_RealTimeData(t *testing.T) {
kwilTesting.RunSchemaTest(t, kwilTesting.SchemaTest{
testutils.RunSchemaTest(t, kwilTesting.SchemaTest{
Name: "tn_ops_realtime_test",
SeedScripts: migrations.GetSeedScriptPaths(),
FunctionTests: []kwilTesting.TestFunc{
testEngineOperationsRealTime(t),
},
}, testutils.GetTestOptions())
}, testutils.GetTestOptionsWithCache())
}

func testEngineOperationsRealTime(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error {
Expand All @@ -348,7 +348,7 @@ func testEngineOperationsRealTime(t *testing.T) func(ctx context.Context, platfo
if err != nil {
return errors.Wrap(err, "error registering data provider")
}

logger := log.NewStdoutLogger()

// Test fetching recent data
Expand Down
4 changes: 2 additions & 2 deletions extensions/tn_cache/internal/engine_ops_permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import (
// and private streams for caching purposes. The SQL authorization functions check if
// wallet_address = 'extension_agent' and grant unrestricted access.
func TestExtensionAgentPermissions(t *testing.T) {
kwilTesting.RunSchemaTest(t, kwilTesting.SchemaTest{
testutils.RunSchemaTest(t, kwilTesting.SchemaTest{
Name: "extension_agent_permissions_test",
SeedScripts: migrations.GetSeedScriptPaths(),
FunctionTests: []kwilTesting.TestFunc{
testExtensionAgentAccess(t),
},
}, testutils.GetTestOptions())
}, testutils.GetTestOptionsWithCache())
}

func testExtensionAgentAccess(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error {
Expand Down
3 changes: 2 additions & 1 deletion extensions/tn_digest/engine_ops_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
kwilTesting "github.com/trufnetwork/kwil-db/testing"
"github.com/trufnetwork/node/extensions/tn_digest/internal"
digestembed "github.com/trufnetwork/node/tests/extensions/digest"
testutils "github.com/trufnetwork/node/tests/streams/utils"
)

// TestBuildAndBroadcastAutoDigestTx_VerifiesTxBuildSignAndDBEffect
Expand All @@ -28,7 +29,7 @@ func TestBuildAndBroadcastAutoDigestTx_VerifiesTxBuildSignAndDBEffect(t *testing
bts, err := digestembed.TestMigrationSQL.ReadFile("test_migration.sql")
require.NoError(t, err)

kwilTesting.RunSchemaTest(t, kwilTesting.SchemaTest{
testutils.RunSchemaTest(t, kwilTesting.SchemaTest{
Comment thread
outerlook marked this conversation as resolved.
Name: "tn_digest_tx_build_broadcast_test",
SeedStatements: []string{string(bts)},
FunctionTests: []kwilTesting.TestFunc{
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ require (
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/testcontainers/testcontainers-go v0.37.0
github.com/trufnetwork/kwil-db v0.10.3-0.20250905175054-602e824e33c2
github.com/trufnetwork/kwil-db/core v0.4.3-0.20250905175054-602e824e33c2
github.com/trufnetwork/kwil-db v0.10.3-0.20250911225741-d6cb2b2747ff
github.com/trufnetwork/kwil-db/core v0.4.3-0.20250911225741-d6cb2b2747ff
github.com/trufnetwork/sdk-go v0.3.2-0.20250630062504-841b40cdb709
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
Expand Down Expand Up @@ -82,7 +82,7 @@ require (
github.com/ebitengine/purego v0.8.2 // indirect
github.com/elastic/gosigar v0.14.3 // indirect
github.com/ethereum/c-kzg-4844 v1.0.2 // indirect
github.com/ethereum/go-ethereum v1.14.13 // indirect
github.com/ethereum/go-ethereum v1.14.13
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1212,10 +1212,10 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/trufnetwork/kwil-db v0.10.3-0.20250905175054-602e824e33c2 h1:sWkNssIA46NoEYD8db4zTHbXJecPvlm8OWbYbyW21Es=
github.com/trufnetwork/kwil-db v0.10.3-0.20250905175054-602e824e33c2/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo=
github.com/trufnetwork/kwil-db/core v0.4.3-0.20250905175054-602e824e33c2 h1:ojCdkwDCQ3/i79zaKQa9PO5l/dBERRZnmP0SFhPOv24=
github.com/trufnetwork/kwil-db/core v0.4.3-0.20250905175054-602e824e33c2/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ=
github.com/trufnetwork/kwil-db v0.10.3-0.20250911225741-d6cb2b2747ff h1:TeVummmhXdwNAuRDst/aWQPfvHZh3TUl7Nw6Q2Oogao=
github.com/trufnetwork/kwil-db v0.10.3-0.20250911225741-d6cb2b2747ff/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo=
github.com/trufnetwork/kwil-db/core v0.4.3-0.20250911225741-d6cb2b2747ff h1:2hQ3ChOBM76eh10Ix0GItIfK5HNwfqrGWhoiQIH/P60=
github.com/trufnetwork/kwil-db/core v0.4.3-0.20250911225741-d6cb2b2747ff/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ=
github.com/trufnetwork/openzeppelin-merkle-tree-go v0.0.2 h1:DCq8MzbWH0wZmICNmMVsSzUHUPl+2vqRhluEABjxl88=
github.com/trufnetwork/openzeppelin-merkle-tree-go v0.0.2/go.mod h1:Y0MJpPp9QXU5vC6Gpoilql2NkgmGNcbHm9HYC2v2N8s=
github.com/trufnetwork/sdk-go v0.3.2-0.20250630062504-841b40cdb709 h1:d9EqPXIjbq/atzEncK5dM3Z9oStx1BxCGuL/sjefeCw=
Expand Down
5 changes: 3 additions & 2 deletions internal/benchmark/digest/digest_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

kwilTesting "github.com/trufnetwork/kwil-db/testing"
testutils "github.com/trufnetwork/node/tests/streams/utils"
utils "github.com/trufnetwork/node/tests/streams/utils"
"github.com/trufnetwork/node/tests/streams/utils/procedure"
"github.com/trufnetwork/sdk-go/core/util"
Expand Down Expand Up @@ -195,13 +196,13 @@ func createBenchmarkSuiteFunc(t *testing.T, scale DigestBenchmarkScale) func(con
// runDigestBenchmarkScale runs a specific benchmark scale.
// This is the core function that handles all benchmark execution logic.
func runDigestBenchmarkScale(t *testing.T, scale DigestBenchmarkScale) {
kwilTesting.RunSchemaTest(t, kwilTesting.SchemaTest{
testutils.RunSchemaTest(t, kwilTesting.SchemaTest{
Name: fmt.Sprintf("digest_benchmark_%s", scale.Name),
SeedScripts: migrations.GetSeedScriptPaths(),
FunctionTests: []kwilTesting.TestFunc{
WithDigestBenchmarkSetup(createBenchmarkSuiteFunc(t, scale)),
},
}, utils.GetTestOptions())
}, utils.GetTestOptionsWithCache())
}
Comment thread
outerlook marked this conversation as resolved.

// WithDigestBenchmarkSetup sets up the testing environment for digest benchmarks.
Expand Down
13 changes: 12 additions & 1 deletion internal/migrations/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
)

//go:embed *.sql
//go:embed *.sql test_only/*.sql
var seedFiles embed.FS

func GetSeedScriptPaths() []string {
Expand All @@ -31,6 +31,17 @@ func GetSeedScriptPaths() []string {
}
}

// process test_only directory
entries, err = seedFiles.ReadDir("test_only")
if err != nil {
panic(err)
}
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".sql") {
seedsFiles = append(seedsFiles, filepath.Join(dir, "test_only", entry.Name()))
}
}
Comment thread
outerlook marked this conversation as resolved.

if len(seedsFiles) == 0 {
panic("no seeds files found in embedded directory")
}
Expand Down
27 changes: 27 additions & 0 deletions internal/migrations/test_only/bootstrap_erc20.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- This file exists to manually enable the ERC20 bootstrap process for our tests

USE kwil_erc20_meta as kwil_erc20_meta;

-- ordered-sync is provisioned by its genesis hook in node runtime. The test harness
-- does not run genesis, so we create the namespace and schema explicitly for tests.
CREATE NAMESPACE IF NOT EXISTS kwil_ordered_sync;
SET CURRENT NAMESPACE TO kwil_ordered_sync;

CREATE TABLE IF NOT EXISTS topics (
id UUID PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
resolve_func TEXT NOT NULL,
last_processed_point int8
);

CREATE TABLE IF NOT EXISTS pending_data (
point int8,
topic_id UUID REFERENCES topics(id) ON UPDATE CASCADE ON DELETE CASCADE,
previous_point int8,
data bytea not null,
PRIMARY KEY (point, topic_id)
);

CREATE TABLE IF NOT EXISTS meta(
version int8 PRIMARY KEY
);
63 changes: 63 additions & 0 deletions tests/extensions/erc20/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Notice: to run tests in this directory, you need to build the node with the `kwiltest` tag.
To make it easier on your IDE, you might want to include in the configurations, for example, for VSCode, you can add the following to your `settings.json`:

```json
{
"go.testTags": "kwiltest"
}
```

### Objective

This directory demonstrates a minimal, production-faithful structure for wiring and invoking the ERC‑20 bridge extension in tests. It:

- Shows how to bootstrap an instance, call actions, and cleanly tear down using the `ForTesting*` helpers.
- Demonstrates how tests should evoke the extension (e.g., alias usage, numeric arguments, engine context, authz boundaries) in isolation.
- Serves as a reference pattern to later integrate these flows into our standard streams tests appropriately.

### Quick start

- Enable the `kwiltest` tag (above) so the test helpers are available.
- Seed and activate an instance for a test:

```go
err := erc20shim.ForTestingSeedAndActivateInstance(ctx, platform, chain, escrow, erc20, 18, 60, alias)
```

- Invoke actions using the alias (e.g., `balance`, `bridge`, `lock_admin`).
- For epoch flows, use `ForTestingFinalizeAndConfirmCurrentEpoch(...)` to deterministically advance.
- Cleanup explicitly with `ForTestingDisableInstance(...)` or rely on the test wrapper’s registered cleanup.

### Why helpers over plain SQL/`USE ...` for ERC-20 tests

- **The ERC-20 bridge extension has runtime wiring beyond SQL.** In production it creates and manages background components and in‑memory state: ordered‑sync topics/listeners for `Transfer` events, a state poller, a singleton cache rehydrated on start, epoch lifecycle rows, and synced ERC‑20 metadata. A bare `USE erc20 { ... } AS alias` only creates the alias; it does not guarantee these side‑effects are present or initialized in the current process.
- **Plain seeds miss critical initialization.** Relying only on SQL seeds/`USE` won’t ensure the `kwil_erc20_meta` schema exists, the `reward_instances` row and first epoch are created, the instance is marked synced with the ERC‑20 address/decimals, the ordered‑sync topic is registered, or that the extension’s singleton has been rehydrated. That leads to flaky tests and "already active"/missing‑wiring failures.

### What `ForTestingSeedAndActivateInstance` does

`ForTestingSeedAndActivateInstance(ctx, platform, chain, escrow, erc20, decimals, periodSeconds, alias)` performs, idempotently:

- Creates the extension alias via `USE erc20 { chain: '<chain>', escrow: '<escrow>' } AS <alias>`.
- Ensures the `kwil_erc20_meta` namespace/schema exist and computes the deterministic instance ID.
- Registers the ordered‑sync transfer topic/state‑poller names for the instance.
- Creates the `reward_instances` row if missing and guarantees a first pending epoch.
- Marks the instance as synced with the provided ERC‑20 address and `decimals`.
- Sets the distribution period to `periodSeconds` for predictable epochs.
- Re‑initializes the extension (OnStart‑like) so the in‑memory singleton reflects DB state.

This yields a production‑like, deterministic setup inside a test transaction, without depending on external chains.

### Cleanup: disabling and teardown

`ForTestingDisableInstance(ctx, platform, chain, escrow, alias)` performs deterministic teardown:

- Executes `UNUSE <alias>` to drop the alias in the test DB.
- Calls `kwil_erc20_meta.disable(id)` to deactivate the instance in storage.
- Unregisters the state poller and transfer listener for the instance.
- Resets and re‑initializes the singleton to a clean state.

Additionally, our test wrapper registers cleanup that resets the singleton and clears runtimes to prevent cross‑test contamination.

### TL;DR

Use the provided `ForTesting*` helpers instead of plain SQL/`USE` to mirror production wiring that SQL alone cannot trigger, ensuring deterministic, isolated ERC‑20 extension tests.
86 changes: 86 additions & 0 deletions tests/extensions/erc20/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//go:build kwiltest

package tests

import (
"context"
"fmt"
"testing"

"github.com/trufnetwork/kwil-db/common"
kwilTesting "github.com/trufnetwork/kwil-db/testing"
"github.com/trufnetwork/node/internal/migrations"
testutils "github.com/trufnetwork/node/tests/streams/utils"

erc20shim "github.com/trufnetwork/kwil-db/node/exts/erc20-bridge/erc20"
)

// Common deterministic values used across ERC20 bridge tests
const (
TestChain = "sepolia"
TestExtensionAlias = "erc20_bridge_test"
TestEscrowA = "0x1111111111111111111111111111111111111111"
TestEscrowB = "0x2222222222222222222222222222222222222222"
TestERC20 = "0x2222222222222222222222222222222222222222"
TestUserA = "0xabc0000000000000000000000000000000000001"
TestUserB = "0xabc0000000000000000000000000000000000002"
TestAmount1 = "1000000000000000000" // 1.0 tokens
TestAmount2 = "2000000000000000000" // 2.0 tokens
)

// seedAndRun is a helper that handles test execution with proper isolation
func seedAndRun(t TestingT, name string, fn kwilTesting.TestFunc) {
seedScripts := migrations.GetSeedScriptPaths()

// Wrap the test function to add singleton reset and cleanup
wrappedFn := func(ctx context.Context, platform *kwilTesting.Platform) error {
// STEP 1: Register cleanup (runs after transaction rollback)
t.Cleanup(func() {
erc20shim.ForTestingClearAllInstances(ctx, platform)
erc20shim.ForTestingResetSingleton()
})

// STEP 2: Run the actual test inside a transaction for rollback isolation
tx, err := platform.DB.BeginTx(ctx)
if err != nil {
return fmt.Errorf("begin tx: %w", err)
}
defer tx.Rollback(ctx)

txPlatform := &kwilTesting.Platform{
Engine: platform.Engine,
DB: tx,
Deployer: platform.Deployer,
Logger: platform.Logger,
}

return fn(ctx, txPlatform)
}

testutils.RunSchemaTest(t.(*testing.T), kwilTesting.SchemaTest{
Name: name,
SeedScripts: seedScripts,
FunctionTests: []kwilTesting.TestFunc{wrappedFn},
}, &testutils.Options{Options: testutils.GetTestOptions()})
}

// engCtx creates a standard EngineContext for testing
func engCtx(ctx context.Context, platform *kwilTesting.Platform, caller string, height int64, overrideAuthz bool) *common.EngineContext {
return &common.EngineContext{
TxContext: &common.TxContext{
Ctx: ctx,
BlockContext: &common.BlockContext{Height: height},
Signer: platform.Deployer,
Caller: caller,
TxID: platform.Txid(),
},
OverrideAuthz: overrideAuthz,
}
}

// TestingT interface for test functions (matches testutils)
type TestingT interface {
Fatalf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Cleanup(func())
}
Loading
Loading