Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Add e2e tests for git exercise participation using ssh and token #10006

Merged
merged 38 commits into from
Dec 20, 2024

Conversation

muradium
Copy link
Contributor

@muradium muradium commented Dec 11, 2024

Checklist

General

Client

Motivation and Context

We want to test the scenario where students make programming exercise submissions by Git using SSH.

Description

Adds 2 E2E tests in Playwright about using RSA and ED25519 encrypted SSH keys for exercise submissions. For simplicity of test setup, SSH keys intented for using in Playwright have been generated inside src/test/playwright/ssh-keys folder. These keys are automatically copied to the ssh keys storage of the machine by Playwright global script global-script.ts, which is executed before tests are ran.

Steps for Testing

  • Code Review: Ensure that tests pass for valid reasons, functionality is adequately tested and check the code quality.
  • Run the tests (optional): Tests are executed during automatic run in CI environment. You can run them locally and check if they pass.

Steps for running the tests:

  1. Navigate to src/test/playwright
  2. Configure Playwright using playwright.env file based on your local setup. Current configuration should work for default Artemis setup.
  3. Run npm install && npm run playwright:setup
  4. Run the test group using one of the methods:
  • Run npx playwright test e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts -g "Programming exercise participation using secure git"
  • Run npm run playwright:open to open the Playwright UI, search for the "Programming exercise participation using secure git" test suite and run it
  1. Confirm that the "Makes a git submission using SSH with {RSA/ED25519} key" tests pass

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked







Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Summary by CodeRabbit

  • New Features

    • Introduced new configuration variables for SSH settings in the local environment.
    • Added data-testid attributes to various components for improved test accessibility.
    • Enhanced functionality for cloning repositories with SSH authentication.
    • Introduced methods for managing Git clone operations and copying clone URLs.
    • Added a new class for managing SSH public keys through API requests.
  • Bug Fixes

    • Improved error handling and visibility checks for SSH key requirements in tests.
  • Documentation

    • Updated comments and headers in configuration files for clarity.
  • Chores

    • Added new SSH key files for testing purposes.
    • Updated .gitignore to include additional ignored files and organized existing entries.

…it url instead of container name in the URLs"

This reverts commit f0a7d7c.
@github-actions github-actions bot added tests client Pull requests that update TypeScript code. (Added Automatically!) docker playwright labels Dec 11, 2024
@muradium muradium changed the title Development: E2E test for exercise participation using git via SSH Development: E2E tests for exercise participation using git via SSH Dec 12, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (1)
src/test/playwright/init/global-setup.ts (1)

1-24: Security: Review file operations outside Artemis directory

As noted in previous reviews, the script operates on files outside the Artemis directory. This could pose security risks and make the testing environment less isolated.

Consider:

  1. Moving SSH keys within the Artemis test directory structure
  2. Adding validation to ensure file operations only occur within allowed paths
  3. Implementing a secure key management strategy that doesn't rely on filesystem operations

Would you like assistance in designing a more secure key management approach?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fba5ac and 13ad227.

📒 Files selected for processing (4)
  • .gitignore (1 hunks)
  • src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (3 hunks)
  • src/test/playwright/init/global-setup.ts (1 hunks)
  • src/test/playwright/support/pageobjects/exercises/programming/GitClient.ts (1 hunks)
🔇 Additional comments (4)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (4)

8-21: LGTM: Import statements are well-organized

The new imports for SSH-related functionality are properly organized and necessary for the implementation.


84-91: LGTM: Test description is more specific

The test description now clearly indicates that it's testing HTTPS-based git submissions, improving test clarity.


300-311: Add error handling for SSH key setup

The function should handle potential errors when reading SSH key files and interacting with the UI.

This issue was previously identified in a past review. Please implement the suggested error handling.


92-120: 🛠️ Refactor suggestion

Add error handling for SSH key setup failures

While the test suite is well-structured, it should handle potential SSH key setup failures gracefully.

Apply this diff to add error handling:

 test(`Makes a git submission using SSH with ${sshAlgorithm} key`, async ({ page, programmingExerciseOverview }) => {
+    try {
         await programmingExerciseOverview.startParticipation(course.id!, exercise.id!, studentOne);
         await makeGitExerciseSubmission(
             page,
             programmingExerciseOverview,
             course,
             exercise,
             studentOne,
             javaAllSuccessfulSubmission,
             'Solution',
             GitCloneMethod.ssh,
             sshAlgorithm,
         );
+    } catch (error) {
+        console.error(`Failed to setup SSH key (${sshAlgorithm}):`, error);
+        throw error;
+    }
 });

Also, verify that the SSH keys are properly configured:

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 14, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (2)

100-114: Enhance test descriptions with expected outcomes

The test descriptions could be more specific about what constitutes a successful test.

Apply this diff:

-test(`Makes a git submission using SSH with ${sshAlgorithm} key`, async ({ page, programmingExerciseOverview }) => {
+test(`Successfully clones and pushes using SSH with ${sshAlgorithm} key, expecting successful build`, async ({ page, programmingExerciseOverview }) => {

281-282: Make CI URL replacement more robust

The URL replacement for CI environment could be more robust using URL parsing.

Apply this diff:

-if (process.env.CI === 'true' && (cloneMethod == GitCloneMethod.https || cloneMethod == GitCloneMethod.ssh)) {
-    repoUrl = repoUrl.replace(/(?<=@).+(?=:)/, 'artemis-app');
+if (process.env.CI === 'true') {
+    try {
+        const url = new URL(repoUrl.replace(':', '/').replace('git@', 'https://'));
+        url.hostname = 'artemis-app';
+        repoUrl = url.toString()
+            .replace('https://', cloneMethod === GitCloneMethod.ssh ? 'git@' : 'https://')
+            .replace('/', ':');
+    } catch (error) {
+        throw new Error(`Failed to parse repository URL: ${error.message}`);
+    }
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13ad227 and ec012fa.

📒 Files selected for processing (1)
  • src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (3 hunks)
🔇 Additional comments (2)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (2)

304-315: ⚠️ Potential issue

Add error handling and cleanup to SSH setup

The function needs better error handling and resource cleanup.

Previous review already noted the need for error handling when reading SSH key files. Additionally:

  1. Add SSH key format validation
  2. Ensure browser context cleanup

Apply this diff:

 async function setupSSHCredentials(context: BrowserContext, sshAlgorithm: SshEncryptionAlgorithm) {
     console.log(`Setting up SSH credentials with key ${SSH_KEY_NAMES[sshAlgorithm]}`);
     const page = await context.newPage();
-    const sshKeyPath = path.join(SSH_KEYS_PATH, `${SSH_KEY_NAMES[sshAlgorithm]}.pub`);
-    const sshKey = await fs.readFile(sshKeyPath, 'utf8');
-    await page.goto('user-settings/ssh');
-    await page.getByTestId('addNewSshKeyButton').click();
-    await page.getByTestId('sshKeyField').fill(sshKey!);
-    const responsePromise = page.waitForResponse(`${BASE_API}/ssh-settings/public-key`);
-    await page.getByTestId('saveSshKeyButton').click();
-    await responsePromise;
+    try {
+        const sshKeyPath = path.join(SSH_KEYS_PATH, `${SSH_KEY_NAMES[sshAlgorithm]}.pub`);
+        const sshKey = await fs.readFile(sshKeyPath, 'utf8');
+        
+        // Validate SSH key format
+        if (!sshKey.trim().startsWith('ssh-')) {
+            throw new Error('Invalid SSH key format');
+        }
+        
+        await page.goto('user-settings/ssh');
+        await page.getByTestId('addNewSshKeyButton').click();
+        await page.getByTestId('sshKeyField').fill(sshKey);
+        const responsePromise = page.waitForResponse(`${BASE_API}/ssh-settings/public-key`);
+        await page.getByTestId('saveSshKeyButton').click();
+        await responsePromise;
+    } catch (error) {
+        throw new Error(`Failed to setup SSH credentials: ${error.message}`);
+    } finally {
+        await page.close();
+    }
 }

271-279: ⚠️ Potential issue

Improve SSH validation and error handling

The SSH-specific logic needs better validation and error handling.

Apply this diff:

 await programmingExerciseOverview.openCloneMenu(cloneMethod);
+if (cloneMethod === GitCloneMethod.ssh && !sshAlgorithm) {
+    throw new Error('SSH algorithm must be provided when using SSH clone method');
+}
 if (cloneMethod == GitCloneMethod.ssh) {
     await expect(programmingExerciseOverview.getCloneUrlButton()).toBeDisabled();
     const sshKeyNotFoundAlert = page.locator('.alert', { hasText: 'To use ssh, you need to add an ssh key to your account' });
     await expect(sshKeyNotFoundAlert).toBeVisible();
-    await setupSSHCredentials(page.context(), sshAlgorithm);
+    try {
+        await setupSSHCredentials(page.context(), sshAlgorithm);
+    } catch (error) {
+        throw new Error(`Failed to setup SSH credentials: ${error.message}`);
+    }
     await page.reload();
     await programmingExerciseOverview.openCloneMenu(cloneMethod);
 }

Likely invalid or redundant comment.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 14, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (1)

100-115: Add assertions to verify SSH key setup

While the tests cover both RSA and ED25519 keys, consider adding assertions to verify:

  1. The SSH key was successfully added
  2. The clone URL is enabled after key setup
 test(`Makes a git submission using SSH with ${sshAlgorithm} key`, async ({ page, programmingExerciseOverview }) => {
     await programmingExerciseOverview.startParticipation(course.id!, exercise.id!, studentOne);
+    // Verify initial state (no SSH key)
+    await programmingExerciseOverview.openCloneMenu(GitCloneMethod.ssh);
+    await expect(programmingExerciseOverview.getCloneUrlButton()).toBeDisabled();
+
+    // Setup SSH key and verify
+    await setupSSHCredentials(page.context(), sshAlgorithm);
+    await page.reload();
+    await programmingExerciseOverview.openCloneMenu(GitCloneMethod.ssh);
+    await expect(programmingExerciseOverview.getCloneUrlButton()).toBeEnabled();
+
     await makeGitExerciseSubmission(
         page,
         programmingExerciseOverview,
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec012fa and 02f6798.

⛔ Files ignored due to path filters (1)
  • docker/playwright-E2E-tests-mysql-localci.yml is excluded by !**/*.yml
📒 Files selected for processing (1)
  • src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (3 hunks)
🔇 Additional comments (5)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (5)

8-21: LGTM: Import statements are well-organized

The new imports for SSH-related functionality are properly organized and necessary for the implementation.


84-84: LGTM: Test description is more explicit

The updated test description clearly distinguishes this test case from the new SSH-based tests.


117-119: Add error handling for SSH key cleanup

The SSH key cleanup in the afterEach hook should handle potential errors to ensure proper cleanup.


268-269: Add validation for SSH algorithm parameter

The function should validate that sshAlgorithm is provided when using SSH clone method.


301-312: Handle potential exceptions when reading SSH key file

The function should handle potential errors when reading the SSH key file.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (1)

116-129: Enhance test descriptions with expected outcomes

The test descriptions could be more explicit about the expected outcomes to improve test readability and maintenance.

Consider updating the test description to include the expected outcome:

-test(`Makes a git submission using SSH with ${sshAlgorithm} key`, async ({ page, programmingExerciseOverview }) => {
+test(`Successfully submits exercise solution via SSH using ${sshAlgorithm} key and verifies build result`, async ({ page, programmingExerciseOverview }) => {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c9eb4b and 6af526c.

📒 Files selected for processing (1)
  • src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (3 hunks)
🔇 Additional comments (4)
src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts (4)

303-308: 🛠️ Refactor suggestion

Add error handling for Git operations

The Git clone operation should handle potential errors gracefully.

Apply this diff:

-    let exerciseRepo;
-    if (cloneMethod == GitCloneMethod.ssh) {
-        exerciseRepo = await gitClient.cloneRepo(repoUrl, repoName, SSH_KEY_NAMES[sshAlgorithm]);
-    } else {
-        exerciseRepo = await gitClient.cloneRepo(repoUrl, repoName);
-    }
+    let exerciseRepo;
+    try {
+        if (cloneMethod == GitCloneMethod.ssh) {
+            exerciseRepo = await gitClient.cloneRepo(repoUrl, repoName, SSH_KEY_NAMES[sshAlgorithm]);
+        } else {
+            exerciseRepo = await gitClient.cloneRepo(repoUrl, repoName);
+        }
+    } catch (error) {
+        console.error(`Failed to clone repository: ${error.message}`);
+        throw new Error(`Repository clone failed: ${error.message}`);
+    }

Likely invalid or redundant comment.


132-134: 🛠️ Refactor suggestion

Improve error handling in the afterEach hook

The SSH key cleanup could fail silently. Consider adding error handling to ensure cleanup issues are properly logged and don't mask test failures.

Apply this diff:

 test.afterEach('Delete SSH key', async ({ accountManagementAPIRequests }) => {
-    await accountManagementAPIRequests.deleteSshPublicKey();
+    try {
+        await accountManagementAPIRequests.deleteSshPublicKey();
+    } catch (error) {
+        console.error('Failed to delete SSH key:', error);
+        throw error; // Re-throw to ensure test failure
+    }
 });

Likely invalid or redundant comment.


317-329: 🛠️ Refactor suggestion

Improve error handling and resource cleanup in SSH setup

The function needs better error handling for file operations and proper cleanup of browser resources.

Apply this diff:

 async function setupSSHCredentials(context: BrowserContext, sshAlgorithm: SshEncryptionAlgorithm) {
     console.log(`Setting up SSH credentials with key ${SSH_KEY_NAMES[sshAlgorithm]}`);
     const page = await context.newPage();
-    const sshKeyPath = path.join(SSH_KEYS_PATH, `${SSH_KEY_NAMES[sshAlgorithm]}.pub`);
-    const sshKey = await fs.readFile(sshKeyPath, 'utf8');
-    await page.goto('user-settings/ssh');
-    await page.getByTestId('addNewSshKeyButton').click();
-    await page.getByTestId('sshKeyField').fill(sshKey!);
-    const responsePromise = page.waitForResponse(`${BASE_API}/ssh-settings/public-key`);
-    await page.getByTestId('saveSshKeyButton').click();
-    await responsePromise;
-    await page.close();
+    try {
+        const sshKeyPath = path.join(SSH_KEYS_PATH, `${SSH_KEY_NAMES[sshAlgorithm]}.pub`);
+        let sshKey: string;
+        try {
+            sshKey = await fs.readFile(sshKeyPath, 'utf8');
+            if (!sshKey.trim().startsWith('ssh-')) {
+                throw new Error('Invalid SSH key format');
+            }
+        } catch (error) {
+            throw new Error(`Failed to read SSH key from ${sshKeyPath}: ${error.message}`);
+        }
+        
+        await page.goto('user-settings/ssh');
+        await page.getByTestId('addNewSshKeyButton').click();
+        await page.getByTestId('sshKeyField').fill(sshKey);
+        const responsePromise = page.waitForResponse(`${BASE_API}/ssh-settings/public-key`);
+        await page.getByTestId('saveSshKeyButton').click();
+        await responsePromise;
+    } finally {
+        await page.close();
+    }
 }

Likely invalid or redundant comment.


287-299: 🛠️ Refactor suggestion

Add validation for SSH parameters and improve URL handling

The function should validate SSH parameters and handle repository URLs more robustly.

Apply these improvements:

+    if (cloneMethod === GitCloneMethod.ssh && !sshAlgorithm) {
+        throw new Error('SSH algorithm must be provided when using SSH clone method');
+    }
+
     await programmingExerciseOverview.openCloneMenu(cloneMethod);
     if (cloneMethod == GitCloneMethod.ssh) {
         await expect(programmingExerciseOverview.getCloneUrlButton()).toBeDisabled();
         const sshKeyNotFoundAlert = page.locator('.alert', { hasText: 'To use ssh, you need to add an ssh key to your account' });
         await expect(sshKeyNotFoundAlert).toBeVisible();
         await setupSSHCredentials(page.context(), sshAlgorithm);
         await page.reload();
         await programmingExerciseOverview.openCloneMenu(cloneMethod);
     }
     let repoUrl = await programmingExerciseOverview.copyCloneUrl();
     if (cloneMethod == GitCloneMethod.https) {
-        repoUrl = repoUrl.replace(student.username!, `${student.username!}:${student.password!}`);
+        try {
+            const url = new URL(repoUrl);
+            url.username = student.username!;
+            url.password = student.password!;
+            repoUrl = url.toString();
+        } catch (error) {
+            throw new Error(`Invalid repository URL: ${error.message}`);
+        }
     }

Likely invalid or redundant comment.

Copy link
Contributor

@coolchock coolchock left a comment

Choose a reason for hiding this comment

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

re-approve

@muradium muradium changed the title Development: E2E tests for exercise participation using git via SSH Development: E2E tests for Git exercise participation using SSH and Token Dec 18, 2024
@krusche krusche changed the title Development: E2E tests for Git exercise participation using SSH and Token Development: Add e2e tests for git exercise participation using ssh and token Dec 20, 2024
@krusche krusche added this to the 7.8.0 milestone Dec 20, 2024
@krusche krusche merged commit 47e5ebc into develop Dec 20, 2024
21 of 24 checks passed
@krusche krusche deleted the feature/playwright/test-ssh-token-clone branch December 20, 2024 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) docker playwright ready to merge tests
Projects
Status: Merged
Development

Successfully merging this pull request may close these issues.

5 participants