Files
universe/e2e/17-governance.spec.ts
zeekayandHanzo Dev 6842224772 test(e2e): dao onchain / vote / work / bounty / pars coverage
Regression-test the full DAO/governance/work-market surface across the live
orgs, read-only (no txs/writes/signing), asserting REAL functionality:

- 30-dao-onchain: BountyV1/ReputationV1/Governor/Safe on Zoo 200200 + Pars
  494949 via eth_call/eth_getCode — code present, bountyCount>=1, stateOf(0)==
  Paid(6), treasury()==org DAO Safe, ReputationV1.writer()==BountyV1 (wiring).
  Lux 96369: canonical CREATE2 Safe suite present, dead Governor asserted 0-byte
  (staged post-flag-day). Hanzo 36963 skip-gated until deploy lands.
- 31-vote-surfaces: lux/zoo/hanzo/pars.vote render + correct brand + ZERO cross-
  brand/competitor leakage + no white-screen (#root descendants + body text) +
  Connect Wallet present. reads-chain gated skip-until-live (Decent-fork shells).
- 32-work-boards: work.zoo.network LIVE badge (green, not FIXTURE), bounty in
  Paid column, on-chain ReputationV1 block, UI bounty count == chain bountyCount.
  work.{lux,hanzo,pars} skip-until-live.
- 33-bounty-bot: bounties.zoo.network webhook routed+POST-only (405), unsigned
  POST rejected (401), healthz internal-scoped (404 public correct). No secrets.
- 34-pars-chain: Pars 494949 chainId + monotonic height + explorer renders.

helpers.ts: additive ethCall/ethGetCode/hasCode/decodeAddress/encodeUint +
HANZO/PARS API domains + orgRpc() (one way to resolve any org's C-Chain RPC).
17-governance: drop dead unused SELECTORS block (on-chain contract asserts now
own their home in 30-dao-onchain). package.json: wire specs into test:full +
per-spec scripts.

36 active tests pass live, 8 skip-gated (ready to flip). Suite stays GREEN.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00

160 lines
6.7 KiB
TypeScript

import { test, expect } from '@playwright/test'
import {
getUrls,
rpcCall,
httpStatus, httpGetJson,
snap,
} from './helpers'
const urls = getUrls()
// ---------------------------------------------------------------------------
// Governance — DAO pages, proposal queries, staking-as-governance
// ---------------------------------------------------------------------------
// On-chain governance/DAO CONTRACT assertions (Bounty/Reputation/Governor/Safe
// via eth_call) live in 30-dao-onchain.spec.ts. This file covers the explorer
// discovery + staking-as-governance surface.
// ── Governance UI ──────────────────────────────────────────────────
test.describe('Governance — DAO Page', () => {
test('governance page loads if URL exists', async ({ request, page }) => {
const govPaths = [
`${urls.exchange}/governance`,
`${urls.exchange}/dao`,
`${urls.exchange}/vote`,
'https://dao.lux.network',
'https://vote.lux.network',
]
let found = false
for (const path of govPaths) {
const status = await httpStatus(request, path)
if (status === 200) {
const res = await page.goto(path, { timeout: 30_000, waitUntil: 'domcontentloaded' })
expect(res?.status()).toBe(200)
await page.waitForTimeout(3000)
const bodyLen = (await page.textContent('body'))?.length || 0
expect(bodyLen).toBeGreaterThan(0)
await snap(page, 'governance-dao')
found = true
console.log(` DAO page found: ${path}`)
break
}
}
if (!found) {
console.log(' No governance page found (governance UI may not be deployed yet)')
}
})
})
// ── Governance Contract Discovery ──────────────────────────────────
test.describe('Governance — Contract Discovery via Explorer', () => {
test('explorer search for governance contracts', async ({ request }) => {
const status = await httpStatus(
request,
`${urls.apiExploreLux}/search?q=governor`,
)
if (status === 200) {
const data = await httpGetJson(
request,
`${urls.apiExploreLux}/search?q=governor`,
) as { items?: { address: string; name: string; type: string }[] }
const count = data.items?.length ?? 0
console.log(` Governor contracts found: ${count}`)
if (data.items && data.items.length > 0) {
for (const item of data.items.slice(0, 3)) {
console.log(` ${item.name || 'unnamed'} (${item.type}) at ${item.address}`)
}
}
} else {
console.log(` Explorer search returned ${status}`)
}
// Explorer search endpoint should respond
expect([200, 404]).toContain(status)
})
test('explorer lists governance-related tokens', async ({ request }) => {
// Look for governance tokens (typically ERC-20 with "GOV" or "LUX" in name)
const data = await httpGetJson(
request,
`${urls.apiExploreLux}/tokens?q=governance&type=ERC-20`,
) as { items?: { address_hash?: string; address?: string; name: string; symbol: string }[] }
const count = data.items?.length ?? 0
console.log(` Governance tokens found: ${count}`)
if (data.items && data.items.length > 0) {
for (const token of data.items.slice(0, 5)) {
console.log(` ${token.name} (${token.symbol}) at ${token.address_hash || token.address}`)
}
}
expect(data).toBeDefined()
})
})
// ── On-Chain Governance Queries ────────────────────────────────────
test.describe('Governance — On-Chain State', () => {
test('can query ERC-20 token list for potential governance tokens', async ({ request }) => {
const data = await httpGetJson(
request,
`${urls.apiExploreLux}/tokens?type=ERC-20`,
) as { items?: { address_hash?: string; address?: string; name: string; symbol: string; total_supply: string }[] }
const count = data.items?.length ?? 0
console.log(` ERC-20 tokens indexed: ${count}`)
if (data.items && data.items.length > 0) {
for (const token of data.items.slice(0, 5)) {
console.log(` ${token.name} (${token.symbol}) supply=${token.total_supply} at ${token.address_hash || token.address}`)
}
}
expect(data).toBeDefined()
})
test('verified contracts list accessible', async ({ request }) => {
const data = await httpGetJson(
request,
`${urls.apiExploreLux}/smart-contracts`,
) as { items?: { address: { hash: string }; name: string; verified_at: string }[] }
const count = data.items?.length ?? 0
console.log(` Verified contracts: ${count}`)
if (data.items && data.items.length > 0) {
for (const contract of data.items.slice(0, 5)) {
console.log(` ${contract.name} at ${contract.address?.hash}`)
}
}
expect(data).toBeDefined()
})
})
// ── Staking as Governance ──────────────────────────────────────────
test.describe('Governance — Staking as Governance Mechanism', () => {
test('validator delegation fees represent governance parameters', async ({ request }) => {
// In Lux, staking IS governance — validator weights determine consensus power
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getCurrentValidators', { subnetID: '' },
) as { validators: { nodeID: string; weight: string; delegationFee: string }[] }
const validators = result.validators
expect(validators.length).toBeGreaterThan(0)
// Calculate total weight (voting power)
const totalWeight = validators.reduce((sum, v) => sum + BigInt(v.weight), 0n)
console.log(` Total network weight (governance power): ${totalWeight}`)
expect(totalWeight).toBeGreaterThan(0n)
// Check weight distribution (no single validator should have >33% weight)
for (const v of validators) {
const pct = (Number(BigInt(v.weight) * 10000n / totalWeight) / 100)
console.log(` ${v.nodeID}: ${pct.toFixed(2)}% weight`)
// Warning threshold: >33% is concerning for governance
if (pct > 33) {
console.log(` WARNING: ${v.nodeID} has ${pct.toFixed(2)}% of total weight`)
}
}
})
test('network has sufficient decentralization (>= 5 validators)', async ({ request }) => {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getCurrentValidators', { subnetID: '' },
) as { validators: { nodeID: string }[] }
const count = result.validators.length
console.log(` Validator count (decentralization metric): ${count}`)
expect(count).toBeGreaterThanOrEqual(5)
})
})