diff --git a/e2e-tests/package.json b/e2e-tests/package.json index 28f3f09773..47fce76ccd 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -55,7 +55,6 @@ "@microsoft/microsoft-graph-client": "3.0.7", "isomorphic-fetch": "3.0.0", "js-yaml": "4.1.1", - "node-fetch": "2.7.0", "octokit": "4.1.4", "pg": "8.18.0", "prettier-plugin-sh": "0.18.0", diff --git a/e2e-tests/playwright/e2e/plugins/keycloak/catalog-users.spec.ts b/e2e-tests/playwright/e2e/plugins/keycloak/catalog-users.spec.ts deleted file mode 100644 index c3e09e5611..0000000000 --- a/e2e-tests/playwright/e2e/plugins/keycloak/catalog-users.spec.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { CatalogUsersPO } from "../../../support/page-objects/catalog/catalog-users-obj"; -import Keycloak from "../../../utils/keycloak/keycloak"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import { test, expect } from "@playwright/test"; -import { ChildProcessWithoutNullStreams, spawn, exec } from "child_process"; -import { KubeClient } from "../../../utils/kube-client"; - -test.describe("Test Keycloak plugin", () => { - let uiHelper: UIhelper; - let keycloak: Keycloak; - let common: Common; - let token: string; - - test.beforeAll(async () => { - test.info().annotations.push({ - type: "component", - description: "plugins", - }); - - keycloak = new Keycloak(); - token = await keycloak.getAuthenticationToken(); - }); - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - common = new Common(page); - await common.loginAsGuest(); - await CatalogUsersPO.visitBaseURL(page); - }); - - test("Users on keycloak should match users on backstage", async ({ - page, - }) => { - const keycloakUsers = await keycloak.getUsers(token); - const backStageUsersLocator = CatalogUsersPO.getListOfUsers(page); - await common.waitForLoad(); - await backStageUsersLocator.first().waitFor({ state: "visible" }); - const backStageUsersCount = await backStageUsersLocator.count(); - - expect(keycloakUsers.length).toBeGreaterThan(0); - expect(backStageUsersCount).toBeGreaterThan(0); - - for (let i = 0; i < backStageUsersCount; i++) { - const backStageUser = backStageUsersLocator.nth(i); - const backStageUserText = await backStageUser.textContent(); - const userFound = keycloakUsers.find( - (user) => user.username === backStageUserText, - ); - expect(userFound).not.toBeNull(); - - // eslint-disable-next-line playwright/no-conditional-in-test - if (userFound) { - await keycloak.checkUserDetails( - page, - userFound, - token, - uiHelper, - keycloak, - ); - } - } - }); -}); - -test.describe("Test Keycloak plugin metrics", () => { - let portForward: ChildProcessWithoutNullStreams; - - test.beforeEach(async () => { - const namespace = process.env.NAME_SPACE || "showcase-ci-nightly"; - const kubeClient = new KubeClient(); - - console.log("Starting port-forward process..."); - - const services = await kubeClient.getServiceByLabel( - namespace, - "app.kubernetes.io/instance=rhdh", - ); - const rhdhMetricsServiceName = services.find((service) => - service.spec?.ports.some((p) => p.port === 9464), - ); - portForward = spawn("/bin/sh", [ - "-c", - ` - oc login --token="${process.env.K8S_CLUSTER_TOKEN}" --server="${process.env.K8S_CLUSTER_URL}" --insecure-skip-tls-verify=true && - kubectl config set-context --current --namespace="${namespace}" && - kubectl port-forward service/${rhdhMetricsServiceName.metadata?.name} 9464:9464 --namespace="${namespace}" - `, - ]); - - console.log("Waiting for port-forward to be ready..."); - await new Promise((resolve, reject) => { - portForward.stdout.on("data", (data) => { - if (data.toString().includes("Forwarding from 127.0.0.1:9464")) { - resolve(); - } - }); - - portForward.stderr.on("data", (data) => { - console.error(`Port forwarding failed: ${data.toString()}`); - reject(new Error(`Port forwarding failed: ${data.toString()}`)); - }); - }); - }); - - test.afterEach(() => { - console.log("Killing port-forward process with ID:", portForward.pid); - portForward.kill("SIGKILL"); - console.log("Killing remaining port-forward process."); - exec( - `ps aux | grep 'kubectl port-forward' | grep -v grep | awk '{print $2}' | xargs kill -9`, - ); - }); - - test("Test keycloak metrics with failure counters", async () => { - const metricsEndpointURL = "http://localhost:9464/metrics"; - const metricLines = await fetchMetrics(metricsEndpointURL); - - const metricLineStartWith = - 'backend_keycloak_fetch_task_failure_count_total{taskInstanceId="'; - const metricLineEndsWith = '"} 1'; - const isContainMetricFailureCounter = metricLines.find( - (line) => - line.startsWith(metricLineStartWith) && - line.endsWith(metricLineEndsWith), - ); - expect(isContainMetricFailureCounter).toBeTruthy(); - }); -}); - -async function fetchMetrics(metricsEndpoitUrl: string): Promise { - const response = await fetch(metricsEndpoitUrl, { - method: "GET", - headers: { "Content-Type": "plain/text" }, - }); - - if (response.status !== 200) - throw new Error("Failed to retrieve metrics from RHDH"); - const data = await response.text(); - - return data.split("\n"); -} diff --git a/e2e-tests/playwright/support/page-objects/catalog/catalog-users-obj.ts b/e2e-tests/playwright/support/page-objects/catalog/catalog-users-obj.ts index 721e0d6f20..d80ea9215d 100644 --- a/e2e-tests/playwright/support/page-objects/catalog/catalog-users-obj.ts +++ b/e2e-tests/playwright/support/page-objects/catalog/catalog-users-obj.ts @@ -1,38 +1,8 @@ -import { Page, Locator } from "@playwright/test"; +import { Page } from "@playwright/test"; export class CatalogUsersPO { static BASE_URL = "/catalog?filters%5Bkind%5D=user&filters%5Buser"; - static getListOfUsers(page: Page): Locator { - // Get all user links in the table's body - // Using rowgroup to target tbody, then getting links within cells - // These links point to /catalog/{namespace}/user/{username} - return page - .getByRole("table") - .first() // Scope to the first table (users table), not pagination table - .getByRole("rowgroup") - .nth(1) // Second rowgroup (data rows), 0-indexed: 0=header, 1=data - .getByRole("cell") - .getByRole("link"); - } - - static getEmailLink(page: Page): Locator { - return page.getByRole("link", { name: /@/ }); - } - - static async visitUserPage(page: Page, username: string) { - // Click on user link in the table by name - await page - .getByRole("table") - .getByRole("link", { name: new RegExp(username, "i") }) - .first() - .click(); - } - - static getGroupLink(page: Page, groupName: string): Locator { - return page.getByRole("link", { name: new RegExp(groupName, "i") }); - } - static async visitBaseURL(page: Page) { await page.goto(this.BASE_URL); } diff --git a/e2e-tests/playwright/utils/keycloak/group.ts b/e2e-tests/playwright/utils/keycloak/group.ts deleted file mode 100644 index 10dc85c636..0000000000 --- a/e2e-tests/playwright/utils/keycloak/group.ts +++ /dev/null @@ -1,7 +0,0 @@ -interface Group { - id: string; - name: string; - path: string; -} - -export default Group; diff --git a/e2e-tests/playwright/utils/keycloak/keycloak.ts b/e2e-tests/playwright/utils/keycloak/keycloak.ts deleted file mode 100644 index bdc3b50e8a..0000000000 --- a/e2e-tests/playwright/utils/keycloak/keycloak.ts +++ /dev/null @@ -1,117 +0,0 @@ -import fetch from "node-fetch"; -import User from "./user"; -import Group from "./group"; -import { expect, Page } from "@playwright/test"; -import { UIhelper } from "../ui-helper"; -import { CatalogUsersPO } from "../../support/page-objects/catalog/catalog-users-obj"; - -interface AuthResponse { - access_token: string; -} -class Keycloak { - private readonly baseURL: string; - private readonly realm: string; - private readonly clientId: string; - private readonly clientSecret: string; - - constructor() { - this.baseURL = Buffer.from( - process.env.KEYCLOAK_AUTH_BASE_URL, - "base64", - ).toString(); - this.realm = Buffer.from( - process.env.KEYCLOAK_AUTH_REALM, - "base64", - ).toString(); - this.clientSecret = Buffer.from( - process.env.KEYCLOAK_AUTH_CLIENT_SECRET, - "base64", - ).toString(); - this.clientId = Buffer.from( - process.env.KEYCLOAK_AUTH_CLIENTID, - "base64", - ).toString(); - } - - async getAuthenticationToken(): Promise { - const response = await fetch( - `${this.baseURL}/auth/realms/${this.realm}/protocol/openid-connect/token`, - { - method: "POST", - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - body: new URLSearchParams({ - grant_type: "client_credentials", - client_id: this.clientId, - client_secret: this.clientSecret, - }).toString(), - }, - ); - - if (response.status !== 200) throw new Error("Failed to authenticate"); - const data = (await response.json()) as AuthResponse; - return data.access_token; - } - - async getUsers(authToken: string): Promise { - const response = await fetch( - `${this.baseURL}/auth/admin/realms/${this.realm}/users`, - { - method: "GET", - headers: { - Authorization: `Bearer ${authToken}`, - }, - }, - ); - - if (response.status !== 200) { - const errorText = await response.text(); - throw new Error(`Failed to get users: ${response.status} - ${errorText}`); - } - return (await response.json()) as Promise; - } - - async getGroupsOfUser(authToken: string, userId: string): Promise { - const response = await fetch( - `${this.baseURL}/auth/admin/realms/${this.realm}/users/${userId}/groups`, - { - method: "GET", - headers: { - Authorization: `Bearer ${authToken}`, - }, - }, - ); - - if (response.status !== 200) { - const errorText = await response.text(); - throw new Error( - `Failed to get groups of user: ${response.status} - ${errorText}`, - ); - } - return (await response.json()) as Promise; - } - - async checkUserDetails( - page: Page, - keycloakUser: User, - token: string, - uiHelper: UIhelper, - keycloak: Keycloak, - ) { - await CatalogUsersPO.visitUserPage(page, keycloakUser.username); - const emailLink = CatalogUsersPO.getEmailLink(page); - await expect(emailLink).toBeVisible(); - await uiHelper.verifyDivHasText( - `${keycloakUser.firstName} ${keycloakUser.lastName}`, - ); - - const groups = await keycloak.getGroupsOfUser(token, keycloakUser.id); - for (const group of groups) { - const groupLink = CatalogUsersPO.getGroupLink(page, group.name); - await expect(groupLink).toBeVisible(); - } - - await CatalogUsersPO.visitBaseURL(page); - } -} - -export default Keycloak; diff --git a/e2e-tests/playwright/utils/keycloak/user.ts b/e2e-tests/playwright/utils/keycloak/user.ts deleted file mode 100644 index 8403fda945..0000000000 --- a/e2e-tests/playwright/utils/keycloak/user.ts +++ /dev/null @@ -1,9 +0,0 @@ -interface User { - id: string; - username: string; - firstName: string; - lastName: string; - email: string; -} - -export default User; diff --git a/e2e-tests/yarn.lock b/e2e-tests/yarn.lock index 7afd2d7f30..3d08609eaa 100644 --- a/e2e-tests/yarn.lock +++ b/e2e-tests/yarn.lock @@ -1679,7 +1679,6 @@ __metadata: ioredis: "npm:5.9.3" isomorphic-fetch: "npm:3.0.0" js-yaml: "npm:4.1.1" - node-fetch: "npm:2.7.0" octokit: "npm:4.1.4" otplib: "npm:12.0.1" pg: "npm:8.18.0" @@ -2970,7 +2969,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.7.0, node-fetch@npm:^2.6.1": +"node-fetch@npm:^2.6.1": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: