mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-10 12:40:34 +02:00
Ship ShadowBroker v0.9.83 with live Infonet gate messaging and DM protocols.
Gate hashchain replication, Tor/SOCKS transport hardening, terminal session teardown, v0.9.83 UI/changelog, and release digest pins for seamless updater verification.
This commit is contained in:
@@ -9,12 +9,12 @@ import {
|
||||
} from '@/lib/updateRuntime';
|
||||
|
||||
const RELEASE: GitHubLatestRelease = {
|
||||
html_url: 'https://github.com/BigBodyCobain/Shadowbroker/releases/tag/v0.9.82',
|
||||
html_url: 'https://github.com/BigBodyCobain/Shadowbroker/releases/tag/v0.9.83',
|
||||
assets: [
|
||||
{ name: 'ShadowBroker_0.9.82_x64_en-US.msi', browser_download_url: 'https://example.test/windows.msi' },
|
||||
{ name: 'ShadowBroker_0.9.82_x64-setup.exe', browser_download_url: 'https://example.test/windows-setup.exe' },
|
||||
{ name: 'ShadowBroker_0.9.82_aarch64.dmg', browser_download_url: 'https://example.test/macos.dmg' },
|
||||
{ name: 'ShadowBroker_0.9.82_amd64.AppImage', browser_download_url: 'https://example.test/linux.AppImage' },
|
||||
{ name: 'ShadowBroker_0.9.83_x64_en-US.msi', browser_download_url: 'https://example.test/windows.msi' },
|
||||
{ name: 'ShadowBroker_0.9.83_x64-setup.exe', browser_download_url: 'https://example.test/windows-setup.exe' },
|
||||
{ name: 'ShadowBroker_0.9.83_aarch64.dmg', browser_download_url: 'https://example.test/macos.dmg' },
|
||||
{ name: 'ShadowBroker_0.9.83_amd64.AppImage', browser_download_url: 'https://example.test/linux.AppImage' },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@ const controlPlaneFetch = vi.fn();
|
||||
vi.mock('@/mesh/wormholeIdentityClient', () => ({
|
||||
fetchWormholeStatus,
|
||||
prepareWormholeInteractiveLane,
|
||||
isWormholePrepAbortedError: (error: unknown) =>
|
||||
error instanceof Error && error.message === 'wormhole_prep_aborted',
|
||||
}));
|
||||
|
||||
vi.mock('@/mesh/wormholeClient', () => ({
|
||||
@@ -76,6 +78,7 @@ vi.mock('@/mesh/controlPlaneStatusClient', () => ({
|
||||
vi.mock('@/lib/meshTerminalLauncher', () => ({
|
||||
requestMeshTerminalOpen,
|
||||
subscribeSecureMeshTerminalLauncherOpen,
|
||||
subscribeInfonetSessionEnd: vi.fn(() => () => {}),
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/updateRuntime', () => ({
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
/**
|
||||
* Sprint 4D behavioral tests — page.tsx wormhole teardown and layer sync.
|
||||
*
|
||||
* These tests exercise actual runtime logic:
|
||||
* 1. teardownWormholeOnClose — calls leaveWormhole only when state is ready or running
|
||||
* 2. Layer sync first-mount suppression — initial sync does NOT dispatch LAYER_TOGGLE_EVENT
|
||||
*/
|
||||
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
@@ -11,80 +7,29 @@ import path from 'node:path';
|
||||
import { teardownWormholeOnClose } from '@/lib/wormholeTeardown';
|
||||
import { LAYER_TOGGLE_EVENT } from '@/hooks/useDataPolling';
|
||||
|
||||
// ─── teardownWormholeOnClose ──────────────────────────────────────────────
|
||||
const endInfonetTerminalSession = vi.fn(async () => {});
|
||||
|
||||
vi.mock('@/lib/infonetTerminalSession', () => ({
|
||||
endInfonetTerminalSession: (...args: unknown[]) => endInfonetTerminalSession(...args),
|
||||
}));
|
||||
|
||||
describe('page.tsx behavior — teardownWormholeOnClose', () => {
|
||||
let fetchState: ReturnType<typeof vi.fn>;
|
||||
let leave: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchState = vi.fn();
|
||||
leave = vi.fn().mockResolvedValue({});
|
||||
endInfonetTerminalSession.mockClear();
|
||||
});
|
||||
|
||||
it('calls leaveWormhole when state is ready', async () => {
|
||||
fetchState.mockResolvedValue({ ready: true, running: false });
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(fetchState).toHaveBeenCalledWith(false);
|
||||
expect(leave).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls leaveWormhole when state is running', async () => {
|
||||
fetchState.mockResolvedValue({ ready: false, running: true });
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(leave).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls leaveWormhole when state is both ready and running', async () => {
|
||||
fetchState.mockResolvedValue({ ready: true, running: true });
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(leave).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does NOT call leaveWormhole when state is neither ready nor running', async () => {
|
||||
fetchState.mockResolvedValue({ ready: false, running: false });
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(fetchState).toHaveBeenCalledWith(false);
|
||||
expect(leave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does NOT call leaveWormhole when state is null', async () => {
|
||||
fetchState.mockResolvedValue(null);
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(leave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('swallows fetchState errors gracefully', async () => {
|
||||
fetchState.mockRejectedValue(new Error('network down'));
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(leave).not.toHaveBeenCalled();
|
||||
// No error thrown — handler is best-effort
|
||||
});
|
||||
|
||||
it('swallows leaveWormhole errors gracefully', async () => {
|
||||
fetchState.mockResolvedValue({ ready: true });
|
||||
leave.mockRejectedValue(new Error('leave failed'));
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
// No error thrown — handler is best-effort
|
||||
});
|
||||
|
||||
it('always passes force=false to fetchState', async () => {
|
||||
fetchState.mockResolvedValue({ ready: true });
|
||||
await teardownWormholeOnClose(fetchState, leave);
|
||||
expect(fetchState).toHaveBeenCalledWith(false);
|
||||
expect(fetchState).not.toHaveBeenCalledWith(true);
|
||||
it('ends the infonet terminal session on close', async () => {
|
||||
await teardownWormholeOnClose();
|
||||
expect(endInfonetTerminalSession).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Layer sync first-mount suppression ───────────────────────────────────
|
||||
|
||||
describe('page.tsx behavior — layer sync first-mount suppression', () => {
|
||||
it('LAYER_TOGGLE_EVENT is the expected string constant', () => {
|
||||
expect(LAYER_TOGGLE_EVENT).toBe('sb:layer-toggle');
|
||||
});
|
||||
|
||||
it('first-mount ref pattern suppresses dispatch, subsequent calls dispatch', () => {
|
||||
// Simulate the initialLayerSyncRef pattern from page.tsx
|
||||
const initialSyncDone = { current: false };
|
||||
const dispatched: boolean[] = [];
|
||||
|
||||
@@ -96,7 +41,6 @@ describe('page.tsx behavior — layer sync first-mount suppression', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// First call (mount): should pass false → no dispatch
|
||||
if (!initialSyncDone.current) {
|
||||
initialSyncDone.current = true;
|
||||
syncLayers(false);
|
||||
@@ -105,7 +49,6 @@ describe('page.tsx behavior — layer sync first-mount suppression', () => {
|
||||
}
|
||||
expect(dispatched).toEqual([false]);
|
||||
|
||||
// Second call (layer change): should pass true → dispatch
|
||||
if (!initialSyncDone.current) {
|
||||
initialSyncDone.current = true;
|
||||
syncLayers(false);
|
||||
@@ -114,7 +57,6 @@ describe('page.tsx behavior — layer sync first-mount suppression', () => {
|
||||
}
|
||||
expect(dispatched).toEqual([false, true]);
|
||||
|
||||
// Third call (another layer change): should still dispatch
|
||||
if (!initialSyncDone.current) {
|
||||
initialSyncDone.current = true;
|
||||
syncLayers(false);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Sprint 4B regression tests — page.tsx decomposition boundary checks.
|
||||
*
|
||||
* These tests validate the frozen contract for page.tsx decomposition:
|
||||
* 1. InfonetTerminal onClose still calls leaveWormhole when wormhole is ready/running
|
||||
* 1. InfonetTerminal onClose ends the terminal session (wormhole + node + tor)
|
||||
* 2. Initial /api/layers sync does NOT dispatch LAYER_TOGGLE_EVENT on first mount
|
||||
* 3. launchMeshChatTab preserves atomic leftOpen + leftMeshExpanded + meshChatLaunchRequest
|
||||
* 4. LocateBar extracted to page-local module
|
||||
@@ -60,30 +60,19 @@ describe('page.tsx decomposition — extraction targets', () => {
|
||||
describe('page.tsx decomposition — InfonetTerminal onClose wormhole teardown', () => {
|
||||
const page = readAppFile('page.tsx');
|
||||
|
||||
it('InfonetTerminal onClose delegates to teardownWormholeOnClose', () => {
|
||||
it('InfonetTerminal onClose ends the terminal session', () => {
|
||||
const infonetSection = page.slice(
|
||||
page.indexOf('<InfonetTerminal'),
|
||||
page.indexOf('</InfonetTerminal>') !== -1
|
||||
? page.indexOf('</InfonetTerminal>')
|
||||
: page.indexOf('/>', page.indexOf('<InfonetTerminal')) + 2,
|
||||
);
|
||||
expect(infonetSection).toContain('teardownWormholeOnClose');
|
||||
expect(infonetSection).toContain('fetchWormholeState');
|
||||
expect(infonetSection).toContain('leaveWormhole');
|
||||
expect(infonetSection).toContain('endInfonetTerminalSession');
|
||||
});
|
||||
|
||||
it('page.tsx imports teardownWormholeOnClose from wormholeTeardown', () => {
|
||||
it('page.tsx imports endInfonetTerminalSession from infonetTerminalSession', () => {
|
||||
expect(page).toMatch(
|
||||
/import\s*\{[^}]*teardownWormholeOnClose[^}]*\}\s*from\s+['"]@\/lib\/wormholeTeardown['"]/,
|
||||
);
|
||||
});
|
||||
|
||||
it('page.tsx imports leaveWormhole and fetchWormholeState from wormholeClient', () => {
|
||||
expect(page).toMatch(
|
||||
/import\s*\{[^}]*leaveWormhole[^}]*\}\s*from\s+['"]@\/mesh\/wormholeClient['"]/,
|
||||
);
|
||||
expect(page).toMatch(
|
||||
/import\s*\{[^}]*fetchWormholeState[^}]*\}\s*from\s+['"]@\/mesh\/wormholeClient['"]/,
|
||||
/import\s*\{[^}]*endInfonetTerminalSession[^}]*\}\s*from\s+['"]@\/lib\/infonetTerminalSession['"]/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user