Files
explore/configs/app/chainRegistry.spec.ts
zeekayandClaude Opus 5.5 80bd5d7c6a Zoo's explorer counts no Lux chains and links no private repo
The Zoo stats panel counted the Lux primary network's chains, a list its
explorer does not show; the chain count now appears only on the Lux
explorer. /ai takes the primary-network guard like /bridge and /dex. Zoo's
source repo is private, so its footer prints the version as text rather
than a link every visitor gets as a 404. A white-label host has no source
repo either, so its branding carries an empty sourceUrl instead of none,
which printed "undefined/tree/<version>".

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-23 20:11:57 -07:00

137 lines
5.8 KiB
TypeScript

import { describe, expect, it, beforeEach } from 'vitest';
import {
getChainsForNetwork,
getCurrentChain,
getCurrentNetwork,
getPChain,
isPrimaryNetworkExplorer,
isWhiteLabelMode,
} from './chainRegistry';
// Drives the registry off a simulated request host, exactly like the browser
// (getHostname() reads window.location.hostname). window.__envs is emptied so
// no NEXT_PUBLIC_* override leaks in — we assert the bundled defaults.
function atHost(hostname: string) {
// Minimal browser global so isBrowser() is true and getHostname() reads the
// simulated request host — no jsdom needed.
(globalThis as unknown as { window: unknown }).window = { location: { hostname }, __envs: {} };
}
// Mirrors the pure logic of ui/snippets/topBar/ChainSwitcher.tsx: the lux
// primary (C-Chain) host shows every chain; a brand explorer shows only chains
// sharing its brand. Returns the chain names the switcher would render.
function switcherChainNames(): Array<string> {
const current = getCurrentChain();
const all = getChainsForNetwork(getCurrentNetwork().network);
const isMain = current.name === 'C-Chain';
return (isMain ? all : all.filter((c) => c.branding.brandName === current.branding.brandName)).map((c) => c.name);
}
describe('chain-visibility rule', () => {
beforeEach(() => atHost('localhost'));
describe('Lux primary explorer (explore.lux.network) = UNIVERSAL (shows everything)', () => {
beforeEach(() => atHost('explore.lux.network'));
it('is the primary-network explorer', () => {
expect(isWhiteLabelMode()).toBe(false);
expect(isPrimaryNetworkExplorer()).toBe(true);
expect(getCurrentChain().name).toBe('C-Chain');
});
// The switcher lists the chains that have an explorer to switch to, and
// no others: every entry is a link, and a link to a name that does not
// resolve is a dead end on every page.
it('lists exactly the mainnet chains with an explorer', () => {
expect(switcherChainNames()).toEqual([ 'C-Chain', 'Zoo', 'Hanzo' ]);
});
});
describe('Brand explorers = OWN-ONLY (no lux primary, no other orgs)', () => {
const cases = [
{ host: 'explore.hanzo.network', name: 'Hanzo', brandName: 'Hanzo AI' },
{ host: 'explore.zoo.network', name: 'Zoo', brandName: 'Zoo Chain' },
];
it.each(cases)('$host is a registered L2, NOT the primary explorer (no leak)', ({ host, brandName }) => {
atHost(host);
expect(isWhiteLabelMode()).toBe(false);
expect(isPrimaryNetworkExplorer()).toBe(false);
expect(getCurrentChain().vm).toBe('L2');
expect(getCurrentChain().branding.brandName).toBe(brandName);
});
it.each(cases)('$host switcher shows ONLY its own chain (no lux-primary, no other orgs)', ({ host, name }) => {
atHost(host);
expect(switcherChainNames()).toEqual([ name ]);
});
});
// The footer renders branding.sourceUrl and branding.orgName verbatim, so a
// foreign org in either is a brand leak a reader can see. It shipped once as a
// hardcoded github.com/luxfi/explore version link on every brand's footer.
describe('a brand host never surfaces another org', () => {
const foreign: Record<string, Array<string>> = {
'explore.hanzo.ai': [ 'luxfi', 'lux.network', 'zoo', 'pars' ],
'explore.hanzo.network': [ 'luxfi', 'lux.network', 'zoo', 'pars' ],
'explore.zoo.network': [ 'luxfi', 'lux.network', 'hanzo', 'pars' ],
'explore.zoo-test.network': [ 'luxfi', 'lux.network', 'hanzo', 'pars' ],
};
it.each(Object.entries(foreign))('%s footer names only its own org', (host, others) => {
atHost(host);
const { orgName, sourceUrl, websiteUrl, githubUrl } = getCurrentChain().branding;
const rendered = [ orgName, sourceUrl, websiteUrl, githubUrl ].join(' ').toLowerCase();
others.forEach((org) => expect(rendered).not.toContain(org));
});
it('lux keeps its own source link', () => {
atHost('explore.lux.network');
expect(getCurrentChain().branding.sourceUrl).toBe('https://github.com/luxfi/explore');
});
// zooai/explore is private: a version link there is a 404 for every visitor.
it('zoo has no source link to a repo visitors cannot open', () => {
atHost('explore.zoo.network');
expect(getCurrentChain().branding.sourceUrl).toBe('');
});
it('hanzo carries the legal entity used across hanzo surfaces', () => {
atHost('explore.hanzo.ai');
expect(getCurrentChain().branding.orgName).toBe('Hanzo AI, Inc.');
});
});
// Which P-Chain secures each chain. Zoo is an L2 carried by Lux's validator
// set, so it reads Lux's, never the P-Chain of a Zoo node. Hanzo runs its own
// committee until hanzod joins as an L2, so it has none.
describe('P-Chain', () => {
it.each([
[ 'explore.lux.network', 'https://api.lux.network' ],
[ 'explore.zoo.network', 'https://api.lux.network' ],
[ 'explore.zoo.ngo', 'https://api.lux.network' ],
[ 'explore.lux-test.network', 'https://api.lux-test.network' ],
[ 'explore.zoo-test.network', 'https://api.lux-test.network' ],
])('%s is secured by the P-Chain at %s', (host, url) => {
atHost(host);
expect(getPChain()).toEqual({ url, name: 'Lux primary network', symbol: 'LUX' });
expect(getPChain(host)?.url).toBe(url);
});
it.each([ 'explore.hanzo.network', 'explore.hanzo.ai', 'explore-hanzo.lux.network' ])('%s has none', (host) => {
atHost(host);
expect(getPChain()).toBeUndefined();
expect(getPChain(host)).toBeUndefined();
});
});
describe('keystone: unregistered white-label hosts are NEVER the primary explorer', () => {
it('does not leak the lux primary network to an unknown host', () => {
atHost('explore.unknown.example');
expect(isWhiteLabelMode()).toBe(true);
expect(isPrimaryNetworkExplorer()).toBe(false);
});
});
});