Files
5chan/src/components/settings-modal/p2p-stats-settings/__tests__/p2p-stats-settings.test.tsx
T
Tommaso Casaburi 3480dc0505 fix(p2p-stats): show peer flags for DNS6 relay hostnames
Extract embedded IPv6 addresses from dns6 multiaddrs so geo lookup and country flags work for relay peers that publish IPv6 via DNS hostnames.
2026-05-31 18:02:20 +07:00

945 lines
34 KiB
TypeScript

import * as React from 'react';
import { createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
const testState = vi.hoisted(() => ({
account: {} as Record<string, any>,
rpcSettings: { state: 'disconnected' } as Record<string, any>,
setAccountMock: vi.fn().mockResolvedValue(undefined),
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}));
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
setAccount: (account: unknown) => testState.setAccountMock(account),
useAccount: () => testState.account,
usePkcRpcSettings: () => testState.rpcSettings,
}));
let container: HTMLDivElement;
let root: Root;
const loadComponent = async (isElectron = false) => {
vi.resetModules();
window.electronApi = isElectron ? ({ isElectron: true } as Window['electronApi']) : undefined;
window.isElectron = isElectron;
return (await import('../p2p-stats-settings')).default;
};
const renderSettings = async (isElectron = false) => {
const P2PStatsSettings = await loadComponent(isElectron);
await act(async () => {
root.render(createElement(P2PStatsSettings));
await Promise.resolve();
});
};
const getStatRows = () =>
new Map(Array.from(container.querySelectorAll('tr')).map((row) => [row.children.item(0)?.textContent ?? '', row.children.item(1)?.textContent ?? '']));
const getMarkerByTitle = (title: string) => Array.from(container.querySelectorAll('svg rect')).find((rect) => rect.querySelector('title')?.textContent === title);
describe('P2PStatsSettings', () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
window.electronApi = undefined;
window.isElectron = false;
testState.account = {
id: 'account-1',
author: { address: 'author', wallets: {} },
pkcOptions: {
ipfsGatewayUrls: ['https://gateway.example'],
},
};
testState.rpcSettings = { state: 'disconnected' };
testState.setAccountMock.mockReset().mockResolvedValue(undefined);
// Default: own-IP endpoint lookups resolve offline so browser
// stats tests never hit the network. Individual tests can override this stub.
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ country: 'US', ip: '147.75.84.175' }),
}),
);
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => root.unmount());
container.remove();
vi.unstubAllGlobals();
});
it('renders browser libp2p stats from the active PKC client', async () => {
testState.account = {
...testState.account,
pkcOptions: {
httpRoutersOptions: ['https://router.example'],
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
heliaWithKuboRpcClientFunctions: {
add: async () => {
throw new Error("Helia 'add' is not supported at the moment in pkc-js API");
},
},
_helia: {
libp2p: {
getConnections: () => [
{
direction: 'outbound',
remoteAddr: { toString: () => '/ip4/127.0.0.1/tcp/4001/ws/p2p/peer-1' },
remotePeer: { toString: () => 'peer-1' },
status: 'open',
},
],
getMultiaddrs: () => ['/ip4/147.75.84.175/tcp/4001/ws'],
getPeers: () => ['peer-1', 'peer-2'],
metrics: {
toJSON: () => ({
helia_bitswap_data_received_bytes: { global: 2048, 'peer-1': 1536 },
helia_bitswap_sent_data_bytes: { global: 1024, 'peer-1': 512 },
}),
},
peerId: { toString: () => 'self-peer' },
services: {
pubsub: {
getPeers: () => ['peer-1'],
},
},
},
routing: {
routers: [
{
async provide() {
// noop
},
},
],
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => Promise.resolve());
const rows = getStatRows();
const connectedPeers = container.querySelector('[data-testid="connected-peers"]');
expect(container.textContent).toContain('Leeching');
expect(container.textContent).toContain('want to seed');
const seederLink = container.querySelector('a[href="https://github.com/bitsocialnet/bitsocial-seeder"]');
expect(seederLink).not.toBeNull();
expect(seederLink?.textContent).toBe('want to seed?');
expect(rows.get('Your IP')).toContain('147.75.84.175');
// Browser mode resolves "Your IP" from the browser's public endpoint, not
// from libp2p observed/WebRTC addresses.
expect(fetch).toHaveBeenCalledWith('https://api.ipify.org?format=json', expect.objectContaining({ signal: expect.any(AbortSignal) }));
const yourIpRow = Array.from(container.querySelectorAll('tr')).find((row) => row.textContent?.includes('Your IP'));
expect(yourIpRow?.querySelector('img[aria-label]')).not.toBeNull();
expect(container.textContent).not.toContain('browser Helia');
expect(container.textContent).not.toContain('seed mode');
expect(container.textContent).not.toContain('status');
expect(connectedPeers?.textContent).toContain('2 peers, 1 connection');
expect(connectedPeers?.textContent).toContain('WebSocket');
expect(connectedPeers?.textContent).toContain('Received 1.50 KB');
expect(connectedPeers?.textContent).toContain('Sent 512 B');
expect(connectedPeers?.textContent).not.toContain('outbound');
expect(connectedPeers?.textContent).not.toContain('open');
expect(connectedPeers?.textContent).toContain('/ip4/127.0.0.1/tcp/4001/ws/p2p/peer-1');
expect(rows.has('connections')).toBe(false);
expect(rows.has('Listen addresses')).toBe(false);
expect(rows.has('p2p_stats_updated')).toBe(true);
const tableRows = Array.from(container.querySelectorAll('tr'));
const rowTexts = tableRows.map((row) => row.textContent ?? '');
const dataSentIndex = rowTexts.findIndex((text) => text.includes('Data sent'));
const updatedIndex = rowTexts.findIndex((text) => text.includes('p2p_stats_updated'));
const connectedPeersIndex = rowTexts.findIndex((text) => text.includes('Connected peers'));
expect(dataSentIndex).toBeGreaterThanOrEqual(0);
expect(updatedIndex).toBeGreaterThan(dataSentIndex);
expect(connectedPeersIndex).toBeGreaterThan(updatedIndex);
expect(container.textContent).toContain('self-peer');
expect(container.textContent).toContain('Peer ID');
expect(container.textContent).toContain('Data received');
expect(container.textContent).toContain('2.00 KB');
expect(container.textContent).toContain('Data sent');
expect(container.textContent).toContain('1.00 KB');
expect(container.textContent).not.toContain('client key');
expect(container.textContent).not.toContain('pubsub peers');
expect(container.textContent).not.toContain('routers');
expect(container.textContent).not.toContain('pubsub topics');
expect(container.textContent).not.toContain('topic subscribers');
});
it('places peer map markers from city-level IP locations', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'https://free.freeipapi.com/api/json/91.234.199.189') {
return {
ok: true,
json: async () => ({
cityName: 'Haarlem',
countryCode: 'NL',
ipAddress: '91.234.199.189',
latitude: 52.3874,
longitude: 4.64622,
regionName: 'North Holland',
}),
};
}
return {
ok: true,
json: async () => ({ country: 'US', ip: '147.75.84.175' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
libp2p: {
getConnections: () => [
{
remoteAddr: { toString: () => '/ip4/91.234.199.189/tcp/4001/ws/p2p/peer-geo' },
remotePeer: { toString: () => 'peer-geo' },
},
],
getMultiaddrs: () => ['/ip4/147.75.84.175/tcp/4001/ws'],
getPeers: () => ['peer-geo'],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
const marker = getMarkerByTitle('peer-geo - Haarlem, North Holland, NL');
expect(marker).not.toBeNull();
expect(marker?.getAttribute('height')).toBe('3');
expect(marker?.getAttribute('width')).toBe('3');
expect(Number(marker?.getAttribute('x'))).toBeCloseTo(183.15, 1);
expect(Number(marker?.getAttribute('y'))).toBeCloseTo(36.11, 1);
expect(fetchMock).toHaveBeenCalledWith('https://free.freeipapi.com/api/json/91.234.199.189', expect.objectContaining({ signal: expect.any(AbortSignal) }));
});
it('shows peer country flags for DNS6 relay hostnames with embedded IPv6 addresses', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'https://free.freeipapi.com/api/json/2a11%3A6100%3A0%3A5e9f%3A%3A0') {
return {
ok: true,
json: async () => ({
cityName: 'Reykjavik',
countryCode: 'IS',
ipAddress: '2a11:6100:0:5e9f::0',
latitude: 64.1466,
longitude: -21.9426,
}),
};
}
return {
ok: true,
json: async () => ({ country: 'US', ip: '147.75.84.175' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
libp2p: {
getConnections: () => [
{
remoteAddr: {
toString: () => '/dns6/2a11-6100-0-5e9f--0.k51qzi5uqu5djg5pdoi9a98.example/tcp/443/ws/p2p/relay-peer/p2p-circuit/p2p/12D3KooWIPv6Peer',
},
remotePeer: { toString: () => '12D3KooWIPv6Peer' },
},
],
getMultiaddrs: () => ['/ip4/147.75.84.175/tcp/4001/ws'],
getPeers: () => ['12D3KooWIPv6Peer'],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
const connectedPeers = container.querySelector('[data-testid="connected-peers"]');
expect(connectedPeers?.textContent).toContain('WebSocket through relay');
expect(connectedPeers?.querySelector('img[aria-label="Iceland"]')).not.toBeNull();
expect(fetchMock).toHaveBeenCalledWith(
'https://free.freeipapi.com/api/json/2a11%3A6100%3A0%3A5e9f%3A%3A0',
expect.objectContaining({ signal: expect.any(AbortSignal) }),
);
});
it('shows the own IP flag and a red precise map marker when leeching', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'https://api.ipify.org?format=json') {
return {
ok: true,
json: async () => ({ ip: '117.2.120.113' }),
};
}
if (requestUrl === 'https://api.country.is/117.2.120.113') {
return {
ok: false,
json: async () => ({}),
};
}
if (requestUrl === 'https://free.freeipapi.com/api/json/117.2.120.113') {
return {
ok: true,
json: async () => ({
cityName: 'Da Nang',
countryCode: 'VN',
ipAddress: '117.2.120.113',
latitude: 16.0678,
longitude: 108.221,
regionName: 'Da Nang City',
}),
};
}
return {
ok: true,
json: async () => ({ country: 'US', ip: '147.75.84.175' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
libp2p: {
getConnections: () => [],
getMultiaddrs: () => ['/ip4/117.2.120.113/tcp/4001/ws'],
getPeers: () => [],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
const rows = getStatRows();
const yourIpRow = Array.from(container.querySelectorAll('tr')).find((row) => row.textContent?.includes('Your IP'));
const marker = getMarkerByTitle('Your node - Da Nang, Da Nang City, VN');
expect(container.textContent).toContain('Leeching');
expect(rows.get('Your IP')).toContain('117.2.120.113');
expect(yourIpRow?.querySelector('img[aria-label]')?.getAttribute('aria-label')).toBe('Vietnam');
expect(marker?.getAttribute('data-peer-role')).toBe('leecher');
expect(Number(marker?.getAttribute('x'))).toBeCloseTo(286.72, 1);
expect(Number(marker?.getAttribute('y'))).toBeCloseTo(72.43, 1);
});
it('keeps browser "Your IP" on the browser public endpoint when libp2p exposes a different public address', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'https://api.ipify.org?format=json') {
return {
ok: true,
json: async () => ({ ip: '104.28.68.171' }),
};
}
if (requestUrl === 'https://api.country.is/104.28.68.171') {
return {
ok: true,
json: async () => ({ country: 'VN', ip: '104.28.68.171' }),
};
}
if (requestUrl === 'https://free.freeipapi.com/api/json/104.28.68.171') {
return {
ok: true,
json: async () => ({
cityName: 'Toronto',
countryCode: 'CA',
ipAddress: '104.28.68.171',
latitude: 43.6532,
longitude: -79.3832,
regionName: 'Ontario',
}),
};
}
if (requestUrl === 'https://free.freeipapi.com/api/json/146.75.187.55') {
return {
ok: true,
json: async () => ({
cityName: 'Bandar Seri Begawan',
countryCode: 'BN',
ipAddress: '146.75.187.55',
latitude: 4.89234,
longitude: 114.942,
regionName: 'Brunei-Muara',
}),
};
}
return {
ok: true,
json: async () => ({ country: 'US', ip: '147.75.84.175' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
libp2p: {
getConnections: () => [
{
localAddr: { toString: () => '/ip4/146.75.187.55/udp/4001/webrtc-direct' },
},
],
getMultiaddrs: () => ['/ip4/146.75.187.55/udp/4001/webrtc-direct'],
getPeers: () => [],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
const rows = getStatRows();
const yourIpRow = Array.from(container.querySelectorAll('tr')).find((row) => row.textContent?.includes('Your IP'));
const marker = getMarkerByTitle('Your node - VN');
expect(rows.get('Your IP')).toContain('104.28.68.171');
expect(rows.get('Your IP')).not.toContain('146.75.187.55');
expect(yourIpRow?.querySelector('img[aria-label]')?.getAttribute('aria-label')).toBe('Vietnam');
expect(marker).not.toBeNull();
expect(container.querySelector('svg rect title')?.textContent).not.toContain('Toronto');
expect(fetchMock).not.toHaveBeenCalledWith('https://free.freeipapi.com/api/json/146.75.187.55', expect.anything());
});
it('reads browser transfer counters from Helia bitswap ledgers', async () => {
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
blockstore: {
child: {
blockBrokers: [
{
bitswap: {
peerWantLists: {
ledgerMap: new Map([['peer-1', { bytesReceived: 4096, bytesSent: 2048 }]]),
},
},
},
],
},
},
libp2p: {
getConnections: () => [
{
remoteAddr: { toString: () => '/ip4/91.234.199.189/tcp/4001/ws/p2p/peer-1' },
remotePeer: { toString: () => 'peer-1' },
},
],
getMultiaddrs: () => ['/ip4/147.75.84.175/tcp/4001/ws'],
getPeers: () => ['peer-1'],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => Promise.resolve());
expect(container.textContent).toContain('Data received');
expect(container.textContent).toContain('4.00 KB');
expect(container.textContent).toContain('Data sent');
expect(container.textContent).toContain('2.00 KB');
expect(container.querySelector('[data-testid="connected-peers"]')?.textContent).toContain('Received 4.00 KB');
expect(container.querySelector('[data-testid="connected-peers"]')?.textContent).toContain('Sent 2.00 KB');
});
it('starts browser transfer counters at zero when Helia exposes no byte totals yet', async () => {
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
libp2p: {
getConnections: () => [],
getMultiaddrs: () => ['/ip4/147.75.84.175/tcp/4001/ws'],
getPeers: () => [],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => Promise.resolve());
const rows = getStatRows();
expect(rows.get('Data received')).toBe('0 B');
expect(rows.get('Data sent')).toBe('0 B');
});
it('falls back to the browser node public endpoint when Helia exposes no public address', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'https://api.ipify.org?format=json') {
return {
ok: true,
json: async () => ({ ip: '104.28.68.171' }),
};
}
return {
ok: true,
json: async () => ({ country: 'VN', ip: '104.28.68.171' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
_helia: {
libp2p: {
getConnections: () => [
{
localAddr: { toString: () => '/ip4/127.0.0.1/tcp/4001/ws' },
remoteAddr: { toString: () => '/ip4/127.0.0.1/tcp/4001/ws/p2p/peer-1' },
remotePeer: { toString: () => 'peer-1' },
},
],
getMultiaddrs: () => [],
getPeers: () => [],
peerId: { toString: () => 'self-peer' },
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => Promise.resolve());
const rows = getStatRows();
expect(rows.get('Your IP')).toContain('104.28.68.171');
expect(rows.get('Your IP')).not.toContain('unknown');
const yourIpRow = Array.from(container.querySelectorAll('tr')).find((row) => row.textContent?.includes('Your IP'));
expect(yourIpRow?.querySelector('img[aria-label]')).not.toBeNull();
expect(fetch).toHaveBeenCalledWith('https://api.ipify.org?format=json', expect.objectContaining({ signal: expect.any(AbortSignal) }));
});
it('reports seeding only when browser Helia can add and publish provider records', async () => {
testState.account = {
...testState.account,
pkcOptions: {
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
},
pkc: {
clients: {
libp2pJsClients: {
libp2pjs: {
key: 'libp2pjs',
heliaWithKuboRpcClientFunctions: {
add: async () => ({ cid: 'cid' }),
},
_helia: {
libp2p: {
getConnections: () => [],
getMultiaddrs: () => ['/ip4/147.75.84.175/tcp/4001/ws'],
getPeers: () => [],
peerId: { toString: () => 'self-peer' },
},
routing: {
routers: [
{
provide: async (cid: unknown) => cid,
},
],
},
},
},
},
},
},
};
await renderSettings(false);
await act(async () => Promise.resolve());
expect(container.textContent).toContain('Seeding');
expect(container.querySelector('a[href="https://github.com/bitsocialnet/bitsocial-seeder"]')).toBeNull();
expect(container.textContent).not.toContain('seed mode');
});
it('renders browser full-node RPC stats with seeding mode and leecher peer markers', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'https://free.freeipapi.com/api/json/147.75.84.175') {
return {
ok: true,
json: async () => ({
cityName: 'New York',
countryCode: 'US',
latitude: 40.7128,
longitude: -74.006,
regionName: 'New York',
}),
};
}
if (requestUrl === 'https://free.freeipapi.com/api/json/91.234.199.189') {
return {
ok: true,
json: async () => ({
cityName: 'Haarlem',
countryCode: 'NL',
latitude: 52.3874,
longitude: 4.64622,
regionName: 'North Holland',
}),
};
}
if (requestUrl === 'https://free.freeipapi.com/api/json/117.2.120.113') {
return {
ok: true,
json: async () => ({
cityName: 'Da Nang',
countryCode: 'VN',
latitude: 16.0678,
longitude: 108.221,
regionName: 'Da Nang City',
}),
};
}
return {
ok: true,
json: async () => ({ country: 'US', ip: '147.75.84.175' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.rpcSettings = { state: 'connected' };
testState.account = {
...testState.account,
pkcOptions: {
pkcRpcClientsOptions: ['ws://147.75.84.175:9138'],
},
pkc: {
clients: {
pkcRpcClients: {
'ws://147.75.84.175:9138': {
getPeers: vi.fn().mockResolvedValue({
peers: [
{
address: '/ip4/91.234.199.189/tcp/4001',
bandwidth: { TotalIn: 256, TotalOut: 512 },
listenAddress: '/ip4/91.234.199.189/tcp/4001',
peerId: 'seed-peer',
},
{
address: '/ip4/117.2.120.113/tcp/4001',
bandwidth: { TotalIn: 128, TotalOut: 64 },
listenAddress: '',
peerId: 'leech-peer',
},
],
}),
getStats: vi.fn().mockResolvedValue({
bandwidth: { TotalIn: 1024, TotalOut: 2048 },
identity: {
AgentVersion: 'kubo/full-node',
ID: 'full-node-peer',
Addresses: ['/ip4/147.75.84.175/tcp/4001'],
},
}),
state: 'connected',
},
},
},
},
};
await renderSettings(false);
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
const rows = getStatRows();
const markers = Array.from(container.querySelectorAll('svg rect'));
const ownMarker = markers.find((marker) => marker.querySelector('title')?.textContent?.startsWith('Your node'));
const leecherMarker = markers.find((marker) => marker.querySelector('title')?.textContent?.startsWith('leech-peer'));
expect(rows.get('Mode')).toBe('Seeding');
expect(rows.get('PKC RPC')).toBe('connected');
expect(rows.get('Peer ID')).toBe('full-node-peer');
expect(rows.get('Your IP')).toContain('147.75.84.175');
expect(rows.get('Data received')).toBe('1.00 KB');
expect(rows.get('Data sent')).toBe('2.00 KB');
expect(container.textContent).toContain('seed-peer');
expect(container.textContent).toContain('leech-peer');
expect(container.querySelector('[data-testid="connected-peers"]')?.textContent).toContain('Received 256 B');
expect(container.querySelector('[data-testid="connected-peers"]')?.textContent).toContain('Sent 64 B');
expect(container.textContent).toContain('Leeching');
expect(container.querySelector('a[href="https://github.com/bitsocialnet/bitsocial-seeder"]')).toBeNull();
expect(ownMarker?.getAttribute('data-peer-role')).toBe('seeder');
expect(leecherMarker?.getAttribute('data-peer-role')).toBe('leecher');
});
it('does not resolve full-node RPC hostnames through external DNS', async () => {
const fetchMock = vi.fn();
vi.stubGlobal('fetch', fetchMock);
testState.rpcSettings = { state: 'connected' };
testState.account = {
...testState.account,
pkcOptions: {
pkcRpcClientsOptions: ['ws://node.example:9138'],
},
pkc: {
clients: {
pkcRpcClients: {
'ws://node.example:9138': {
getPeers: vi.fn().mockResolvedValue({ peers: [] }),
getStats: vi.fn().mockResolvedValue({
bandwidth: { TotalIn: 0, TotalOut: 0 },
identity: {
ID: 'hostname-rpc-node',
},
}),
state: 'connected',
},
},
},
},
};
await renderSettings(false);
await act(async () => Promise.resolve());
const rows = getStatRows();
expect(rows.get('Mode')).toBe('Seeding');
expect(rows.get('Peer ID')).toBe('hostname-rpc-node');
expect(rows.get('Your IP')).toBe('unavailable');
expect(fetchMock).not.toHaveBeenCalled();
});
it('renders Electron Kubo stats as seeding with the node location on the map', async () => {
const fetchMock = vi.fn(async (url: string | URL | Request) => {
const requestUrl = String(url);
if (requestUrl === 'http://localhost:50019/api/v0/id') {
return {
ok: true,
text: async () =>
JSON.stringify({
Addresses: [
'/dns4/77-168-54-121.example/tcp/4001/tls/ws/p2p/relay-peer/p2p-circuit/p2p/desktop-kubo-peer',
'/ip4/117.2.120.113/udp/4001/webrtc-direct/p2p/desktop-kubo-peer',
],
AgentVersion: 'kubo/0.41.0',
ID: 'desktop-kubo-peer',
}),
};
}
if (requestUrl === 'http://localhost:50019/api/v0/swarm/peers?direction=true&latency=true&streams=true') {
return {
ok: true,
text: async () =>
JSON.stringify({
Peers: [
{
Addr: '/ip4/91.234.199.189/tcp/4001',
Direction: 'outbound',
ListenAddress: '/ip4/91.234.199.189/tcp/4001',
Peer: 'kubo-seeder',
},
{
Addr: '/ip4/203.0.113.10/tcp/4001',
Direction: 'inbound',
ListenAddress: '',
Peer: 'kubo-leecher',
},
],
}),
};
}
if (requestUrl === 'http://localhost:50019/api/v0/stats/bw') {
return { ok: true, text: async () => JSON.stringify({ RateIn: 12, RateOut: 34, TotalIn: 4096, TotalOut: 8192 }) };
}
if (requestUrl === 'https://free.freeipapi.com/api/json/117.2.120.113') {
return {
ok: true,
json: async () => ({
cityName: 'Da Nang',
countryCode: 'VN',
latitude: 16.0678,
longitude: 108.221,
regionName: 'Da Nang City',
}),
};
}
if (requestUrl === 'https://free.freeipapi.com/api/json/91.234.199.189') {
return {
ok: true,
json: async () => ({
cityName: 'Haarlem',
countryCode: 'NL',
latitude: 52.3874,
longitude: 4.64622,
regionName: 'North Holland',
}),
};
}
return {
ok: true,
json: async () => ({ country: 'VN', ip: '117.2.120.113' }),
};
});
vi.stubGlobal('fetch', fetchMock);
testState.rpcSettings = { state: 'connected' };
testState.account = {
...testState.account,
pkcOptions: {
pkcRpcClientsOptions: ['ws://localhost:9138'],
},
};
await renderSettings(true);
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
const rows = getStatRows();
const markers = Array.from(container.querySelectorAll('svg rect'));
const ownMarker = markers.find((marker) => marker.querySelector('title')?.textContent?.startsWith('Your node'));
const leecherMarker = markers.find((marker) => marker.querySelector('title')?.textContent?.startsWith('kubo-leecher'));
expect(rows.get('Mode')).toBe('Seeding');
expect(rows.get('Kubo RPC')).toBe('http://localhost:50019/api/v0');
expect(rows.get('Peer ID')).toBe('desktop-kubo-peer');
expect(rows.get('Your IP')).toContain('117.2.120.113');
expect(rows.get('Your IP')).not.toContain('77.168.54.121');
expect(rows.get('Data received')).toBe('4.00 KB');
expect(rows.get('Data sent')).toBe('8.00 KB');
expect(rows.has('PKC RPC')).toBe(false);
expect(rows.has('Agent')).toBe(false);
expect(rows.has('Repo size')).toBe(false);
expect(rows.has('Repo objects')).toBe(false);
expect(rows.has('Bitswap peers')).toBe(false);
expect(rows.has('Bitswap wantlist')).toBe(false);
expect(rows.has('Bandwidth in')).toBe(false);
expect(rows.has('Bandwidth out')).toBe(false);
expect(container.textContent).toContain('kubo-seeder');
expect(container.textContent).toContain('kubo-leecher');
expect(container.textContent).toContain('Leeching');
expect(container.textContent).not.toContain('Listen addresses');
expect(ownMarker?.getAttribute('data-peer-role')).toBe('seeder');
expect(leecherMarker?.getAttribute('data-peer-role')).toBe('leecher');
expect(fetchMock).not.toHaveBeenCalledWith('http://localhost:50019/api/v0/repo/stat', expect.anything());
expect(fetchMock).not.toHaveBeenCalledWith('http://localhost:50019/api/v0/bitswap/stat', expect.anything());
expect(fetchMock).not.toHaveBeenCalledWith('http://localhost:50019/api/v0/version', expect.anything());
expect(fetchMock).not.toHaveBeenCalledWith('http://localhost:50019/api/v0/swarm/addrs/local', expect.anything());
});
});