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

Smoke testing #6

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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
33 changes: 25 additions & 8 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
name: E2E Tests

on:
workflow_dispatch # disabled pending db seeding
# push:
# branches: [ main ]
# pull_request:
# branches: [ main ]
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: build containers and run cypress e2e tests
# TODO: seed the database with a sparse(r) dump so there are pages to test..

- name: deploy services in e2e mode
run: make e2e

- name: run e2e tests
uses: cypress-io/github-action@v6
with:
working-directory: e2e
wait-on: "http://localhost:8000"
wait-on-timeout: 300

- name: display service logs
if: failure()
run: docker compose -f docker-compose.yml -f e2e.yml logs

- name: upload cypress videos
if: failure()
uses: actions/upload-artifact@v4
with:
name: cypress-videos
path: e2e/cypress/videos
24 changes: 18 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ release-version: .env
.PHONY: docker-compose.yml
docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PATH) release-version
case "$(DEPLOY_ENVIRONMENT)" in \
dev|staging|e2e) docker compose -f base.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \
dev|staging) docker compose -f base.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \
prod) docker compose -f base.yml -f staging.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \
*) echo "invalid environment. must be either dev, staging or prod" 1>&2; exit 1;; \
esac
Expand Down Expand Up @@ -138,9 +138,21 @@ clean_deploy: clean
test: build
docker compose run --rm server /code/deploy/test.sh

# e2e testing setup

E2E_SHARED_DIR=${DOCKER_SHARED_DIR}/e2e
E2E_BACKUPS_PATH=${E2E_SHARED_DIR}/backups
E2E_REPO_PATH=${E2E_BACKUPS_PATH}/repo

$(E2E_REPO_PATH):
mkdir -p $(E2E_BACKUPS_PATH)
wget -c ${BORG_REPO_URL} -P $(E2E_BACKUPS_PATH)
tar -Jxf $(E2E_BACKUPS_PATH)/repo.tar.xz -C $(E2E_BACKUPS_PATH)

.PHONY: e2e
e2e: DEPLOY_ENVIRONMENT=e2e
e2e: build
docker compose run server inv collectstatic
docker compose run --rm e2e npm run test
docker compose down
e2e: docker-compose.yml secrets $(DOCKER_SHARED_DIR) $(E2E_REPO_PATH)
docker compose -f docker-compose.yml -f e2e.yml up -d --build
docker compose -f docker-compose.yml -f e2e.yml exec server bash -c "\
inv borg.restore --force && \
./manage.py migrate && \
inv prepare"
24 changes: 22 additions & 2 deletions django/core/settings/e2e.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
from .dev import *
from .test import *

DEPLOY_ENVIRONMENT = Environment.TEST
DEBUG = True

DJANGO_VITE_DEV_MODE = False

SHARE_DIR = path.realpath("/shared/e2e")
LIBRARY_ROOT = path.join(SHARE_DIR, "library")
LIBRARY_PREVIOUS_ROOT = path.join(SHARE_DIR, ".latest")
REPOSITORY_ROOT = path.join(BASE_DIR, "repository")
BACKUP_ROOT = path.join(SHARE_DIR, "backups")
BORG_ROOT = path.join(BACKUP_ROOT, "repo")
EXTRACT_ROOT = path.join(SHARE_DIR, "extract")
MEDIA_ROOT = path.join(SHARE_DIR, "media")

DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("DB_NAME"),
"USER": os.getenv("DB_USER"),
"PASSWORD": read_secret("db_password", os.getenv("DB_PASSWORD")),
"HOST": "e2edb",
"PORT": os.getenv("DB_PORT"),
}
}
13 changes: 9 additions & 4 deletions django/curator/invoke_tasks/borg.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,19 @@ def restore_database(

@task()
def restore(
ctx, repo=settings.BORG_ROOT, archive=None, target_database=db._DEFAULT_DATABASE
ctx,
repo=settings.BORG_ROOT,
archive=None,
target_database=db._DEFAULT_DATABASE,
force=False,
):
"""Restore the library files, media files and database to the state given in the borg repo at path REPO
using archive ARCHIVE. The target_database argument is for testing so a different database can be used to
make sure the database is getting restored properly"""
confirm(
"Are you sure you want to restore the database and all file content (y/n)? "
)
if not force:
confirm(
"Are you sure you want to restore the database and all file content (y/n)? "
)
with tempfile.TemporaryDirectory(dir=settings.SHARE_DIR) as working_directory:
_restore(
ctx,
Expand Down
4 changes: 2 additions & 2 deletions django/curator/invoke_tasks/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def create_pgpass_file(ctx, db_key=_DEFAULT_DATABASE, force=False):
if os.path.isfile(pgpass_path) and not force:
return
with open(pgpass_path, "w+") as pgpass:
pgpass.write("db:*:*:{db_user}:{db_password}\n".format(**db_config))
pgpass.write("{db_host}:*:*:{db_user}:{db_password}\n".format(**db_config))
ctx.run("chmod 0600 ~/.pgpass")


Expand Down Expand Up @@ -132,7 +132,7 @@ def restore_from_dump(
cat_cmd = "zcat"
drop(ctx, database=target_database, create=True)
ctx.run(
"{cat_cmd} {dumpfile} | psql -w -q -o restore-from-dump-log.txt -h db {db_name} {db_user}".format(
"{cat_cmd} {dumpfile} | psql -w -q -o restore-from-dump-log.txt -h {db_host} {db_name} {db_user}".format(
cat_cmd=cat_cmd, dumpfile=dumpfile, **db_config
),
echo=True,
Expand Down
52 changes: 34 additions & 18 deletions e2e.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
services:
e2e:
image: comses/cypress
build: e2e
environment:
- CYPRESS_baseUrl=http://server:8000
db:
image: alpine # disable the normal db container
command: tail -f /dev/null
healthcheck: null
e2edb:
image: postgis/postgis:15-3.4
secrets:
- db_password # re-using the same db password
volumes:
- ./e2e/cypress:/code/cypress
- ./e2e/cypress.config.js:/code/cypress.config.js
- ./django/deploy/wait-for-it.sh:/code/wait-for-it.sh
depends_on:
- server
- ./build/secrets/db_password:/run/secrets/db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 30s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: "${DB_USER}"
POSTGRES_DB: "${DB_NAME}"
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
vite:
volumes:
- ./frontend:/code
command: ["npm", "run", "build"]
environment:
NODE_ENV: "e2e"
server:
image: comses/server:dev
volumes:
- ./django:/code
- ./docs:/docs
depends_on:
db:
condition: service_started
e2edb:
condition: service_healthy
elasticsearch:
condition: service_started
redis:
condition: service_started
vite:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
environment:
DJANGO_SETTINGS_MODULE: "core.settings.e2e"
ports:
- "127.0.0.1:8000:8000"
14 changes: 0 additions & 14 deletions e2e/Dockerfile

This file was deleted.

4 changes: 2 additions & 2 deletions e2e/cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
baseUrl: "http://localhost:8000",
specPattern: ["cypress/e2e/**/*.spec.ts"],
specPattern: ["cypress/tests/**/*.spec.ts"],
supportFile: false,
screenshotOnRunFailure: false,
video: false,
video: true,
}
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//test narative documentation
1 change: 1 addition & 0 deletions e2e/cypress/fixtures/codebase/testSimulationOutput.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//testSimulationOutput
2 changes: 2 additions & 0 deletions e2e/cypress/fixtures/codebase/testSourceCode.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//test source code

1 change: 1 addition & 0 deletions e2e/cypress/fixtures/codebase/testUploadData.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//test upload data
19 changes: 19 additions & 0 deletions e2e/cypress/tests/beforeLoginTests.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//test ability to browse/download codebases
describe("Browse Codebases", () => {
it("should browse the model library and be able to enter a search query", () => {
cy.visit("/"); // visit homepage
cy.visit("/codebases");
assert(cy.get("h1").contains("Computational Model Library"));
});
});

//download codebase
describe("Download Codebases", () => {
it("should download a codebase", () => {
// Navigate to the first 'search-result', find the 'a' tag within the 'title' div, and click it
cy.get(".search-result", { timeout: 10000 })
.first()
.find(".title a")
.click();
});
});
121 changes: 121 additions & 0 deletions e2e/cypress/tests/codebase.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { loginBeforeEach } from "./setup.spec";
import "cypress-file-upload";

//login
describe("Login", () => {
it("should log into comses homepage with test user", () => {
loginBeforeEach("test_user", "123456");
});
});


describe("Visit codebases page", () => {
//codebases PAGE

it("should visit the codebases page", () => {
cy.visit("/codebases");
assert(cy.get("h1").contains("Computational Model Library"));
});

it("should be able to download a codebase", () => {
cy.visit("/codebases");
cy.get(".search-result").first().find("a").first().click();
cy.get("#djHideToolBarButton").click();
cy.get('button.btn.btn-primary.my-1.w-100[rel="nofollow"]').click();
cy.get("#form-field-industry").select("College/University");
cy.get("#form-field-affiliation").type("Arizona State University{enter}", {
force: true,
});
cy.get("#form-field-reason").select("Research");
cy.get(
'button[type="submit"][form="download-request-form"].btn.btn-success'
).click();
});

it("should be able to upload a codebase", () => {
loginBeforeEach("test_user", "123456");
cy.visit("/codebases");
assert(cy.get("h1").contains("Computational Model Library"));
cy.get("#djHideToolBarButton").click();
cy.contains("Publish a model").click();
cy.get('[data-cy="codebase title"]').type("Codebase Title");
cy.get('[data-cy="codebase description"]').type("Codebase Description");
cy.get('[data-cy="codebase replication-text"]').type(
"Codebase Replication Text"
);
cy.get('[data-cy="codebase associated publications"]').type(
"Codebase Associated Publications"
);
cy.get('[data-cy="codebase references"]').type("Codebase References");
cy.get('[data-cy="next"]').click();
//add images
cy.get('[data-cy="add image"]').click(); //add image
cy.get('[data-cy="upload-image"]')
.first()
.selectFile("cypress/fixtures/codebase/codebasetestimage.png", {
force: true,
});
cy.get('button.btn.btn-outline-gray[data-bs-dismiss="modal"]')
.get("button")
.first()
.click({ force: true });
cy.get('button.btn.btn-outline-gray[data-bs-dismiss="modal"]')
.should("be.visible")
.and("not.be.disabled")
.first()
.click({ force: true });
cy.get("body").click(0, 0);
cy.get("body").click(0, 0); //FIX: find a more precise way of closing the image upload modal

//add source code files
cy.get('[data-cy="upload-code"]')
.first()
.selectFile("cypress/fixtures/codebase/testSourceCode.txt", {
force: true,
});
cy.get('[data-cy="upload-docs"]')
.first()
.selectFile("cypress/fixtures/codebase/testNarrativeDocumentation.txt", {
force: true,
});
cy.get('[data-cy="upload-data"]')
.first()
.selectFile("cypress/fixtures/codebase/testUploadData.txt", {
force: true,
});
cy.get('[data-cy="upload-results"]')
.first()
.selectFile("cypress/fixtures/codebase/testSimulationOutput.txt", {
force: true,
});
cy.get('[data-cy="add metadata"]').click();
cy.get('[data-cy="release-notes"] textarea').type("Release notes");
cy.get('[data-cy="embargo-end-date"]').click();
cy.get('[data-cy="embargo-end-date"]').contains("29").click();
cy.get('[data-cy="operating-system"] select').select(
"Operating System Independent"
);
cy.get('[data-cy="software-frameworks"]').type("NetLogo {enter} ");
cy.get("body").click(0, 0);
cy.get('[data-cy="programming-languages"').type("Net Logo {enter}");
cy.get("body").click(0, 0);
cy.get('[data-cy="license"] .multiselect__select').click();
cy.get('[data-cy="license"] .multiselect__element')
.contains("GPL-2.0")
.click();
cy.get('[data-cy="save-and-continue"]').click();
cy.get('button.btn.btn-danger[rel="nofollow"]').click();
cy.get('button[type="submit"].btn.btn-danger[form="publish-form"]').click();
});

it("should verify that the codebase was uploaded correctly", () => {
cy.visit("/codebases");
cy.get(".search-result").first().find("a").first().click();
assert(cy.get("h1").contains("Codebase Title"));
assert(cy.get("p").contains("Codebase Description"));
assert(cy.get("p").contains("Codebase Replication Text"));
assert(cy.get("p").contains("Codebase Associated Publications"));
assert(cy.get("p").contains("Codebase References"));

})
});
Loading
Loading