Files
universe/e2e/16-app-chains.spec.ts
zeekayandHanzo Dev 89e2274b46 Point the gateway and the scripts at the segment luxd serves
The route table lives in configs/gateway/*.json, where each endpoint names both
the public path and the url_pattern sent upstream. Both moved: luxd answers
under /v1/chain and nothing translates between them.

The operational scripts went with it — Makefiles, the roll and validator
helpers, the e2e specs and the market-maker config all composed /v1/bc
addresses that now reach nothing. A test that dials a dead path fails in a way
that looks like the chain is down.

Lines that record an observation keep their original spelling.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-30 17:47:43 -07:00

214 lines
8.1 KiB
TypeScript

import { test, expect } from '@playwright/test'
import {
getUrls, getChainIds, isLocal,
rpcCall, ethBlockNumber, ethChainId,
httpStatus, httpGetJson,
} from './helpers'
import { EXPLORERS } from './env'
const urls = getUrls()
const chainIds = getChainIds()
// ---------------------------------------------------------------------------
// App Chain Health — verify each app chain C-Chain is alive and producing
// ---------------------------------------------------------------------------
// App chain RPC endpoints (production)
const APPCHAIN_RPCS = isLocal()
? {
zoo: urls.rpcZoo,
}
: {
zoo: 'https://api.zoo.network/v1/chain/C/rpc',
hanzo: 'https://api.hanzo.network/v1/chain/C/rpc',
spc: 'https://api.spc.network/v1/chain/C/rpc',
pars: 'https://api.pars.network/v1/chain/C/rpc',
}
// ── Zoo C-Chain ────────────────────────────────────────────────────
test.describe('App Chains — Zoo', () => {
test('Zoo C-Chain RPC responds', async ({ request }) => {
let block = 0
for (let attempt = 0; attempt < 3; attempt++) {
try {
block = await ethBlockNumber(request, APPCHAIN_RPCS.zoo)
break
} catch {
if (attempt < 2) await new Promise(r => setTimeout(r, 1000))
}
}
console.log(` Zoo C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
})
test('Zoo C-Chain returns correct chain ID', async ({ request }) => {
try {
const cid = await ethChainId(request, APPCHAIN_RPCS.zoo)
console.log(` Zoo chain ID: ${cid} (expected: ${chainIds.zoo})`)
expect(cid).toBe(chainIds.zoo)
} catch {
console.log(' Zoo chain ID query failed (RPC may be throttled)')
}
})
test('Zoo C-Chain is producing blocks', async ({ request }) => {
let block1 = 0
let block2 = 0
for (let attempt = 0; attempt < 3; attempt++) {
try {
block1 = await ethBlockNumber(request, APPCHAIN_RPCS.zoo)
break
} catch {
if (attempt < 2) await new Promise(r => setTimeout(r, 1000))
}
}
await new Promise(r => setTimeout(r, 3000))
for (let attempt = 0; attempt < 3; attempt++) {
try {
block2 = await ethBlockNumber(request, APPCHAIN_RPCS.zoo)
break
} catch {
if (attempt < 2) await new Promise(r => setTimeout(r, 1000))
}
}
console.log(` Zoo blocks: ${block1} -> ${block2}`)
expect(block2).toBeGreaterThanOrEqual(block1)
})
})
// ── Hanzo C-Chain ──────────────────────────────────────────────────
test.describe('App Chains — Hanzo', () => {
test('Hanzo C-Chain RPC responds', async ({ request }) => {
if (!('hanzo' in APPCHAIN_RPCS)) {
test.skip(true, 'Hanzo RPC not configured in local mode')
}
const rpc = (APPCHAIN_RPCS as Record<string, string>).hanzo
try {
const block = await ethBlockNumber(request, rpc)
console.log(` Hanzo C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
} catch {
console.log(' Hanzo C-Chain RPC not reachable (chain may not be deployed yet)')
}
})
test('Hanzo C-Chain returns correct chain ID', async ({ request }) => {
if (!('hanzo' in APPCHAIN_RPCS)) {
test.skip(true, 'Hanzo RPC not configured in local mode')
}
const rpc = (APPCHAIN_RPCS as Record<string, string>).hanzo
try {
const cid = await ethChainId(request, rpc)
console.log(` Hanzo chain ID: ${cid} (expected: ${chainIds.hanzo})`)
expect(cid).toBe(chainIds.hanzo)
} catch {
console.log(' Hanzo chain ID query failed')
}
})
})
// ── SPC C-Chain ────────────────────────────────────────────────────
test.describe('App Chains — SPC', () => {
test('SPC C-Chain RPC responds', async ({ request }) => {
if (!('spc' in APPCHAIN_RPCS)) {
test.skip(true, 'SPC RPC not configured in local mode')
}
const rpc = (APPCHAIN_RPCS as Record<string, string>).spc
try {
const block = await ethBlockNumber(request, rpc)
console.log(` SPC C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
} catch {
console.log(' SPC C-Chain RPC not reachable (chain may not be deployed yet)')
}
})
test('SPC C-Chain returns correct chain ID', async ({ request }) => {
if (!('spc' in APPCHAIN_RPCS)) {
test.skip(true, 'SPC RPC not configured in local mode')
}
const rpc = (APPCHAIN_RPCS as Record<string, string>).spc
try {
const cid = await ethChainId(request, rpc)
console.log(` SPC chain ID: ${cid} (expected: ${chainIds.spc})`)
expect(cid).toBe(chainIds.spc)
} catch {
console.log(' SPC chain ID query failed')
}
})
})
// ── Pars C-Chain ───────────────────────────────────────────────────
test.describe('App Chains — Pars', () => {
test('Pars C-Chain RPC responds', async ({ request }) => {
if (!('pars' in APPCHAIN_RPCS)) {
test.skip(true, 'Pars RPC not configured in local mode')
}
const rpc = (APPCHAIN_RPCS as Record<string, string>).pars
try {
const block = await ethBlockNumber(request, rpc)
console.log(` Pars C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
} catch {
console.log(' Pars C-Chain RPC not reachable (chain may not be deployed yet)')
}
})
test('Pars C-Chain returns correct chain ID', async ({ request }) => {
if (!('pars' in APPCHAIN_RPCS)) {
test.skip(true, 'Pars RPC not configured in local mode')
}
const rpc = (APPCHAIN_RPCS as Record<string, string>).pars
try {
const cid = await ethChainId(request, rpc)
console.log(` Pars chain ID: ${cid} (expected: ${chainIds.pars})`)
expect(cid).toBe(chainIds.pars)
} catch {
console.log(' Pars chain ID query failed')
}
})
})
// ── Cross-Chain Block Production ──────────────────────────────────
test.describe('App Chains — Cross-Chain Block Comparison', () => {
test('all app chain explorers report blocks', async ({ request }) => {
for (const { name, api } of EXPLORERS.filter((e) => e.org !== 'lux')) {
try {
const data = await httpGetJson(request, `${api}/stats`) as Record<string, unknown>
const blocks = Number(data.total_blocks || 0)
const txs = Number(data.total_transactions || 0)
console.log(` ${name}: ${blocks} blocks, ${txs} txs`)
expect(blocks).toBeGreaterThan(0)
} catch {
console.log(` ${name}: explorer not reachable`)
}
}
})
})
// ── Registered Blockchains ─────────────────────────────────────────
test.describe('App Chains — Registered Blockchains', () => {
test('P-Chain lists all expected chains', async ({ request }) => {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getBlockchains', {},
) as { blockchains: { name: string; id: string; netID: string; vmID: string }[] }
const chains = result.blockchains
console.log(` Registered blockchains: ${chains.length}`)
for (const chain of chains) {
console.log(` ${chain.name}: id=${chain.id} net=${chain.netID}`)
}
// At minimum: C-Chain, X-Chain, and D (P-Chain internal)
expect(chains.length).toBeGreaterThanOrEqual(3)
})
test('blockchains have unique IDs', async ({ request }) => {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getBlockchains', {},
) as { blockchains: { id: string }[] }
const ids = result.blockchains.map(c => c.id)
const unique = new Set(ids)
expect(unique.size).toBe(ids.length)
console.log(` All ${ids.length} blockchain IDs are unique`)
})
})