Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Instrument authentication for e2e tests
Open did:key:z6MkkfM3...sVz5 opened 1 year ago
  • Fix error serialize in the test-http-api crate
  • Add tests to verify authenticated identities

checkcheck-e2e

👉 Workflow runs 👉 Branch on GitHub

14 files changed +61 -45 ac550e06 c35ab783
modified Cargo.lock
@@ -3898,6 +3898,7 @@ name = "radicle-types"
version = "0.1.0"
dependencies = [
 "anyhow",
+
 "axum",
 "base64 0.22.1",
 "localtime",
 "radicle",
modified crates/radicle-types/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
anyhow = { version = "1.0.90" }
+
axum = { version = "0.7.5", default-features = false, features = ["json"] }
base64 = { version = "0.22.1" }
radicle = { git = "https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" }
radicle-surf = { version = "0.22.1", features = ["serde"] }
modified crates/radicle-types/src/error.rs
@@ -1,3 +1,6 @@
+
use axum::body::Body;
+
use axum::http::{Response, StatusCode};
+
use axum::response::IntoResponse;
use serde::Serialize;

#[derive(Debug, thiserror::Error)]
@@ -108,6 +111,16 @@ impl Serialize for Error {
    }
}

+
impl IntoResponse for Error {
+
    fn into_response(self) -> Response<Body> {
+
        (
+
            StatusCode::INTERNAL_SERVER_ERROR,
+
            serde_json::to_string(&self).unwrap(),
+
        )
+
            .into_response()
+
    }
+
}
+

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod test {
modified crates/test-http-api/src/api.rs
@@ -15,6 +15,7 @@ use radicle::{git, identity};
use radicle_types as types;
use radicle_types::cobs::issue::{Action, NewIssue};
use radicle_types::cobs::CobOptions;
+
use radicle_types::error::Error;
use radicle_types::traits::auth::Auth;
use radicle_types::traits::cobs::Cobs;
use radicle_types::traits::issue::{Issues, IssuesMut};
@@ -23,8 +24,6 @@ use radicle_types::traits::repo::Repo;
use radicle_types::traits::thread::Thread;
use radicle_types::traits::Profile;

-
use crate::error::Error;
-

#[derive(Clone)]
pub struct Context {
    profile: Arc<radicle::Profile>,
deleted crates/test-http-api/src/error.rs
@@ -1,32 +0,0 @@
-
use axum::http::StatusCode;
-
use axum::response::{IntoResponse, Response};
-
use axum::Json;
-
use serde_json::json;
-

-
#[derive(Debug, thiserror::Error)]
-
pub enum Error {
-
    /// radicle_types error.
-
    #[error(transparent)]
-
    Types(#[from] radicle_types::error::Error),
-
}
-

-
impl IntoResponse for Error {
-
    fn into_response(self) -> Response {
-
        let (status, msg) = match self {
-
            other => {
-
                if cfg!(debug_assertions) {
-
                    (StatusCode::INTERNAL_SERVER_ERROR, Some(other.to_string()))
-
                } else {
-
                    (StatusCode::INTERNAL_SERVER_ERROR, None)
-
                }
-
            }
-
        };
-

-
        let body = Json(json!({
-
            "error": msg.or_else(|| status.canonical_reason().map(|r| r.to_string())),
-
            "code": status.as_u16()
-
        }));
-

-
        (status, body).into_response()
-
    }
-
}
modified crates/test-http-api/src/lib.rs
@@ -7,7 +7,6 @@ use tokio::net::TcpListener;
use radicle::Profile;

mod api;
-
mod error;

#[derive(Debug, Clone)]
pub struct Options {
modified crates/test-http-api/src/main.rs
@@ -30,6 +30,6 @@ fn parse_options() -> Result<api::Options, lexopt::Error> {
        }
    }
    Ok(api::Options {
-
        listen: listen.unwrap_or_else(|| ([0, 0, 0, 0], 8080).into()),
+
        listen: listen.unwrap_or_else(|| ([0, 0, 0, 0], 8081).into()),
    })
}
modified playwright.config.ts
@@ -32,7 +32,8 @@ const config: PlaywrightTestConfig = {

  webServer: [
    {
-
      command: "npm run start -- --strictPort --port 3001",
+
      command:
+
        "VITE_AUTH_LONG_DELAY=1000 npm run start -- --strictPort --port 3001",
      port: 3001,
    },
  ],
modified src/App.svelte
@@ -39,7 +39,10 @@
    try {
      await invoke("authenticate");
      void router.loadFromLocation();
-
      void dynamicInterval(checkAuth, 30_000);
+
      void dynamicInterval(
+
        checkAuth,
+
        import.meta.env.VITE_AUTH_LONG_DELAY || 30_000,
+
      );
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (e: any) {
      void router.push({
modified src/lib/auth.ts
@@ -26,7 +26,7 @@ export async function checkAuth() {
    if (get(activeRouteStore).resource === "authenticationError") {
      window.history.back();
    }
-
    dynamicInterval(checkAuth, 30_000);
+
    dynamicInterval(checkAuth, import.meta.env.VITE_AUTH_LONG_DELAY || 30_000);
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  } catch (e: any) {
    if (get(activeRouteStore).resource !== "authenticationError") {
modified src/lib/invoke.ts
@@ -12,11 +12,14 @@ export async function invoke<T = null>(
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(args),
-
    })
-
      .then(data => data.json())
-
      .catch(() => {
-
        throw Error(`Issue with HTTP Route: ${JSON.stringify({ cmd, args })}`);
-
      });
+
    }).then(async response => {
+
      const json = await response.json();
+
      if (!response.ok) {
+
        throw json;
+
      }
+

+
      return json;
+
    });
  }
}

added tests/e2e/authenticate.spec.ts
@@ -0,0 +1,17 @@
+
import { test, expect } from "@tests/support/fixtures.js";
+

+
test("removing identities from ssh-agent and re-adding them", async ({
+
  page,
+
  peer,
+
}) => {
+
  await page.goto("/");
+
  await expect(page.getByText("palm")).toBeVisible();
+

+
  await peer.logOut();
+
  await expect(
+
    page.getByText("Not able to find your keys in the ssh agent"),
+
  ).toBeVisible();
+

+
  await peer.authenticate();
+
  await expect(page.getByText("palm")).toBeVisible();
+
});
modified tests/e2e/repos.spec.ts
@@ -1,4 +1,4 @@
-
import { expect, test } from "@playwright/test";
+
import { expect, test } from "@tests/support/fixtures.js";

test("navigate to repo issues", async ({ page }) => {
  await page.goto("/repos");
modified tests/support/peerManager.ts
@@ -235,6 +235,17 @@ export class RadiclePeer {
    });
  }

+
  /**
+
   Removes the peers identity file from the `ssh-agent`
+
   **/
+
  public async logOut(): Promise<void> {
+
    await this.spawn("ssh-add", ["-d", `${this.#radHome}/keys/radicle.pub`]);
+
  }
+

+
  public async authenticate(): Promise<void> {
+
    await this.spawn("rad", ["auth"]);
+
  }
+

  public async startHttpd(port?: number): Promise<void> {
    if (!port) {
      port = await getPort();