From db94969f8a17c0f2af271e3c7bc71070f15ffb90 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Fri, 7 Jul 2023 15:11:19 +0200 Subject: [PATCH] Allow httpd API ports to be generated randomly. To avoid port collisions between tests that can eventually run in parallel, we need to be able to generate random port numbers. This commit generates random httpd port numbers for inline tests, while keeping the current palm seed with the default port number 8080. Signed-off-by: Sebastian Martinez --- httpd-client/tests/support/globalSetup.ts | 2 +- tests/e2e/project/commits.spec.ts | 38 +++++++++---------- tests/e2e/project/issues.spec.ts | 6 ++- tests/support/fixtures.ts | 15 ++++---- tests/support/globalSetup.ts | 2 +- tests/support/peerManager.ts | 46 +++++++++++++++++++---- 6 files changed, 72 insertions(+), 37 deletions(-) diff --git a/httpd-client/tests/support/globalSetup.ts b/httpd-client/tests/support/globalSetup.ts index aea9f2283..6de96b6a5 100644 --- a/httpd-client/tests/support/globalSetup.ts +++ b/httpd-client/tests/support/globalSetup.ts @@ -52,7 +52,7 @@ export default async function globalSetup(): Promise<() => void> { console.log("Running tests"); await palm.stopNode(); } else { - await startPalmHttpd(); + await startPalmHttpd(8080); } return () => killAllProcesses(); diff --git a/tests/e2e/project/commits.spec.ts b/tests/e2e/project/commits.spec.ts index 0ec7b3860..d448092cd 100644 --- a/tests/e2e/project/commits.spec.ts +++ b/tests/e2e/project/commits.spec.ts @@ -11,12 +11,12 @@ import { createProject } from "@tests/support/project"; test("peer and branch switching", async ({ page }) => { await page.goto(sourceBrowsingUrl); - await page.locator('role=link[name="6 commits"]').click(); + await page.getByRole("link", { name: "6 commits" }).click(); // Alice's peer. { await page.getByTitle("Change peer").click(); - await page.locator(`text=${aliceRemote}`).click(); + await page.getByText(aliceRemote).click(); await expect(page.getByTitle("Change peer")).toHaveText( ` did:key:${aliceRemote .substring(8) @@ -37,7 +37,7 @@ test("peer and branch switching", async ({ page }) => { await expect(earliestCommit).toContainText("36d5bbe"); await page.getByTitle("Change branch").click(); - await page.locator("text=feature/branch").click(); + await page.getByText("feature/branch").click(); await expect(page.getByTitle("Current branch")).toContainText( "feature/branch 1aded56", @@ -46,7 +46,7 @@ test("peer and branch switching", async ({ page }) => { await expect(page.locator(".history .teaser")).toHaveCount(9); await page.getByTitle("Change branch").click(); - await page.locator("text=orphaned-branch").click(); + await page.getByText("orphaned-branch").click(); await expect(page.getByTitle("Current branch")).toContainText( "orphaned-branch af3641c", @@ -58,7 +58,7 @@ test("peer and branch switching", async ({ page }) => { // Bob's peer. { await page.getByTitle("Change peer").click(); - await page.locator(`text=${bobRemote}`).click(); + await page.getByText(bobRemote).click(); await expect(page.getByTitle("Change peer")).toContainText( ` did:key:${bobRemote.substring(8).substring(0, 6)}…${bobRemote.slice( -6, @@ -89,7 +89,7 @@ test("peer and branch switching", async ({ page }) => { test("expand commit message", async ({ page }) => { await page.goto(sourceBrowsingUrl); - await page.locator('role=link[name="6 commits"]').click(); + await page.getByRole("link", { name: "6 commits" }).click(); const commitToggle = page .locator("div") .filter({ hasText: /^Add a C source file and its binary …$/ }) @@ -117,10 +117,10 @@ test("relative timestamps", async ({ page }) => { }); await page.goto(sourceBrowsingUrl); - await page.locator('role=link[name="6 commits"]').click(); + await page.getByRole("link", { name: "6 commits" }).click(); await page.getByTitle("Change peer").click(); - await page.locator(`text=${bobRemote}`).click(); + await page.getByText(bobRemote).click(); await expect(page.getByTitle("Change peer")).toHaveText( `did:key:${bobRemote.substring(8).substring(0, 6)}…${bobRemote.slice(-6)}`, ); @@ -138,12 +138,12 @@ test("pushing changes while viewing history", async ({ page, peerManager }) => { name: "alice", gitOptions: gitOptions["alice"], }); - await alice.startHttpd(8090); + await alice.startHttpd(); await alice.startNode(); const { rid, projectFolder } = await createProject(alice, "alice-project"); - await page.goto(`/seeds/127.0.0.1:8090/${rid}`); - await page.locator('role=link[name="1 commit"]').click(); - await expect(page).toHaveURL(`/seeds/127.0.0.1:8090/${rid}/history`); + await page.goto(`${alice.uiUrl()}/${rid}`); + await page.getByRole("link", { name: "1 commit" }).click(); + await expect(page).toHaveURL(`${alice.uiUrl()}/${rid}/history`); await alice.git(["commit", "--allow-empty", "--message", "first change"], { cwd: projectFolder, @@ -152,13 +152,13 @@ test("pushing changes while viewing history", async ({ page, peerManager }) => { cwd: projectFolder, }); await page.reload(); - await expect(page).toHaveURL(`/seeds/127.0.0.1:8090/${rid}/history`); - await expect(page.locator('role=link[name="2 commits"]')).toBeVisible(); + await expect(page).toHaveURL(`${alice.uiUrl()}/${rid}/history`); + await expect(page.getByRole("link", { name: "2 commits" })).toBeVisible(); await expect(page.getByTitle("Current branch")).toContainText("main 516fa74"); - await page.locator("text=alice-project").click(); - await expect(page).toHaveURL(`/seeds/127.0.0.1:8090/${rid}`); - await page.locator('role=link[name="2 commits"]').click(); + await page.getByText("alice-project").click(); + await expect(page).toHaveURL(`${alice.uiUrl()}/${rid}`); + await page.getByRole("link", { name: "2 commits" }).click(); await alice.git( [ @@ -175,8 +175,8 @@ test("pushing changes while viewing history", async ({ page, peerManager }) => { cwd: projectFolder, }); await page.reload(); - await expect(page).toHaveURL(`/seeds/127.0.0.1:8090/${rid}/history`); - await expect(page.locator('role=link[name="3 commits"]')).toHaveText( + await expect(page).toHaveURL(`${alice.uiUrl()}/${rid}/history`); + await expect(page.getByRole("link", { name: "3 commits" })).toHaveText( "3 commits", ); await expect(page.getByTitle("Current branch")).toContainText("main bb9089a"); diff --git a/tests/e2e/project/issues.spec.ts b/tests/e2e/project/issues.spec.ts index 3956b0b87..2ae8ad4d9 100644 --- a/tests/e2e/project/issues.spec.ts +++ b/tests/e2e/project/issues.spec.ts @@ -48,7 +48,7 @@ test("test issue editing failing", async ({ page, authenticatedPeer }) => { ); await page.goto( - `/seeds/127.0.0.1:8070/${rid}/issues/d316f7a90a40dacbfb8728044bad50c9f71d44ba`, + `${authenticatedPeer.uiUrl()}/${rid}/issues/d316f7a90a40dacbfb8728044bad50c9f71d44ba`, ); await page.getByPlaceholder("Leave your comment").fill("This is a comment"); @@ -62,7 +62,9 @@ test("go through the entire ui issue flow", async ({ }) => { const { rid } = await createProject(authenticatedPeer, "commenting"); - await page.goto(`/seeds/127.0.0.1:8070/${rid}`); + await page.goto( + `/seeds/${authenticatedPeer.httpdBaseUrl.hostname}:${authenticatedPeer.httpdBaseUrl.port}/${rid}`, + ); await page.getByRole("link", { name: "0 issues" }).click(); await page.getByRole("link", { name: "New issue" }).click(); await page.getByPlaceholder("Title").fill("This is a title"); diff --git a/tests/support/fixtures.ts b/tests/support/fixtures.ts index 0b6a6b001..a4db3fbd8 100644 --- a/tests/support/fixtures.ts +++ b/tests/support/fixtures.ts @@ -165,18 +165,20 @@ export const test = base.extend<{ gitOptions: gitOptions["bob"], }); - await peer.startHttpd(8070); + await peer.startHttpd(); await peer.startNode(); await page.goto("/"); await page.getByRole("button", { name: "radicle.local" }).click(); - await page.locator('input[name="port"]').fill("8070"); + await page + .locator('input[name="port"]') + .fill(peer.httpdBaseUrl.port.toString()); await page.locator('input[name="port"]').press("Enter"); const { stdout } = await peer.rad([ "web", "--frontend", "http://localhost:3001", "--backend", - "http://127.0.0.1:8070", + `${peer.httpdBaseUrl.scheme}://${peer.httpdBaseUrl.hostname}:${peer.httpdBaseUrl.port}`, "--json", ]); const result = authSchema.safeParse(JSON.parse(stdout)); @@ -191,7 +193,7 @@ export const test = base.extend<{ await use(peer); - await peer.stopHttpd(8070); + await peer.stopHttpd(); await peer.stopNode(); }, @@ -256,13 +258,13 @@ export function appConfigWithFixture() { }; } -export async function startPalmHttpd() { +export async function startPalmHttpd(httpdPort: number) { const peerManager = await createPeerManager({ dataDir: Path.resolve(Path.join(tmpDir, "peers")), outputLog: FsSync.createWriteStream(Path.resolve(Path.join(tmpDir, "log"))), }); const palm = await peerManager.startPeer({ name: "palm" }); - await palm.startHttpd(8080); + await palm.startHttpd(httpdPort); } export async function createSourceBrowsingFixture( @@ -611,7 +613,6 @@ export const markdownRid = "rad:z2tchH2Ti4LxRKdssPQYs6VHE5rsg"; export const sourceBrowsingUrl = `/seeds/127.0.0.1/${sourceBrowsingRid}`; export const cobUrl = `/seeds/127.0.0.1/${cobRid}`; export const markdownUrl = `/seeds/127.0.0.1/${markdownRid}`; -export const seedPort = 8080; export const seedRemote = "z6MktULudTtAsAhRegYPiZ6631RV3viv12qd4GQF8z1xB22S"; export const gitOptions = { alice: { diff --git a/tests/support/globalSetup.ts b/tests/support/globalSetup.ts index 053d02148..0facd9ba1 100644 --- a/tests/support/globalSetup.ts +++ b/tests/support/globalSetup.ts @@ -61,7 +61,7 @@ export default async function globalSetup(): Promise<() => void> { console.log("Running tests"); await palm.stopNode(); } else { - await startPalmHttpd(); + await startPalmHttpd(8080); } return () => killAllProcesses(); diff --git a/tests/support/peerManager.ts b/tests/support/peerManager.ts index 4549e2c1a..14f6f2032 100644 --- a/tests/support/peerManager.ts +++ b/tests/support/peerManager.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import type { BaseUrl } from "@httpd-client"; import type { ExecaChildProcess, Options } from "execa"; import * as Fs from "node:fs/promises"; @@ -115,6 +116,7 @@ export class RadiclePeer { #outputLog: Stream.Writable; #gitOptions?: Record; #listenSocketAddr?: string; + #httpdBaseUrl?: BaseUrl; #nodeProcess?: ExecaChildProcess; #httpdProcess?: ExecaChildProcess; @@ -184,25 +186,42 @@ export class RadiclePeer { }); } - public async startHttpd(port: number) { + public async startHttpd(port?: number): Promise { + if (!port) { + port = await getPort(); + } + this.#httpdBaseUrl = { + hostname: "127.0.0.1", + port, + scheme: "http", + }; this.#httpdProcess = this.spawn("radicle-httpd", [ "--listen", - `0.0.0.0:${port}`, + `${this.#httpdBaseUrl.hostname}:${this.#httpdBaseUrl.port}`, ]); await waitOn({ - resources: [`tcp:127.0.0.1:${port}`], + resources: [ + `tcp:${this.#httpdBaseUrl.hostname}:${this.#httpdBaseUrl.port}`, + ], timeout: 7000, }); } - public async stopHttpd(port: number) { + public async stopHttpd() { + if (!this.#httpdBaseUrl || !this.#httpdProcess) { + throw new Error("No httpd service running"); + } this.#httpdProcess?.kill("SIGTERM"); await waitOn({ - resources: [`tcp:127.0.0.1:${port}`], + resources: [ + `tcp:${this.#httpdBaseUrl.hostname}:${this.#httpdBaseUrl.port}`, + ], reverse: true, }); + + this.#httpdBaseUrl = undefined; } public async startNode(params?: { @@ -312,10 +331,23 @@ export class RadiclePeer { } public uiUrl(): string { - return `/seeds/127.0.0.1:8080`; + if (!this.#httpdBaseUrl) { + throw new Error("No httpd service running"); + } + + return `/seeds/${this.#httpdBaseUrl.hostname}:${this.#httpdBaseUrl.port}`; } + public ridUrl(rid: string): string { - return `/seeds/127.0.0.1:8080/${rid}`; + return `/seeds/${this.httpdBaseUrl.hostname}:${this.httpdBaseUrl.port}/${rid}`; + } + + public get httpdBaseUrl(): BaseUrl { + if (!this.#httpdBaseUrl) { + throw new Error("No httpd service running"); + } + + return this.#httpdBaseUrl; } public git(args: string[] = [], opts?: Options): ExecaChildProcess {