test: add port ready, container cleanup

This commit is contained in:
vorotamoroz
2026-05-22 03:19:48 +00:00
parent cc3c992b1d
commit 3693d6a6b6
4 changed files with 171 additions and 22 deletions
+57 -15
View File
@@ -1,11 +1,26 @@
import { runCli } from "./cli.ts";
import { isLocalP2pRelay, startP2pRelay, stopP2pRelay } from "./docker.ts";
import { waitForPort } from "./net.ts";
export type PeerEntry = {
id: string;
name: string;
};
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function parseRelayEndpoint(relay: string): { hostname: string; port: number } {
const url = new URL(relay);
const port = url.port ? Number(url.port) : url.protocol === "ws:" ? 80 : url.protocol === "wss:" ? 443 : NaN;
if (!Number.isFinite(port)) {
throw new Error(`Unsupported relay URL: ${relay}`);
}
const hostname = url.hostname === "localhost" ? "127.0.0.1" : url.hostname;
return { hostname, port };
}
export function parsePeerLines(output: string): PeerEntry[] {
return output
.split(/\r?\n/)
@@ -20,28 +35,55 @@ export async function discoverPeer(
timeoutSeconds: number,
targetPeer?: string
): Promise<PeerEntry> {
const result = await runCli(vaultDir, "--settings", settingsFile, "p2p-peers", String(timeoutSeconds));
if (result.code !== 0) {
throw new Error(`p2p-peers failed\n${result.combined}`);
}
const peers = parsePeerLines(result.stdout);
if (targetPeer) {
const matched = peers.find((peer) => peer.id === targetPeer || peer.name === targetPeer);
if (matched) return matched;
}
if (peers.length === 0) {
const fallback = result.combined.match(/Advertisement from\s+([^\s]+)/);
if (fallback?.[1]) {
return { id: fallback[1], name: fallback[1] };
const retries = Math.max(0, Number(Deno.env.get("LIVESYNC_P2P_PEERS_RETRY") ?? "3"));
let lastCombined = "";
for (let attempt = 0; attempt <= retries; attempt++) {
const result = await runCli(vaultDir, "--settings", settingsFile, "p2p-peers", String(timeoutSeconds));
lastCombined = result.combined;
if (result.code === 0) {
const peers = parsePeerLines(result.stdout);
if (targetPeer) {
const matched = peers.find((peer) => peer.id === targetPeer || peer.name === targetPeer);
if (matched) return matched;
}
if (peers.length > 0) {
return peers[0];
}
const fallback = result.combined.match(/Advertisement from\s+([^\s]+)/);
if (fallback?.[1]) {
return { id: fallback[1], name: fallback[1] };
}
}
throw new Error(`No peers discovered\n${result.combined}`);
if (attempt < retries) {
const waitMs = 400 * (attempt + 1);
console.warn(
`[WARN] p2p-peers returned no usable peers, retrying (${attempt + 1}/${retries}) in ${waitMs}ms`
);
await sleep(waitMs);
continue;
}
throw new Error(
result.code !== 0 ? `p2p-peers failed\n${result.combined}` : `No peers discovered\n${result.combined}`
);
}
return peers[0];
throw new Error(`No peers discovered\n${lastCombined}`);
}
export async function maybeStartLocalRelay(relay: string): Promise<boolean> {
if (!isLocalP2pRelay(relay)) return false;
await startP2pRelay();
const endpoint = parseRelayEndpoint(relay);
await waitForPort(endpoint.hostname, endpoint.port, {
timeoutMs: Number(Deno.env.get("LIVESYNC_P2P_RELAY_READY_TIMEOUT_MS") ?? "15000"),
intervalMs: Number(Deno.env.get("LIVESYNC_P2P_RELAY_READY_INTERVAL_MS") ?? "250"),
connectTimeoutMs: Number(Deno.env.get("LIVESYNC_P2P_RELAY_CONNECT_TIMEOUT_MS") ?? "1000"),
});
return true;
}