From cd9bcb0245ce16e59a32102c29fc003abbfd4def Mon Sep 17 00:00:00 2001 From: mrlutik Date: Sun, 14 Dec 2025 22:05:07 +0100 Subject: [PATCH] feat: add integration tests with sekin deployment - Add integration-test job to CI that deploys SEKAI from kiracore/sekin - Create ci-integration-test.yaml scenario testing 12 query operations - Add resolveAddress() to mapper for keys.parse, auth.account, gov.roles, gov.permissions, gov.role-assign, staking.validators, staking.validator - Resolver handles both key names and actual kira addresses --- .github/workflows/ci.yml | 99 ++++++++++++++++++--- examples/scenarios/ci-integration-test.yaml | 78 ++++++++++++++++ pkg/scenarios/mapper.go | 69 +++++++++++--- 3 files changed, 223 insertions(+), 23 deletions(-) create mode 100644 examples/scenarios/ci-integration-test.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f27bd3b..ba6b632 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,9 +34,10 @@ jobs: - name: Run go vet run: go vet ./... - test: - name: Test + build: + name: Build Check runs-on: ubuntu-latest + needs: [lint] steps: - uses: actions/checkout@v4 @@ -46,13 +47,20 @@ jobs: go-version: '1.21' cache: true - - name: Run tests - run: go test -v -race ./... + - name: Build + run: | + CGO_ENABLED=0 go build -o sekai-cli ./cmd/sekai-cli + ./sekai-cli --help - build: - name: Build Check + integration-test: + name: Integration Tests runs-on: ubuntu-latest - needs: [lint, test] + needs: [lint] + env: + CONTAINER: sekin-sekai-1 + CHAIN_ID: testnet-1 + SEKAI_CONTAINER: sekin-sekai-1 + SEKAI_CHAIN_ID: testnet-1 steps: - uses: actions/checkout@v4 @@ -62,7 +70,78 @@ jobs: go-version: '1.21' cache: true - - name: Build + - name: Build sekai-cli + run: CGO_ENABLED=0 go build -o sekai-cli ./cmd/sekai-cli + + - name: Clone sekin repository + run: git clone --depth 1 https://github.com/kiracore/sekin.git /tmp/sekin + + - name: Start SEKAI infrastructure run: | - CGO_ENABLED=0 go build -o sekai-cli ./cmd/sekai-cli - ./sekai-cli --help + cd /tmp/sekin + docker compose up -d sekai syslog-ng + echo "Waiting for containers to start..." + sleep 10 + docker ps + + - name: Initialize SEKAI network + run: | + # Run the spin-local-testnet script steps (except start which blocks) + echo ">>> Step 1: Initialize sekaid" + docker exec ${CONTAINER} /scaller init --chain-id "${CHAIN_ID}" --moniker "CI-Node" + + echo ">>> Step 2: Add genesis key" + docker exec ${CONTAINER} /scaller keys-add --name genesis + + echo ">>> Step 3: Add genesis account" + docker exec ${CONTAINER} /scaller add-genesis-account --name genesis --coins 300000000000000ukex + + echo ">>> Step 4: Claim validator role" + docker exec ${CONTAINER} /scaller gentx-claim --name genesis --moniker "CI-Validator" + + echo ">>> Step 5: Start sekaid (background with auto-restart)" + docker exec -d ${CONTAINER} /scaller start --restart always + + echo "Waiting for node to start producing blocks..." + sleep 30 + + - name: Wait for chain to be ready + run: | + echo "Checking chain status..." + for i in {1..30}; do + if docker exec ${CONTAINER} /sekaid status 2>/dev/null | grep -q '"catching_up":false'; then + echo "Chain is ready and synced!" + docker exec ${CONTAINER} /sekaid status + exit 0 + fi + echo "Attempt $i/30: Chain not ready yet, waiting..." + sleep 5 + done + echo "Chain did not become ready in time" + docker exec ${CONTAINER} /sekaid status || true + exit 1 + + - name: Run sekai-cli status check + run: ./sekai-cli status + + - name: Run scenario-based integration test + run: ./sekai-cli scenario run examples/scenarios/ci-integration-test.yaml + + - name: Run Go integration tests + run: go test -v -timeout 30m ./test/integration/... + + - name: Collect logs on failure + if: failure() + run: | + echo "=== Docker containers ===" + docker ps -a + echo "=== SEKAI container logs ===" + docker logs ${CONTAINER} 2>&1 | tail -200 || true + echo "=== Syslog logs ===" + docker logs sekin-syslog-ng-1 2>&1 | tail -100 || true + + - name: Cleanup + if: always() + run: | + cd /tmp/sekin + docker compose down -v || true diff --git a/examples/scenarios/ci-integration-test.yaml b/examples/scenarios/ci-integration-test.yaml new file mode 100644 index 0000000..0dccc6e --- /dev/null +++ b/examples/scenarios/ci-integration-test.yaml @@ -0,0 +1,78 @@ +# CI Integration Test Scenario +# This scenario tests basic sekai-cli functionality against a running SEKAI node +name: ci-integration-test +description: Integration tests for CI pipeline - verifies basic queries and transactions + +steps: + # === Status Checks === + - name: Check node status + module: status + action: status + output: node_status + + - name: Get network properties + module: gov + action: network-properties + output: network_props + + # === Key Management === + - name: List keys + module: keys + action: list + output: keys_list + + # === Bank Module Queries === + - name: Check genesis balance + module: bank + action: balances + params: + address: genesis + output: genesis_balance + + - name: Get total supply + module: bank + action: total-supply + output: total_supply + + # === Governance Queries === + - name: List all roles + module: gov + action: all-roles + output: all_roles + + - name: Get genesis permissions + module: gov + action: permissions + params: + address: genesis + output: genesis_permissions + + - name: List councilors + module: gov + action: councilors + output: councilors + + # === Token Module Queries === + - name: Get token rates + module: tokens + action: all-rates + output: token_rates + + - name: Get token black whites + module: tokens + action: token-black-whites + output: token_black_whites + + # === Staking Queries === + - name: List validators + module: staking + action: validators + output: validators + + # === Auth Module Queries === + - name: Get genesis account info + module: auth + action: account + params: + address: genesis + output: genesis_account diff --git a/pkg/scenarios/mapper.go b/pkg/scenarios/mapper.go index 5babc7e..062c88f 100644 --- a/pkg/scenarios/mapper.go +++ b/pkg/scenarios/mapper.go @@ -72,6 +72,8 @@ func NewActionMapper(client sdk.Client) *ActionMapper { // resolveAddress resolves a key name to an address if needed. // If the input looks like a bech32 address (starts with "kira"), it's returned as-is. // Otherwise, it tries to resolve it as a key name. +// TODO: Handle Ethereum addresses (0x...) for torii bridge integration. +// TODO: Handle custom bech32 prefixes for minted tokens. func (m *ActionMapper) resolveAddress(ctx context.Context, nameOrAddress string) (string, error) { // If it looks like an address, return as-is if strings.HasPrefix(nameOrAddress, "kira") { @@ -280,10 +282,14 @@ func (m *ActionMapper) executeKeys(ctx context.Context, action string, params ma return nil, nil, err case "parse": - address := params["address"] - if address == "" { + addrParam := params["address"] + if addrParam == "" { return nil, nil, fmt.Errorf("keys.parse requires 'address' parameter") } + address, err := m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } result, err := m.keysMod.Parse(ctx, address) return result, nil, err @@ -427,10 +433,14 @@ func (m *ActionMapper) executeAuth(ctx context.Context, action string, params ma switch action { case "account": - address := params["address"] - if address == "" { + addrParam := params["address"] + if addrParam == "" { return nil, nil, fmt.Errorf("auth.account requires 'address' parameter") } + address, err := m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } result, err := m.authMod.Account(ctx, address) return result, nil, err @@ -510,18 +520,26 @@ func (m *ActionMapper) executeGov(ctx context.Context, action string, params map return result, nil, err case "roles": - address := params["address"] - if address == "" { + addrParam := params["address"] + if addrParam == "" { return nil, nil, fmt.Errorf("gov.roles requires 'address' parameter") } + address, err := m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } result, err := m.govMod.Roles(ctx, address) return result, nil, err case "permissions": - address := params["address"] - if address == "" { + addrParam := params["address"] + if addrParam == "" { return nil, nil, fmt.Errorf("gov.permissions requires 'address' parameter") } + address, err := m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } result, err := m.govMod.Permissions(ctx, address) return result, nil, err @@ -586,13 +604,19 @@ func (m *ActionMapper) executeGov(ctx context.Context, action string, params map case "role-assign", "assign-role": from := params["from"] - address := params["address"] + addrParam := params["address"] roleStr := params["role"] - if from == "" || address == "" || roleStr == "" { + if from == "" || addrParam == "" || roleStr == "" { return nil, nil, fmt.Errorf("gov.role-assign requires 'from', 'address', and 'role' parameters") } + // Resolve address (could be key name or actual address) + address, err := m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } + // Parse role ID as int roleID, err := strconv.Atoi(roleStr) if err != nil { @@ -1489,8 +1513,17 @@ func (m *ActionMapper) executeStaking(ctx context.Context, action string, params switch action { case "validators": + // Resolve address if provided + var address string + if addrParam := params["address"]; addrParam != "" { + var err error + address, err = m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } + } opts := &staking.ValidatorQueryOpts{ - Address: params["address"], + Address: address, ValAddr: params["val_address"], Moniker: params["moniker"], Status: params["status"], @@ -1499,14 +1532,24 @@ func (m *ActionMapper) executeStaking(ctx context.Context, action string, params return result, nil, err case "validator": - address := params["address"] + addrParam := params["address"] valAddr := params["val_address"] moniker := params["moniker"] - if address == "" && valAddr == "" && moniker == "" { + if addrParam == "" && valAddr == "" && moniker == "" { return nil, nil, fmt.Errorf("staking.validator requires 'address', 'val_address', or 'moniker' parameter") } + // Resolve address if provided + var address string + if addrParam != "" { + var err error + address, err = m.resolveAddress(ctx, addrParam) + if err != nil { + return nil, nil, fmt.Errorf("failed to resolve address: %w", err) + } + } + opts := &staking.ValidatorQueryOpts{ Address: address, ValAddr: valAddr,