Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions test/cli/challenge-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,18 @@ import {
type ManagedChildProcess,
stopPkcDaemon,
waitForCondition,
startPkcDaemon,
startPkcDaemonWithDynamicPorts,
waitForKuboReady
} from "../helpers/daemon-helpers.js";

dns.setDefaultResultOrder("ipv4first");

type PKCInstance = Awaited<ReturnType<typeof PKC>>;

// --- Port allocation (unique to this test file) ---
const RPC_PORT = 59138;
const KUBO_API_PORT = 50039;
const GATEWAY_PORT = 6493;
const rpcWsUrl = `ws://localhost:${RPC_PORT}`;
const kuboApiUrl = `http://0.0.0.0:${KUBO_API_PORT}/api/v0`;
const gatewayUrl = `http://0.0.0.0:${GATEWAY_PORT}`;
// Ports/URLs are allocated dynamically per run and assigned in beforeAll (issue #87).
let RPC_PORT: number;
let KUBO_API_PORT: number;
let rpcWsUrl: string;

// --- Helpers specific to this test file ---

Expand Down Expand Up @@ -167,11 +164,12 @@ describe("challenge integration tests", { timeout: 600_000 }, () => {
expect(installResult.exitCode).toBe(0);
expect(installResult.stdout).toContain("added test-challenge@1.0.0 in");

// Start daemon — it handles kubo, RPC, and webui internally
daemonProcess = await startPkcDaemon(
["--pkcOptions.dataPath", dataPath, "--pkcRpcUrl", rpcWsUrl],
{ KUBO_RPC_URL: kuboApiUrl, IPFS_GATEWAY_URL: gatewayUrl }
);
// Start daemon — it handles kubo, RPC, and webui internally. Dynamic ports + retry guard
// against the macOS ephemeral-range bind race (issue #87); the seeded dataPath is reused
// across retries (preInitKuboWithEphemeralSwarm is idempotent).
const daemon = await startPkcDaemonWithDynamicPorts((e) => ["--pkcOptions.dataPath", dataPath, "--pkcRpcUrl", e.rpcWsUrl]);
daemonProcess = daemon.daemonProcess;
({ rpcPort: RPC_PORT, kuboPort: KUBO_API_PORT, rpcWsUrl } = daemon);

// Wait for kubo API to be fully ready (it can lag behind the "Communities in data path" message)
const kuboReady = await waitForKuboReady(`http://localhost:${KUBO_API_PORT}/api/v0`, 30000);
Expand Down
21 changes: 9 additions & 12 deletions test/cli/command-completion-time.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,18 @@ import WebSocket from "ws";
import {
type ManagedChildProcess,
stopPkcDaemon,
startPkcDaemon,
startPkcDaemonWithDynamicPorts,
waitForCondition,
waitForWebSocketOpen,
waitForPortFree
} from "../helpers/daemon-helpers.js";
dns.setDefaultResultOrder("ipv4first");

// --- Port allocation (unique to this test file) ---
const RPC_PORT = 9538;
const KUBO_API_PORT = 50209;
const GATEWAY_PORT = 6703;
const rpcWsUrl = `ws://localhost:${RPC_PORT}`;
const kuboApiUrl = `http://0.0.0.0:${KUBO_API_PORT}/api/v0`;
const gatewayUrl = `http://0.0.0.0:${GATEWAY_PORT}`;
// Ports/URLs are allocated dynamically per run and assigned in beforeAll (issue #87).
let RPC_PORT: number;
let KUBO_API_PORT: number;
let GATEWAY_PORT: number;
let rpcWsUrl: string;

// Generic subprocess runner with timeout
const runBitsocialCommand = (
Expand Down Expand Up @@ -89,10 +87,9 @@ describe("CLI commands complete within 10s (real pkc instance)", () => {
logDir = path.join(stateHome, "bitsocial");
await fsPromise.mkdir(logDir, { recursive: true });

daemonProcess = await startPkcDaemon(
["--logPath", logDir, "--pkcRpcUrl", rpcWsUrl],
{ KUBO_RPC_URL: kuboApiUrl, IPFS_GATEWAY_URL: gatewayUrl }
);
const daemon = await startPkcDaemonWithDynamicPorts((e) => ["--logPath", logDir, "--pkcRpcUrl", e.rpcWsUrl]);
daemonProcess = daemon.daemonProcess;
({ rpcPort: RPC_PORT, kuboPort: KUBO_API_PORT, gatewayPort: GATEWAY_PORT, rpcWsUrl } = daemon);

// Wait for log file to appear
await waitForCondition(async () => {
Expand Down
194 changes: 110 additions & 84 deletions test/cli/daemon-kubo-restart-race.test.ts

Large diffs are not rendered by default.

244 changes: 129 additions & 115 deletions test/cli/daemon.test.ts

Large diffs are not rendered by default.

21 changes: 9 additions & 12 deletions test/cli/edit-null-removal.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@ import WebSocket from "ws";
import {
type ManagedChildProcess,
stopPkcDaemon,
startPkcDaemon,
startPkcDaemonWithDynamicPorts,
waitForCondition,
waitForWebSocketOpen,
waitForPortFree
} from "../helpers/daemon-helpers.js";
dns.setDefaultResultOrder("ipv4first");

// --- Port allocation (unique to this test file) ---
const RPC_PORT = 9638;
const KUBO_API_PORT = 50309;
const GATEWAY_PORT = 6803;
const rpcWsUrl = `ws://localhost:${RPC_PORT}`;
const kuboApiUrl = `http://0.0.0.0:${KUBO_API_PORT}/api/v0`;
const gatewayUrl = `http://0.0.0.0:${GATEWAY_PORT}`;
// Ports/URLs are allocated dynamically per run and assigned in beforeAll (issue #87).
let RPC_PORT: number;
let KUBO_API_PORT: number;
let GATEWAY_PORT: number;
let rpcWsUrl: string;

const runBitsocialCommand = (
args: string[],
Expand Down Expand Up @@ -67,10 +65,9 @@ describe("community edit null removal (real pkc instance)", () => {
let communityAddress: string;

beforeAll(async () => {
daemonProcess = await startPkcDaemon(
["--pkcRpcUrl", rpcWsUrl],
{ KUBO_RPC_URL: kuboApiUrl, IPFS_GATEWAY_URL: gatewayUrl }
);
const daemon = await startPkcDaemonWithDynamicPorts((e) => ["--pkcRpcUrl", e.rpcWsUrl]);
daemonProcess = daemon.daemonProcess;
({ rpcPort: RPC_PORT, kuboPort: KUBO_API_PORT, gatewayPort: GATEWAY_PORT, rpcWsUrl } = daemon);

await waitForCondition(async () => {
try {
Expand Down
18 changes: 6 additions & 12 deletions test/cli/logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,13 @@ import dns from "node:dns";
import {
type ManagedChildProcess,
stopPkcDaemon,
startPkcDaemon,
startPkcDaemonWithDynamicPorts,
waitForCondition
} from "../helpers/daemon-helpers.js";
dns.setDefaultResultOrder("ipv4first");

// --- Port allocation (unique to this test file) ---
const RPC_PORT = 9438;
const KUBO_API_PORT = 50129;
const GATEWAY_PORT = 6583;
const rpcWsUrl = `ws://localhost:${RPC_PORT}`;
const kuboApiUrl = `http://0.0.0.0:${KUBO_API_PORT}/api/v0`;
const gatewayUrl = `http://0.0.0.0:${GATEWAY_PORT}`;
// Ports/URLs are allocated dynamically per run and assigned in beforeAll (issue #87).
let rpcWsUrl: string;

const createLogDir = async () => {
const logDir = randomDirectory();
Expand Down Expand Up @@ -497,10 +492,9 @@ describe("bitsocial logs (live daemon tests)", async () => {

beforeAll(async () => {
({ logDir } = await createLogDir());
daemonProcess = await startPkcDaemon(
["--logPath", logDir, "--pkcRpcUrl", rpcWsUrl],
{ KUBO_RPC_URL: kuboApiUrl, IPFS_GATEWAY_URL: gatewayUrl }
);
const daemon = await startPkcDaemonWithDynamicPorts((e) => ["--logPath", logDir, "--pkcRpcUrl", e.rpcWsUrl]);
daemonProcess = daemon.daemonProcess;
rpcWsUrl = daemon.rpcWsUrl;
// Wait for log file to be written
await waitForCondition(async () => {
const files = await fsPromise.readdir(logDir);
Expand Down
24 changes: 11 additions & 13 deletions test/cli/mintpass-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,18 @@ import {
type ManagedChildProcess,
stopPkcDaemon,
waitForCondition,
startPkcDaemon,
startPkcDaemonWithDynamicPorts,
waitForKuboReady
} from "../helpers/daemon-helpers.js";

dns.setDefaultResultOrder("ipv4first");

type PKCInstance = Awaited<ReturnType<typeof PKC>>;

// --- Port allocation (unique to this test file) ---
const RPC_PORT = 59238;
const KUBO_API_PORT = 50049;
const GATEWAY_PORT = 6503;
const rpcWsUrl = `ws://localhost:${RPC_PORT}`;
const kuboApiUrl = `http://0.0.0.0:${KUBO_API_PORT}/api/v0`;
const gatewayUrl = `http://0.0.0.0:${GATEWAY_PORT}`;
// Ports/URLs are allocated dynamically per run and assigned in beforeAll (issue #87).
let RPC_PORT: number;
let KUBO_API_PORT: number;
let rpcWsUrl: string;

// --- Helpers specific to this test file ---

Expand Down Expand Up @@ -129,11 +126,12 @@ describe.skipIf(process.platform === "win32")("@bitsocial/mintpass-challenge int
expect(installResult.exitCode).toBe(0);
expect(installResult.stdout).toContain("added @bitsocial/mintpass-challenge");

// Start daemon — it handles kubo, RPC, and webui internally
daemonProcess = await startPkcDaemon(["--pkcOptions.dataPath", dataPath, "--pkcRpcUrl", rpcWsUrl], {
KUBO_RPC_URL: kuboApiUrl,
IPFS_GATEWAY_URL: gatewayUrl
});
// Start daemon — it handles kubo, RPC, and webui internally. Dynamic ports + retry guard
// against the macOS ephemeral-range bind race (issue #87); the seeded dataPath is reused
// across retries (preInitKuboWithEphemeralSwarm is idempotent).
const daemon = await startPkcDaemonWithDynamicPorts((e) => ["--pkcOptions.dataPath", dataPath, "--pkcRpcUrl", e.rpcWsUrl]);
daemonProcess = daemon.daemonProcess;
({ rpcPort: RPC_PORT, kuboPort: KUBO_API_PORT, rpcWsUrl } = daemon);

// Wait for kubo API to be fully ready (it can lag behind the "Communities in data path" message)
const kuboReady = await waitForKuboReady(`http://localhost:${KUBO_API_PORT}/api/v0`, 30000);
Expand Down
50 changes: 27 additions & 23 deletions test/cli/update-install-restart-race.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,15 @@ import fs from "fs/promises";
import path from "path";
import {
stopPkcDaemon,
startPkcDaemon,
startPkcDaemonWithDynamicPorts,
ensureKuboNodeStopped,
waitForKuboReady,
type ManagedChildProcess
} from "../helpers/daemon-helpers.js";

// Ports unique to this file (avoid collisions with other test files and external processes).
const RPC_PORT = 9468;
const KUBO_PORT = 50121;
const GATEWAY_PORT = 6581;
const RPC_URL = `ws://localhost:${RPC_PORT}`;
const KUBO_URL = `http://0.0.0.0:${KUBO_PORT}/api/v0`;
const KUBO_API_URL = `http://localhost:${KUBO_PORT}/api/v0`;
const GATEWAY_URL = `http://0.0.0.0:${GATEWAY_PORT}`;
// Ports are allocated dynamically per test (issue #87): the kubo API port this file used to pin
// (50121) fell in macOS's ephemeral range, so under fileParallelism it could be grabbed by another
// test file's outbound socket and the daemon's kubo bind would intermittently fail.

const CLI_VERSION = JSON.parse(readFileSync(path.join(process.cwd(), "package.json"), "utf-8")).version as string;

Expand All @@ -72,6 +67,7 @@ const runUpdateInstall = (env: Record<string, string>): Promise<{ exitCode: numb
describe("bitsocial update install restart race (issue #70)", async () => {
let daemonA: ManagedChildProcess | undefined;
let restartedPidFile: string | undefined;
let kuboApiUrl: string | undefined;

afterEach(async () => {
if (daemonA) await stopPkcDaemon(daemonA);
Expand All @@ -91,7 +87,8 @@ describe("bitsocial update install restart race (issue #70)", async () => {
}
}
restartedPidFile = undefined;
await ensureKuboNodeStopped(KUBO_API_URL);
if (kuboApiUrl) await ensureKuboNodeStopped(kuboApiUrl);
kuboApiUrl = undefined;
});

it.skipIf(process.platform === "win32")(
Expand Down Expand Up @@ -138,29 +135,36 @@ describe("bitsocial update install restart race (issue #70)", async () => {
);
await fs.chmod(shim, 0o755);

const sharedEnv = {
const isolatedEnv = {
HOME: isolatedHome,
XDG_DATA_HOME: path.join(isolatedHome, ".local", "share"),
KUBO_RPC_URL: KUBO_URL,
IPFS_GATEWAY_URL: GATEWAY_URL
XDG_DATA_HOME: path.join(isolatedHome, ".local", "share")
};

await ensureKuboNodeStopped(KUBO_API_URL);

// Start daemon A — a real daemon with a real kubo, writing its state file into the
// isolated home so update install discovers only this daemon.
daemonA = await startPkcDaemon(["--pkcRpcUrl", RPC_URL], {
...sharedEnv,
PKC_CLI_TEST_KUBO_SHUTDOWN_DELAY_MS: String(KUBO_SHUTDOWN_DELAY_MS)
});
// isolated home so update install discovers only this daemon. Dynamic ports + retry guard
// the macOS ephemeral-range bind race (issue #87); the update install restart below reuses
// the same KUBO_RPC_URL so the shim's port check observes the right kubo API port.
const daemon = await startPkcDaemonWithDynamicPorts(
(e) => ["--pkcRpcUrl", e.rpcWsUrl],
(e) => ({
...isolatedEnv,
KUBO_RPC_URL: e.kuboRpcUrl,
IPFS_GATEWAY_URL: e.gatewayUrl,
PKC_CLI_TEST_KUBO_SHUTDOWN_DELAY_MS: String(KUBO_SHUTDOWN_DELAY_MS)
})
);
daemonA = daemon.daemonProcess;
kuboApiUrl = daemon.kuboApiUrl;
expect(typeof daemonA.pid).toBe("number");
expect(await waitForKuboReady(KUBO_API_URL, 45000)).toBe(true);
expect(await waitForKuboReady(kuboApiUrl, 45000)).toBe(true);

// Run the real `bitsocial update install <currentVersion>`: same version => skips npm,
// but runs the full stop + _restartDaemons path. The shim (first on PATH) is what it
// spawns for the restart.
const result = await runUpdateInstall({
...sharedEnv,
...isolatedEnv,
KUBO_RPC_URL: daemon.kuboRpcUrl,
IPFS_GATEWAY_URL: daemon.gatewayUrl,
PATH: `${tmpBin}:${process.env.PATH}`,
PKC_CLI_TEST_RESTART_MARKER: markerFile
});
Expand Down
Loading
Loading