Skip to content

test: (e2e) add gitlab authentication tests - #3987

Merged
openshift-merge-bot[bot] merged 23 commits into
redhat-developer:mainfrom
albarbaro:add-gitlab-test
Jan 28, 2026
Merged

test: (e2e) add gitlab authentication tests#3987
openshift-merge-bot[bot] merged 23 commits into
redhat-developer:mainfrom
albarbaro:add-gitlab-test

Conversation

@albarbaro

@albarbaro albarbaro commented Jan 12, 2026

Copy link
Copy Markdown
Member

Description

Support Gitlab Auth Provider.

Which issue(s) does this PR fix

PR acceptance criteria

Please make sure that the following steps are complete:

  • GitHub Actions are completed and successful
  • Unit Tests are updated and passing
  • E2E Tests are updated and passing
  • Documentation is updated if necessary (requirement for new features)
  • Add a screenshot if the change is UX/UI related

How to test changes / Special notes to the reviewer

@albarbaro

Copy link
Copy Markdown
Member Author

/test ?

@openshift-ci

openshift-ci Bot commented Jan 12, 2026

Copy link
Copy Markdown

@albarbaro: The following commands are available to trigger required jobs:

/test e2e-ocp-helm

The following commands are available to trigger optional jobs:

/test cleanup-mapt-destroy-orphaned-aks-clusters
/test cleanup-mapt-destroy-orphaned-eks-clusters
/test e2e-aks-helm-nightly
/test e2e-aks-operator-nightly
/test e2e-eks-helm-nightly
/test e2e-eks-operator-nightly
/test e2e-gke-helm-nightly
/test e2e-gke-operator-nightly
/test e2e-ocp-helm-nightly
/test e2e-ocp-helm-upgrade-nightly
/test e2e-ocp-operator-auth-providers-nightly
/test e2e-ocp-operator-nightly
/test e2e-ocp-v4-17-helm-nightly
/test e2e-ocp-v4-19-helm-nightly
/test e2e-ocp-v4-20-helm-nightly
/test e2e-osd-gcp-helm-nightly
/test e2e-osd-gcp-operator-nightly

Use /test all to run the following jobs that were automatically triggered:

pull-ci-redhat-developer-rhdh-main-e2e-ocp-helm
Details

In response to this:

/test ?

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🔒 Security concerns

Sensitive information exposure:
The test provisions and stores sensitive values (e.g., AUTH_PROVIDERS_GITLAB_TOKEN, AUTH_PROVIDERS_GITLAB_CLIENT_SECRET) into a Kubernetes secret via deployment.addSecretData(...). Ensure these secrets are never printed in logs and that CI logs/artifacts do not capture them. Also validate that the GitLab personal access token used for /api/v4/applications has least-privilege and is scoped appropriately, since it can create trusted OAuth applications.

⚡ Recommended focus areas for review

Test Structure

The suite uses an async callback in test.describe and performs await calls at module scope to compute URLs. Both patterns can be problematic in Playwright depending on the runner/TS compilation settings (e.g., describe callbacks are expected to be synchronous and top-level awaits may not be supported in the emitted module format). Consider moving async setup into beforeAll and keeping describe synchronous to avoid test discovery/initialization issues.

test.describe("Configure GitLab Provider", async () => {
  let common: Common;
  let uiHelper: UIhelper;
  let gitlabHelper: GitLabHelper;
  let oauthAppId: number | null = null;

  const namespace = "albarbaro-test-namespace-gitlab";
  const appConfigMap = "app-config-rhdh";
  const rbacConfigMap = "rbac-policy";
  const dynamicPluginsConfigMap = "dynamic-plugins";
  const secretName = "rhdh-secrets";

  // set deployment instance
  const deployment: RHDHDeployment = new RHDHDeployment(
    namespace,
    appConfigMap,
    rbacConfigMap,
    dynamicPluginsConfigMap,
    secretName,
  );
  deployment.instanceName = "rhdh";

  // compute backstage baseurl
  const backstageUrl = await deployment.computeBackstageUrl();
  const backstageBackendUrl = await deployment.computeBackstageBackendUrl();
  console.log(`Backstage BaseURL is: ${backstageUrl}`);

  test.use({ baseURL: backstageUrl });
Test Selection

The PW_PROJECT.ANY_TEST project is now restricted to only the GitLab spec via testMatch. This changes previous behavior that allowed running any spec in that project and may break existing workflows/documentation that rely on ANY_TEST for arbitrary execution. Verify this is intended (or consider a separate project for GitLab rather than repurposing ANY_TEST).

  name: PW_PROJECT.ANY_TEST,
  testMatch: "**/auth-providers/gitlab.spec.ts", // Run only GitLab auth provider tests
},
Resource Cleanup

The test creates real external resources (a GitLab OAuth application and a Kubernetes namespace/deployment) but cleanup appears to only delete the OAuth app and kill a process. Confirm the namespace/configmaps/secrets/deployment are reliably cleaned up to avoid leaking infra resources across runs (especially when tests fail before afterAll), and ensure oauthAppId corresponds to the correct identifier expected by the GitLab delete endpoint.

  // Initialize GitLab helper and create OAuth application dynamically
  gitlabHelper = new GitLabHelper({
    host: process.env.AUTH_PROVIDERS_GITLAB_HOST!,
    personalAccessToken: process.env.AUTH_PROVIDERS_GITLAB_TOKEN!,
  });

  const callbackUrl = `${backstageBackendUrl}/api/auth/gitlab/handler/frame`;
  const oauthAppName = `rhdh-test-${Date.now()}`;
  console.log(`[TEST] Creating GitLab OAuth application: ${oauthAppName}`);
  const oauthApp = await gitlabHelper.createOAuthApplication(
    oauthAppName,
    callbackUrl,
    "api read_user write_repository sudo",
    true, // trusted = true to skip UI confirmation
  );
  oauthAppId = oauthApp.id;
  console.log(
    `[TEST] GitLab OAuth application created - ID: ${oauthApp.application_id}`,
  );

  // clean old namespaces
  await deployment.deleteNamespaceIfExists();

  // create namespace and wait for it to be active
  await (await deployment.createNamespace()).waitForNamespaceActive();

  // create all base configmaps
  await deployment.createAllConfigs();

  // generate static token
  await deployment.generateStaticToken();

  // set enviroment variables and create secret
  if (!process.env.ISRUNNINGLOCAL) {
    await deployment.addSecretData("BASE_URL", backstageUrl);
    await deployment.addSecretData("BASE_BACKEND_URL", backstageBackendUrl);
  }
  await deployment.addSecretData(
    "AUTH_PROVIDERS_GITLAB_HOST",
    process.env.AUTH_PROVIDERS_GITLAB_HOST!,
  );
  await deployment.addSecretData(
    "AUTH_PROVIDERS_GITLAB_CLIENT_ID",
    oauthApp.application_id,
  );
  await deployment.addSecretData(
    "AUTH_PROVIDERS_GITLAB_CLIENT_SECRET",
    oauthApp.secret,
  );
  await deployment.addSecretData(
    "AUTH_PROVIDERS_GITLAB_TOKEN",
    process.env.AUTH_PROVIDERS_GITLAB_TOKEN!,
  );

  await deployment.createSecret();

  // enable gitlab login with ingestion
  console.log("[TEST] Enabling GitLab login with ingestion...");
  await deployment.enableGitlabLoginWithIngestion();
  await deployment.updateAllConfigs();
  console.log("[TEST] GitLab login with ingestion enabled successfully");

  // create backstage deployment and wait for it to be ready
  await deployment.createBackstageDeployment();
  await deployment.waitForDeploymentReady();

  // wait for rhdh first sync and portal to be reachable
  await deployment.waitForSynced();
});

test.beforeEach(async () => {
  test.info().setTimeout(600 * 1000);
  console.log(
    `Running test case ${test.info().title} - Attempt #${test.info().retry}`,
  );
});

test("Login with GitLab default resolver", async () => {
  test.setTimeout(10 * 60 * 1000); // 10 minutes timeout

  const login = await common.gitlabLogin(
    "user1",
    process.env.DEFAULT_USER_PASSWORD,
  );
  expect(login).toBe("Login successful");

  await uiHelper.goToPageUrl("/settings", "Settings");
  await uiHelper.verifyHeading("user1");
  await common.signOut();
  await context.clearCookies();
});

test(`Ingestion of GitLab users and groups: verify the user entities and groups are created with the correct relationships`, async () => {
  test.setTimeout(300 * 1000);
  await page.waitForTimeout(5000);

  expect(
    await deployment.checkUserIsIngestedInCatalog([
      "user1",
      "user2",
      "Administrator"
    ]),
  ).toBe(true);
  expect(
    await deployment.checkGroupIsIngestedInCatalog([
      "group1",
      "all",
      "nested",
    ]),
  ).toBe(true);
  expect(
    await deployment.checkUserIsInGroup("user1", "group1"),
  ).toBe(true);
  expect(
    await deployment.checkUserIsInGroup("user2", "group1"),
  ).toBe(true);
  expect(
    await deployment.checkUserIsInGroup("root", "group1"),
  ).toBe(true);
  expect(
    await deployment.checkUserIsInGroup("user1", "nested"),
  ).toBe(true);
  expect(
    await deployment.checkUserIsInGroup("user2", "nested"),
  ).toBe(true);
  expect(
    await deployment.checkUserIsInGroup("root", "nested"),
  ).toBe(true);


});

test.afterAll(async () => {
  console.log("[TEST] Starting cleanup...");

  // Delete the dynamically created OAuth application
  if (oauthAppId !== null && gitlabHelper) {
    try {
      await gitlabHelper.deleteOAuthApplication(oauthAppId);
      console.log("[TEST] GitLab OAuth application deleted successfully");
    } catch (error) {
      console.error(
        "[TEST] Failed to delete GitLab OAuth application:",
        error,
      );
    }
  }

  await deployment.killRunningProcess();
  console.log("[TEST] Cleanup completed");
});
📄 References
  1. No matching references available

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Type

Tests, Enhancement


Description

  • Add comprehensive GitLab authentication provider E2E tests with OAuth app creation/deletion

  • Implement GitLabHelper utility for GitLab API interactions and OAuth application management

  • Enable GitLab login with ingestion in RHDH deployment configuration

  • Add GitLab popup login handler and user authentication flow in Common utility

  • Update deployment log regex to recognize GitLab sync events


File Walkthrough

Relevant files
Configuration changes
playwright.config.ts
Configure test runner for GitLab auth tests                           

e2e-tests/playwright.config.ts

  • Modified ANY_TEST project to run only GitLab authentication tests
    instead of all tests
  • Changed testMatch pattern from "**/*.spec.ts" to
    "**/auth-providers/gitlab.spec.ts"
+1/-1     
Tests
gitlab.spec.ts
Add GitLab authentication provider E2E test suite               

e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts

  • New comprehensive E2E test suite for GitLab authentication provider
    configuration
  • Tests GitLab OAuth app creation, login flow, and user/group ingestion
  • Validates user entities and group relationships in catalog
  • Includes setup/teardown with dynamic OAuth app lifecycle management
+210/-0 
Enhancement
gitlab-helper.ts
Add GitLab API helper utility class                                           

e2e-tests/playwright/utils/authentication-providers/gitlab-helper.ts

  • New GitLabHelper class for GitLab API interactions
  • Implements OAuth application creation with configurable scopes and
    trusted flag
  • Provides OAuth app deletion and listing functionality
  • Includes error handling and logging for API operations
+178/-0 
rhdh-deployment.ts
Add GitLab deployment configuration methods                           

e2e-tests/playwright/utils/authentication-providers/rhdh-deployment.ts

  • Updated syncedLogRegex to recognize GitLab sync events alongside
    existing providers
  • Added enableGitlabLoginWithIngestion() method to configure GitLab
    catalog provider
  • Added setGitlabResolver() method for GitLab sign-in resolver
    configuration
  • Fixed typo in checkUserIsInGroup log message from "groups" to "user"
+89/-2   
common.ts
Add GitLab login handler and authentication flow                 

e2e-tests/playwright/utils/common.ts

  • Added handleGitlabPopupLogin() private method for GitLab OAuth popup
    interaction
  • Implements GitLab login form filling with username/password and 2FA
    handling
  • Added gitlabLogin() public method for GitLab authentication flow
  • Handles authorization button clicks and popup closure detection
+73/-0   
Documentation
README.md
Update documentation for GitLab provider support                 

e2e-tests/playwright/e2e/auth-providers/README.md

  • Added GitLab to list of supported authentication providers
  • Added backstage-plugin-catalog-backend-module-gitlab-org-dynamic to
    required plugins
+2/-0     

@albarbaro albarbaro changed the title test (e2e): Add gitlab test test: (e2e) add gitlab authentication tests Jan 12, 2026
@rhdh-qodo-merge

rhdh-qodo-merge Bot commented Jan 12, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Correct dynamic plugin identifier

Replace the relative path with the correct package name
backstage-plugin-catalog-backend-module-gitlab-org-dynamic when calling
setDynamicPluginEnabled.

e2e-tests/playwright/utils/authentication-providers/rhdh-deployment.ts [1296-1299]

 this.setDynamicPluginEnabled(
-  "./dynamic-plugins/dist/backstage-plugin-catalog-backend-module-gitlab-org-dynamic",
+  "backstage-plugin-catalog-backend-module-gitlab-org-dynamic",
   true,
 );
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion corrects a critical error where a relative path is used instead of the package name, which would cause the setDynamicPluginEnabled function to fail and prevent the GitLab plugin from being enabled.

High
Remove async from describe

Remove the async modifier from the test.describe callback function to prevent
potential race conditions and ensure correct test registration.

e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts [9]

-test.describe("Configure GitLab Provider", async () => {
+test.describe("Configure GitLab Provider", () => {
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that using async with test.describe is an anti-pattern that can cause race conditions and prevents top-level await operations from working as expected, which is critical for test setup.

Medium
High-level
Use dynamic namespaces in E2E tests

The new GitLab E2E test uses a hardcoded Kubernetes namespace. To prevent test
collisions in CI, this should be replaced with a dynamically generated unique
namespace.

Examples:

e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts [15]
  const namespace = "albarbaro-test-namespace-gitlab";

Solution Walkthrough:

Before:

// e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts

test.describe("Configure GitLab Provider", async () => {
  // ...
  const namespace = "albarbaro-test-namespace-gitlab";
  // ...
  const deployment: RHDHDeployment = new RHDHDeployment(
    namespace,
    appConfigMap,
    rbacConfigMap,
    dynamicPluginsConfigMap,
    secretName,
  );
  // ...
});

After:

// e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts
import { v4 as uuidv4 } from 'uuid';

test.describe("Configure GitLab Provider", async () => {
  // ...
  const namespace = `gitlab-test-${uuidv4().slice(0, 8)}`;
  // ...
  const deployment: RHDHDeployment = new RHDHDeployment(
    namespace,
    appConfigMap,
    rbacConfigMap,
    dynamicPluginsConfigMap,
    secretName,
  );
  // ...
});
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a hardcoded namespace in the new E2E test, a critical flaw that will cause test collisions and failures in a CI environment.

Medium
General
Replace fixed wait with polling

Replace the fixed waitForTimeout(5000) with expect.toPass to create a reliable
polling mechanism that waits for user and group ingestion to complete.

e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts [150-188]

 test(`Ingestion of GitLab users and groups: verify the user entities and groups are created with the correct relationships`, async () => {
   test.setTimeout(300 * 1000);
-  await page.waitForTimeout(5000);
-
-  expect(
-    await deployment.checkUserIsIngestedInCatalog([
-      "user1",
-      "user2",
-      "Administrator"
-    ]),
-  ).toBe(true);
-  expect(
-    await deployment.checkGroupIsIngestedInCatalog([
-      "group1",
-      "all",
-      "nested",
-    ]),
-  ).toBe(true);
-  expect(
-    await deployment.checkUserIsInGroup("user1", "group1"),
-  ).toBe(true);
-  expect(
-    await deployment.checkUserIsInGroup("user2", "group1"),
-  ).toBe(true);
-  expect(
-    await deployment.checkUserIsInGroup("root", "group1"),
-  ).toBe(true);
-  expect(
-    await deployment.checkUserIsInGroup("user1", "nested"),
-  ).toBe(true);
-  expect(
-    await deployment.checkUserIsInGroup("user2", "nested"),
-  ).toBe(true);
-  expect(
-    await deployment.checkUserIsInGroup("root", "nested"),
-  ).toBe(true);
-    
-
+  
+  await expect(async () => {
+    expect(
+      await deployment.checkUserIsIngestedInCatalog([
+        "user1",
+        "user2",
+        "Administrator"
+      ]),
+    ).toBe(true);
+    expect(
+      await deployment.checkGroupIsIngestedInCatalog([
+        "group1",
+        "all",
+        "nested",
+      ]),
+    ).toBe(true);
+    expect(
+      await deployment.checkUserIsInGroup("user1", "group1"),
+    ).toBe(true);
+    expect(
+      await deployment.checkUserIsInGroup("user2", "group1"),
+    ).toBe(true);
+    expect(
+      await deployment.checkUserIsInGroup("root", "group1"),
+    ).toBe(true);
+    expect(
+      await deployment.checkUserIsInGroup("user1", "nested"),
+    ).toBe(true);
+    expect(
+      await deployment.checkUserIsInGroup("user2", "nested"),
+    ).toBe(true);
+    expect(
+      await deployment.checkUserIsInGroup("root", "nested"),
+    ).toBe(true);
+  }).toPass({ timeout: 120_000 });
 });
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that page.waitForTimeout is an anti-pattern and proposes a robust polling mechanism using expect.toPass, which significantly improves test reliability.

Medium
Accept multiple login outcomes

Modify the assertion to accept both "Login successful" and "Already logged in"
as valid outcomes, making the test more robust.

e2e-tests/playwright/e2e/auth-providers/gitlab.spec.ts [142]

-expect(login).toBe("Login successful");
+expect(["Login successful", "Already logged in"]).toContain(login);
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the test should handle cases where the user is already logged in, making the test more robust and less prone to failure in different states.

Medium
Optimize login check with Promise.race

Use Promise.race to concurrently wait for either the login popup to close or the
login form to appear, avoiding a fixed timeout and speeding up test execution.

e2e-tests/playwright/utils/common.ts [402-408]

-// Check if popup closes automatically
-try {
-  await popup.waitForEvent("close", { timeout: 5000 });
+// Check if popup closes automatically or if login form is visible
+const closePromise = popup.waitForEvent("close", { timeout: 5000 });
+const loginFormPromise = popup.locator("#user_login").waitFor({ state: 'visible', timeout: 5000 });
+
+const result = await Promise.race([
+  closePromise.then(() => 'closed'),
+  loginFormPromise.then(() => 'login_visible')
+]).catch(() => 'timeout');
+
+if (result === 'closed') {
   return "Already logged in";
-} catch {
-  // Popup didn't close, proceed with login
 }
+// if result is 'login_visible' or 'timeout', proceed with login
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This is a good performance optimization that correctly identifies an unnecessary delay and proposes an elegant solution using Promise.race to make the test helper more efficient.

Low
  • Update

@albarbaro

Copy link
Copy Markdown
Member Author

/test e2e-ocp-operator-auth-providers-nightly

@albarbaro
albarbaro requested review from JessicaJHee, alizard0 and kim-tsao and removed request for josephca and psrna January 12, 2026 11:51
@github-actions

Copy link
Copy Markdown
Contributor

The image is available at:

/test e2e-ocp-helm

@github-actions

Copy link
Copy Markdown
Contributor

🚫 Image Push Skipped.

The container image push was skipped because the build was skipped (either due to [skip-build] tag or no relevant changes with existing image)

/test e2e-ocp-helm

@albarbaro

Copy link
Copy Markdown
Member Author

/test e2e-ocp-operator-auth-providers-nightly

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 7 days with no activity. Remove stale label or comment or this will be closed in 21 days.

@github-actions github-actions Bot added the Stale label Jan 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@albarbaro

Copy link
Copy Markdown
Member Author

/test e2e-ocp-helm

@rhdh-qodo-merge

Copy link
Copy Markdown
ⓘ Your monthly quota for Qodo has expired. Upgrade your plan
ⓘ Paying users. Check that your Qodo account is linked with this Git user account

@albarbaro

Copy link
Copy Markdown
Member Author

/test e2e-ocp-helm

@rhdh-qodo-merge

Copy link
Copy Markdown
ⓘ Your monthly quota for Qodo has expired. Upgrade your plan
ⓘ Paying users. Check that your Qodo account is linked with this Git user account

@JessicaJHee JessicaJHee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@albarbaro

Copy link
Copy Markdown
Member Author

/test e2e-ocp-helm

@rhdh-qodo-merge

Copy link
Copy Markdown
ⓘ Your monthly quota for Qodo has expired. Upgrade your plan
ⓘ Paying users. Check that your Qodo account is linked with this Git user account

@github-actions

Copy link
Copy Markdown
Contributor

@alizard0

Copy link
Copy Markdown
Member

/lgtm

@openshift-ci openshift-ci Bot added the lgtm label Jan 28, 2026
@openshift-ci openshift-ci Bot removed the lgtm label Jan 28, 2026
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

@JessicaJHee

Copy link
Copy Markdown
Member

/lgtm

@openshift-ci openshift-ci Bot added the lgtm label Jan 28, 2026
@openshift-ci

openshift-ci Bot commented Jan 28, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alizard0, JessicaJHee

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-bot
openshift-merge-bot Bot merged commit 662d5d0 into redhat-developer:main Jan 28, 2026
20 checks passed
@openshift-cherrypick-robot

Copy link
Copy Markdown
Contributor

@albarbaro: new pull request created: #4094

Details

In response to this:

/cherrypick release-1.9

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants