Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
94 changes: 94 additions & 0 deletions .github/workflows/era-events-test-common.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Era Events Test Common
on:
workflow_call:
inputs:
network:
type: string
required: false
default: 'kusama'
run-id:
type: string
description: "run-id of the artifacts to download"
required: true

env:
ZOMBIE_BITE_BASE_PATH: ${{ github.workspace }}/migration-run

jobs:
era-events-test:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4

- uses: extractions/setup-just@v2

- name: download_post_migration_db
uses: ./.github/actions/download-artifact
env:
RUN_ID: ${{ inputs.run-id }}
GH_TOKEN: ${{ github.token }}
NETWORK: ${{ inputs.network }}
with:
gh-token: ${{ env.GH_TOKEN }}
destination-path: ${{ env.ZOMBIE_BITE_BASE_PATH }}
run-id: ${{ env.RUN_ID }}
name-pattern: "${{ env.NETWORK }}-post-migration-db-*"

- name: download_doppelganger_binaries
uses: ./.github/actions/download-doppelganger-binaries
with:
destination-path: ${{ env.ZOMBIE_BITE_BASE_PATH }}

- name: install_zombie_bite
shell: bash
run: |
just install-zombie-bite

- name: verify_artifact
shell: bash
run: |
echo "::group::Artifact contents"
ls -la "$ZOMBIE_BITE_BASE_PATH" || true
ls -la "$ZOMBIE_BITE_BASE_PATH/spawn" || true
echo "::endgroup::"

- uses: actions/setup-node@v4
with:
node-version: 22

- name: npm_install_and_build
shell: bash
run: |
npm install
npm run build

- name: run_zombie_bite_spawn
shell: bash
env:
BASE_PATH: ${{ env.ZOMBIE_BITE_BASE_PATH }}
run: |
export PATH="${BASE_PATH}:$PATH"
nohup just zb spawn $ZOMBIE_BITE_BASE_PATH > nohup.out 2>&1 &

- name: wait_for_network
uses: ./.github/actions/wait-zb-network-ready

- name: run_era_events_test
shell: bash
timeout-minutes: 30
env:
BASE_PATH: ${{ env.ZOMBIE_BITE_BASE_PATH }}
NETWORK: ${{ inputs.network }}
run: |
echo "Running era events test..."
just ahm era-events-test $BASE_PATH $NETWORK

- name: upload_logs_on_failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: era-events-test-logs-${{ inputs.network }}-${{ github.sha }}
path: |
nohup.out
${{ env.ZOMBIE_BITE_BASE_PATH }}/spawn-debug/**/*.log
21 changes: 20 additions & 1 deletion .github/workflows/migration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
required: false
network:
description: "network to use, default kusama"
default: 'kusama'
default: "kusama"
type: choice
options:
- kusama
Expand Down Expand Up @@ -60,3 +60,22 @@ jobs:
run-id: ${{ inputs.run-id }}
network: ${{ inputs.network }}

# TODO: Create dedicated CI job that spawns post-migration network, lets it run for a
# complete era (e.g., 2*14400 blocks), then saves the database as an artifact.
# This ensures we always have databases with full era history for testing.
# For now, using hardcoded run-id from a known good run with full era data.
era-events-test-kusama:
if: ${{ inputs.network == 'kusama' }}
uses: ./.github/workflows/era-events-test-common.yml
with:
# TODO: Replace with run-id from dedicated long-running CI job
run-id: "TBD"
network: kusama

era-events-test-polkadot:
if: ${{ inputs.network == 'polkadot' }}
uses: ./.github/workflows/era-events-test-common.yml
with:
# TODO: Replace with run-id from dedicated long-running CI job
run-id: "TBD"
network: polkadot
7 changes: 7 additions & 0 deletions justfiles/ahm.justfile
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,10 @@ rust-test runtime base_path:
--features {{ runtime }}-ahm \
--features try-runtime \
post_migration_checks_only -- --include-ignored --nocapture --test-threads 1

# Run era events test with archive mode databases from zombie-bite
# Requires: zombie-bite databases in <network>/db/full structure
# Example: just ahm era-events-test ./polkadot-era-test polkadot
era-events-test base_path network="polkadot":
just ahm _npm-build
node dist/zombie-bite-scripts/run_era_events_test.js {{ base_path }} {{ network }}
108 changes: 108 additions & 0 deletions migration-tests/era-events-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import "@polkadot/api-augment";
import "@polkadot/types-augment";
import { ApiPromise, WsProvider } from "@polkadot/api";
import { logger } from "../shared/logger.js";
import { eraEventsTest } from "./pallets/staking/era_events.js";
import { existsSync, readFileSync } from "fs";
import { join } from "path";

/**
* Runner for era events tests using zombie-bite spawned networks
*
* This test expects zombie-bite spawn to be already running with the
* extracted databases from the CI artifact containing enough blocks to accommodate a whole era.
*
* The test will:
* 1. Read ports from ports.json
* 2. Connect to running RC and AH nodes via WebSocket
* 3. Find era start by searching backwards through block history
* 4. Collect and validate all events from era start to era end
*/

type Network = "kusama" | "polkadot";

interface Ports {
alice_port: number;
collator_port: number;
}

/**
* Read ports from zombie-bite ports.json file
*/
function readPorts(basePath: string): Ports {
const portsFile = join(basePath, "ports.json");

if (!existsSync(portsFile)) {
throw new Error(
`Ports file not found: ${portsFile}\n` +
`Make sure zombie-bite spawn is running in ${basePath}`,
);
}

try {
const portsData = JSON.parse(readFileSync(portsFile, "utf8"));
return {
alice_port: portsData.alice_port,
collator_port: portsData.collator_port,
};
} catch (error) {
throw new Error(`Failed to read ports.json: ${error}`);
}
}

/**
* Run era events test by connecting to zombie-bite spawned network
*
* @param basePath - Directory containing ports.json and zombie-bite spawn artifacts
* @param network - Network name (kusama or polkadot)
*/
export async function runEraEventsTest(
basePath: string,
network: Network = "kusama",
): Promise<boolean> {
logger.info("Starting era events test (zombie-bite spawn mode)", {
basePath,
network,
});

const ports = readPorts(basePath);
logger.info("Connecting to zombie-bite network", {
rc_port: ports.alice_port,
ah_port: ports.collator_port,
});

const rcProvider = new WsProvider(`ws://127.0.0.1:${ports.alice_port}`);
const rcApi = await ApiPromise.create({ provider: rcProvider });

const ahProvider = new WsProvider(`ws://127.0.0.1:${ports.collator_port}`);
const ahApi = await ApiPromise.create({ provider: ahProvider });

try {
logger.info("✅ Connected to both chains");

const context = {
pre: {
rc_api_before: rcApi,
ah_api_before: ahApi,
},
post: {
rc_api_after: rcApi,
ah_api_after: ahApi,
},
};

logger.info("Running era events validation...");
const pre_payload = await eraEventsTest.pre_check(context.pre);
await eraEventsTest.post_check(context.post, pre_payload);

logger.info(`✅ Era events test completed successfully`);
return true;
} catch (error) {
logger.error(`❌ Era events test failed:`, error);
return false;
} finally {
logger.info("Disconnecting from nodes...");
await rcApi.disconnect();
await ahApi.disconnect();
}
}
Loading