diff --git a/.github/workflows/server-test.yml b/.github/workflows/server-test.yml index 9de168e6..51cc7630 100644 --- a/.github/workflows/server-test.yml +++ b/.github/workflows/server-test.yml @@ -41,6 +41,10 @@ jobs: scheduler/target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # Install Nextest + - name: Install nextest + uses: taiki-e/install-action@nextest + - name: Run migrations run: | cd scheduler @@ -84,4 +88,4 @@ jobs: - name: Run server tests run: | cd scheduler - cargo test --all + cargo nextest run --workspace diff --git a/justfile b/justfile index 23bbfe37..ee42f034 100644 --- a/justfile +++ b/justfile @@ -3,6 +3,7 @@ export DATABASE_URL := "postgresql://postgres:postgres@localhost:45432/nettusche # Install minimal tools install_tools: cargo install sqlx-cli --no-default-features --features postgres + cargo install cargo-nextest # Install all tools install_all_tools: install_tools @@ -26,9 +27,9 @@ _setup_client_node: dev: _setup_db cd scheduler && cargo run -# Test -test: _setup_db - cd scheduler && cargo test --all +# Run the tests on a temporary DB container +test: + bash ./scripts/run_tests.sh # Lint lint: _setup_db diff --git a/scheduler/.config/nextest.toml b/scheduler/.config/nextest.toml new file mode 100644 index 00000000..373c500c --- /dev/null +++ b/scheduler/.config/nextest.toml @@ -0,0 +1,8 @@ +# Test group for running some tests serially +[test-groups] +serial-integration = { max-threads = 1 } + +# Tests that contain `serial_` are run serially +[[profile.default.overrides]] +filter = 'test(serial_)' +test-group = 'serial-integration' diff --git a/scheduler/crates/api/src/event/get_upcoming_reminders.rs b/scheduler/crates/api/src/event/get_upcoming_reminders.rs index 08a39d32..37bfb3f5 100644 --- a/scheduler/crates/api/src/event/get_upcoming_reminders.rs +++ b/scheduler/crates/api/src/event/get_upcoming_reminders.rs @@ -188,7 +188,7 @@ mod tests { #[actix_web::main] #[serial_test::serial] #[test] - async fn get_upcoming_reminders() { + async fn serial_get_upcoming_reminders() { let mut ctx = setup_context().await; ctx.sys = Arc::new(StaticTimeSys1 {}); @@ -271,7 +271,7 @@ mod tests { #[actix_web::main] #[serial_test::serial] #[test] - async fn updating_event_also_updates_reminders() { + async fn serial_updating_event_also_updates_reminders() { let mut ctx = setup_context().await; ctx.sys = Arc::new(StaticTimeSys1 {}); @@ -334,7 +334,7 @@ mod tests { #[actix_web::main] #[serial_test::serial] #[test] - async fn deleting_event_reminder_setting_also_deletes_reminders() { + async fn serial_deleting_event_reminder_setting_also_deletes_reminders() { let mut ctx = setup_context().await; ctx.sys = Arc::new(StaticTimeSys1 {}); @@ -389,7 +389,7 @@ mod tests { #[actix_web::main] #[serial_test::serial] #[test] - async fn deleting_event_also_deletes_reminders() { + async fn serial_deleting_event_also_deletes_reminders() { let mut ctx = setup_context().await; ctx.sys = Arc::new(StaticTimeSys1 {}); diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh new file mode 100644 index 00000000..03201af4 --- /dev/null +++ b/scripts/run_tests.sh @@ -0,0 +1,77 @@ +#! /bin/bash + +# Script to run all the (Rust) tests of the scheduler project +# It launches a temporary PostgreSQL container, runs the migrations, runs the tests and then stops and removes the container + +# Function to clean up the containers +CLEANUP_CALLED=false +cleanup() { + if [ "$CLEANUP_CALLED" = true ]; then + return + fi + echo "Cleaning up..." + CLEANUP_CALLED=true + + if [ -n "$NC_PID" ] && kill -0 $NC_PID 2>/dev/null; then + kill $NC_PID >/dev/null 2>&1 + fi + + if [ "$(docker ps -q -f name=$RANDOM_NAME)" ]; then + docker stop $RANDOM_NAME >/dev/null 2>&1 + fi + + if [ "$(docker ps -q -f name=ryuk)" ]; then + docker stop ryuk >/dev/null 2>&1 + fi +} + +# Set up a trap to call the cleanup function on EXIT, SIGINT, and SIGTERM +trap cleanup EXIT SIGINT SIGTERM + +# Search for a free port to bind the temporary PG container +BASE_PORT=1234 +INCREMENT=1 + +PORT=$BASE_PORT +IS_FREE=$(netstat -taln | grep $PORT) + +while [[ -n "$IS_FREE" ]]; do + PORT=$((PORT + INCREMENT)) + IS_FREE=$(netstat -taln | grep $PORT) +done + +# Generate a random name for the temporary PG container +RANDOM_NAME="pg_test_$(date +%s)" + +LABEL="nittei_testing=true" + +cd scheduler && cargo build --workspace + +# Launch the resource reaper (like testcontainers) +docker run -d --name ryuk --rm -v /var/run/docker.sock:/var/run/docker.sock -e RYUK_VERBOSE=true -e RYUK_PORT=8080 -p 8080:8080 testcontainers/ryuk:0.8.1 >/dev/null 2>&1 + +# Keep the connection open and send the label to Ryuk +TIMEOUT=60 +( + echo "label=${LABEL}" + # Keep the connection open to Ryuk and read the ACK response + while [ $((TIMEOUT--)) -gt 0 ]; do + sleep 1 + done +) | nc localhost 8080 & +>/dev/null 2>&1 +NC_PID=$! + +# Launch a PG container +docker run --rm -d -l ${LABEL} --name $RANDOM_NAME -p $PORT:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=scheduler postgres:13 >/dev/null 2>&1 + +# Set the DATABASE_URL environment variable +export DATABASE_URL="postgres://postgres:postgres@localhost:${PORT}/scheduler" + +# Run the migrations +cd crates/infra && sqlx migrate run && cd ../.. + +# Run the tests +cargo nextest run --workspace && cd .. + +# The cleanup function will be called automatically