Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: 'WrongSecrets CTF Party: E2E Tests'

on:
pull_request:
branches: [ main ]

jobs:
e2e-test:
name: Run Cypress E2E Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Start Minikube
uses: medyagh/setup-minikube@latest
with:
driver: docker
cpus: '2'
memory: '8000'
kubernetes-version: 'v1.32.0'

- name: Install Helm
uses: azure/setup-helm@v4

- name: Install yq
run: sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && sudo chmod +x /usr/bin/yq

# This new step runs the deploy script in the background and then waits
- name: Run Deployment Script in Background and Wait
run: |
./build-and-deploy-minikube.sh &
echo "Deployment script running in background. Waiting for pods..."
sleep 30
kubectl wait --for=condition=ready pod -l app=wrongsecrets-balancer --timeout=5m

- name: Install Cypress and Run Tests
uses: cypress-io/github-action@v6
with:
working-directory: wrongsecrets-balancer
# The kubectl wait command is now the primary check
wait-on: 'http://localhost:3000'
wait-on-timeout: 120 # 2 minutes
browser: chrome
headless: true
2 changes: 1 addition & 1 deletion build-and-deploy-minikube.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ source ./scripts/check-available-commands.sh
checkCommandsAvailable helm docker kubectl yq minikube

minikube delete
minikube start --cpus=8 --memory=12000MB --network-plugin=cni --cni=calico --driver=docker --kubernetes-version=1.32.0
minikube start --cpus=2 --memory=8000MB --network-plugin=cni --cni=calico --driver=docker --kubernetes-version=1.32.0
eval $(minikube docker-env)
./build-and-deploy.sh

Expand Down
38 changes: 38 additions & 0 deletions scripts/run-e2e-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash
# This script will exit immediately if any command fails
set -e

echo "--- Starting Minikube ---"
minikube start --cpus=2 --memory=8000MB --driver=docker --network-plugin=cni --cni=calico --kubernetes-version=1.32.0

echo "--- Updating Helm Repositories ---"
helm repo update

echo "--- Deploying Application ---"
helm upgrade --install wrongsecrets ./helm/wrongsecrets-ctf-party

echo "--- Waiting for Balancer Deployment to be Ready ---"
kubectl wait --for=condition=available deployment/wrongsecrets-balancer --timeout=5m

echo "--- Starting Port Forward in Background ---"
kubectl port-forward service/wrongsecrets-balancer 3000:3000 &
# Store the ID of the background process
PORT_FORWARD_PID=$!

echo "--- Waiting for Port Forward to establish... ---"
sleep 5

echo "--- Getting Admin Password ---"
# Note: We get the password here for the test to use it
ADMIN_PASSWORD=$(kubectl get secrets wrongsecrets-balancer-secret -o=jsonpath='{.data.adminPassword}' | base64 --decode)
export CYPRESS_ADMIN_PASSWORD=$ADMIN_PASSWORD

echo "--- Running Cypress Tests ---"
# Navigate to the correct directory to run the tests
cd wrongsecrets-balancer
# Run Cypress tests headlessly
npx cypress run

echo "--- Cleaning up port-forward process ---"
# Stop the background port-forward process
kill $PORT_FORWARD_PID
7 changes: 7 additions & 0 deletions wrongsecrets-balancer/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
};
22 changes: 22 additions & 0 deletions wrongsecrets-balancer/cypress/e2e/admin_login.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
describe('Admin Login', () => {
it('should allow the admin to log in through the main page', () => {
// NOTE: The admin password changes every time you deploy.
// You must get the new password from the terminal before running this test.
const adminPassword = 'RSX9I94K';

// Visit the homepage to log in.
cy.visit('http://localhost:3000');

// Type "admin" as the team name and click the button.
cy.get('[data-test-id="teamname-input"]').type('admin');
cy.get('[data-test-id="create-join-team-button"]').click();

// On the next page, type the admin password.
cy.get('[data-test-id="passcode-input"]').type(adminPassword);
cy.contains('button', 'Join Team').click();

// Verify that the admin page has loaded. We give it a longer timeout (10 seconds)
// because the list of teams might take a moment to load from the server.
cy.contains('Active Teams', { timeout: 10000 }).should('be.visible');
});
});
37 changes: 37 additions & 0 deletions wrongsecrets-balancer/cypress/e2e/team_workflow.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
describe('Team Creation and Joining Workflow', () => {
it('should create a team, then allow a user to join it with the passcode', () => {
// Generates a short, unique team name that is under the 16-character limit.
const teamName = `ctf-${Date.now().toString().slice(-9)}`;

// Set up a "spy" to intercept the network request that creates the team.
cy.intercept('POST', `/balancer/teams/${teamName}/join`).as('createTeamRequest');

// === PART 1: CREATE THE TEAM ===
cy.visit('http://localhost:3000');
cy.get('[data-test-id="teamname-input"]').type(teamName);
cy.get('[data-test-id="create-join-team-button"]').click();

// === PART 2: CAPTURE PASSCODE & JOIN ===
// Wait for the network request to finish and get the passcode from its response data.
cy.wait('@createTeamRequest').then((interception) => {
const passcode = interception.response.body.passcode;

// Now that we have the real passcode, go back to the homepage.
cy.visit('http://localhost:3000');

// Enter the same unique team name again.
cy.get('[data-test-id="teamname-input"]').type(teamName);
cy.get('[data-test-id="create-join-team-button"]').click();

// On the "Joining team" page, type the real passcode we captured.
cy.get('[data-test-id="passcode-input"]').type(passcode);
cy.contains('button', 'Join Team').click();

// === PART 3: FINAL VERIFICATION (with a long timeout) ===
// Instead of waiting for a network call, we wait directly for the final button to appear.
// We give it up to 2 minutes (120000ms) for the backend instance to get ready.
cy.contains('Start Hacking', { timeout: 120000 }).should('be.visible');
cy.contains('Start your Webtop').should('be.visible');
});
});
});
5 changes: 5 additions & 0 deletions wrongsecrets-balancer/cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "[email protected]",
"body": "Fixtures are a great way to mock data for responses to routes"
}
25 changes: 25 additions & 0 deletions wrongsecrets-balancer/cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
17 changes: 17 additions & 0 deletions wrongsecrets-balancer/cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'
Loading