diff --git a/e2e/assets/counter_as/dfx.json b/e2e/assets/counter_as/dfx.json deleted file mode 100644 index 4c088c7203..0000000000 --- a/e2e/assets/counter_as/dfx.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "// TODO": "Replace this file with `dfx config`.", - "version": 1, - "dfx": "0.3.0-local-debug", - "canisters": { - "counter": { - "main": "counter.as", - "canister_id": 42 - } - }, - "defaults": { - "build": { - "output": "build/" - }, - "start": { - "port": 8000, - "address": "127.0.0.1", - "serve_root": "app/" - } - } -} diff --git a/e2e/assets/counter_as/patch.bash b/e2e/assets/counter_as/patch.bash new file mode 100644 index 0000000000..97cac3c4b3 --- /dev/null +++ b/e2e/assets/counter_as/patch.bash @@ -0,0 +1 @@ +dfx config canisters/hello/main counter.as diff --git a/e2e/assets/counter_wat/dfx.json b/e2e/assets/counter_wat/dfx.json deleted file mode 100644 index 178547bcaa..0000000000 --- a/e2e/assets/counter_wat/dfx.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "// TODO": "Replace this file with `dfx config`.", - "version": 1, - "dfx": "0.3.0-local-debug", - "canisters": { - "hello": { - "main": "counter.wat", - "canister_id": 42 - } - }, - "defaults": { - "build": { - "output": "build/" - }, - "start": { - "port": 8000, - "address": "127.0.0.1", - "serve_root": "app/" - } - } -} diff --git a/e2e/assets/greet_as/dfx.json b/e2e/assets/greet_as/dfx.json deleted file mode 100644 index 4a70b01d4d..0000000000 --- a/e2e/assets/greet_as/dfx.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "// TODO": "Replace this file with `dfx config`.", - "version": 1, - "dfx": "0.3.0-local-debug", - "canisters": { - "greet": { - "main": "greet.as", - "canister_id": 42 - } - }, - "defaults": { - "build": { - "output": "build/" - }, - "start": { - "port": 8000, - "address": "127.0.0.1", - "serve_root": "app/" - } - } -} diff --git a/e2e/assets/greet_as/patch.bash b/e2e/assets/greet_as/patch.bash new file mode 100644 index 0000000000..59b460ad9d --- /dev/null +++ b/e2e/assets/greet_as/patch.bash @@ -0,0 +1 @@ +dfx config canisters/hello/main greet.as diff --git a/e2e/assets/invalid_as/invalid.as b/e2e/assets/invalid_as/invalid.as new file mode 100644 index 0000000000..3fba78085a --- /dev/null +++ b/e2e/assets/invalid_as/invalid.as @@ -0,0 +1,4 @@ +Some Invalid Code +No way this is valid ActorScript, right? + + // Who knows... diff --git a/e2e/assets/invalid_as/patch.bash b/e2e/assets/invalid_as/patch.bash new file mode 100644 index 0000000000..515ba2875c --- /dev/null +++ b/e2e/assets/invalid_as/patch.bash @@ -0,0 +1,3 @@ +#!/dev/null + +dfx config canisters/hello/main invalid.as diff --git a/e2e/basic-project.bash b/e2e/basic-project.bash index e742f52ae7..d95c4fabb4 100644 --- a/e2e/basic-project.bash +++ b/e2e/basic-project.bash @@ -1,16 +1,13 @@ #!/usr/bin/env bats +load utils/_ + setup() { # We want to work from a temporary directory, different for every test. cd $(mktemp -d -t dfx-e2e-XXXXXXXX) export RUST_BACKTRACE=1 - dfx new e2e-project - test -d e2e-project - test -f e2e-project/dfx.json - cd e2e-project - - echo PWD: $(pwd) >&2 + dfx_new } teardown() { @@ -19,45 +16,25 @@ teardown() { killall dfx nodemanager client || true } -# Create a new project and starts its client in the background. -dfx_start() { - # Bats create a FD 3 for test output, but child processes inherit it and Bats will - # wait for it to close. Because `dfx start` leave a child process running, we need - # to close this pipe, otherwise Bats will wait indefinitely. - dfx start --background 3>&- -} - -# Takes a name of the asset folder, and copy those files to the current project. -install_asset() { - ASSET_ROOT=${BATS_TEST_DIRNAME}/assets/$1/ - cp -R $ASSET_ROOT/* . -} - @test "build + install + call + request-status -- greet_as" { install_asset greet_as dfx_start dfx build - INSTALL_REQUEST_ID=$(dfx canister install 1 build/greet.wasm) + INSTALL_REQUEST_ID=$(dfx canister install 1 canisters/greet.wasm) dfx canister request-status $INSTALL_REQUEST_ID - run dfx canister query 1 greet --type=string Banzai - [[ $status == 0 ]] - [[ "$output" == "Hello, Banzai!" ]] + assert_command dfx canister query 1 greet --type=string Banzai + assert_eq "Hello, Banzai!" # Using call --wait. - run dfx canister call --wait 1 greet --type=string Bongalo - echo $output - [[ $status == 0 ]] - [[ "$output" == "Hello, Bongalo!" ]] + assert_command dfx canister call --wait 1 greet --type=string Bongalo + assert_eq "Hello, Bongalo!" # Using call and request-status. - run dfx canister call 1 greet --type=string Blueberry - [[ $status == 0 ]] - + assert_command dfx canister call 1 greet --type=string Blueberry # At this point $output is the request ID. - run dfx canister request-status $output - [[ $status == 0 ]] - [[ "$output" == "Hello, Blueberry!" ]] + assert_command dfx canister request-status $output + assert_eq "Hello, Blueberry!" } @test "build + install + call + request-status -- counter_wat" { @@ -105,36 +82,34 @@ install_asset() { install_asset counter_as dfx_start dfx build - dfx canister install 1 build/counter.wasm + dfx canister install 1 canisters/counter.wasm --wait + + assert_command dfx canister call 1 read --wait + assert_eq "0" + + assert_command dfx canister call 1 inc --wait + assert_eq "" - run dfx canister call 1 read --wait - [[ "$output" == "0" ]] - run dfx canister call 1 inc --wait - [[ "$output" == "" ]] - run dfx canister query 1 read - [[ "$output" == "1" ]] + assert_command dfx canister query 1 read + assert_eq "1" dfx canister call --wait 1 inc - run dfx canister query 1 read - [[ "$output" == "2" ]] + assert_command dfx canister query 1 read + assert_eq "2" dfx canister call --wait 1 inc - run dfx canister query 1 read - [[ "$output" == "3" ]] + assert_command dfx canister query 1 read + assert_eq "3" - run dfx canister call 1 inc - [[ $status == 0 ]] - dfx canister request-status $output - [[ $status == 0 ]] + assert_command dfx canister call 1 inc + assert_command dfx canister request-status $output # Call write. - run dfx canister call 1 write --type=number 1337 --wait - [[ $status == 0 ]] + assert_command dfx canister call 1 write --type=number 1337 --wait + assert_eq "" # Write has no return value. But we can _call_ read too. - run dfx canister call 1 read - [[ $status == 0 ]] - run dfx canister request-status $output - [[ $status == 0 ]] - [[ "$output" == "1337" ]] + assert_command dfx canister call 1 read + assert_command dfx canister request-status $output + assert_eq "1337" } diff --git a/e2e/config.bash b/e2e/config.bash index d8336f48df..dcc5fb3584 100644 --- a/e2e/config.bash +++ b/e2e/config.bash @@ -1,33 +1,29 @@ #!/usr/bin/env bats +load utils/_ + setup() { # We want to work from a temporary directory, different for every test. cd $(mktemp -d -t dfx-e2e-XXXXXXXX) + + dfx_new } @test "dfx config -- read/write" { - dfx new e2e-project - cd e2e-project - - run dfx config defaults/build/output - [[ $status == 0 ]] - [[ "$output" == "\"canisters/\"" ]] + assert_command dfx config defaults/build/output + assert_eq '"canisters/"' - run dfx config defaults.build.output - [[ $status == 0 ]] - [[ "$output" == "\"canisters/\"" ]] + assert_command dfx config defaults.build.output + assert_eq '"canisters/"' - run dfx config defaults/build/output "other/" - [[ $status == 0 ]] + assert_command dfx config defaults/build/output "other/" + assert_eq "" - run dfx config defaults/build/output - [[ $status == 0 ]] - [[ "$output" == "\"other/\"" ]] + assert_command dfx config defaults/build/output + assert_eq '"other/"' - run dfx config non_existent - [[ $status == 255 ]] + assert_command_fail dfx config non_existent # We don't allow to change values that are non existent. - run dfx config non_existent 123 - [[ $status != 0 ]] + assert_command_fail dfx config non_existent 123 } diff --git a/e2e/frontend.bash b/e2e/frontend.bash index 8a8af506b1..e01fc69dcf 100644 --- a/e2e/frontend.bash +++ b/e2e/frontend.bash @@ -1,11 +1,12 @@ #!/usr/bin/env bats +load utils/_ + setup() { # We want to work from a temporary directory, different for every test. cd $(mktemp -d -t dfx-e2e-XXXXXXXX) - dfx new e2e-project - cd e2e-project + dfx_new } teardown() { @@ -14,35 +15,19 @@ teardown() { killall dfx nodemanager client || true } -# Creates a new project and starts its client in the background. -dfx_start() { - # Bats create a FD 3 for test output, but child processes inherit it and Bats will - # wait for it to close. Because `dfx start` leaves a child process running, we need - # to close this pipe, otherwise Bats will wait indefinitely. - dfx start --background $* 3>&- -} - -# Takes a name of the asset folder, and copy those files to the current project. -install_asset() { - ASSET_ROOT=${BATS_TEST_DIRNAME}/assets/$1/ - cp -R $ASSET_ROOT/* . -} - @test "dfx start serves a frontend" { dfx_start - run curl http://localhost:8000 # 8000 = default port. - [[ $status == 0 ]] - grep -i "" <(echo $output) + assert_command curl http://localhost:8000 # 8000 = default port. + assert_match "" } @test "dfx start serves a frontend on a port" { dfx_start --host 127.0.0.1:12345 - run curl http://localhost:12345 # 8000 = default port. - [[ $status == 0 ]] - grep -i "" <(echo $output) + assert_command curl http://localhost:12345 # 8000 = default port. + assert_match "" - run curl http://localhost:8000 - [[ $status != 0 ]] # This should have failed (no frontend on 8000). + assert_command_fail curl http://localhost:8000 + assert_match "Connection refused" } diff --git a/e2e/utils/_.bash b/e2e/utils/_.bash new file mode 100644 index 0000000000..4d083d9922 --- /dev/null +++ b/e2e/utils/_.bash @@ -0,0 +1,28 @@ +source ${BATSLIB}/load.bash +load utils/assertions + +# Takes a name of the asset folder, and copy those files to the current project. +install_asset() { + ASSET_ROOT=${BATS_TEST_DIRNAME}/assets/$1/ + cp -R $ASSET_ROOT/* . + + [ -f ./patch.bash ] && source ./patch.bash +} + +dfx_new() { + dfx new e2e-project + test -d e2e-project + test -f e2e-project/dfx.json + cd e2e-project + + echo PWD: $(pwd) >&2 +} + +# Start the client in the background. +dfx_start() { + # Bats create a FD 3 for test output, but child processes inherit it and Bats will + # wait for it to close. Because `dfx start` leave a child process running, we need + # to close this pipe, otherwise Bats will wait indefinitely. + dfx start --background "$@" 3>&- +} + diff --git a/e2e/utils/assertions.bash b/e2e/utils/assertions.bash new file mode 100644 index 0000000000..616c6a2072 --- /dev/null +++ b/e2e/utils/assertions.bash @@ -0,0 +1,83 @@ +#! + +# Asserts that a command line succeeds. Still sets $output to the stdout and stderr +# of the command. +# Arguments: +# $@ - The command to run. +# Returns: +# none +assert_command() { + run "$@" + [[ $status == 0 ]] || \ + ( (echo "$*"; echo "$output" | batslib_decorate "Output") \ + | batslib_decorate "Command failed" \ + | fail) +} + +# Asserts that a command line fails. Still sets $output to the stdout and stderr +# of the command. +# Arguments: +# $@ - The command to run. +# Returns: +# none +assert_command_fail() { + run "$@" + [[ $status != 0 ]] || \ + ( (echo "$*"; echo $output | batslib_decorate "Output") \ + | batslib_decorate "Command succeeded (should have failed)" \ + | fail) +} + +# Asserts that a string contains another string, using regexp. +# Arguments: +# $1 - The regex to use to match. +# $2 - The string to match against (output). By default it will use +# $output. +assert_match() { + regex=$1 + if [[ $# < 2 ]]; then + text=$output + else + text=$2 + fi + [[ "$text" =~ "$regex" ]] || \ + (batslib_print_kv_single_or_multi 10 "regex" "$regex" "actual" "$text" \ + | batslib_decorate "output does not match" \ + | fail) +} + +# Asserts that two values are equal. +# Arguments: +# $1 - The expected value. +# $2 - The actual value. +assert_eq() { + expected=$1 + if [[ $# < 2 ]]; then + actual=$output + else + actual=$2 + fi + + [[ "$actual" == "$expected" ]] || \ + (batslib_print_kv_single_or_multi 10 "expected" $expected "actual" $actual \ + | batslib_decorate "output does not match" \ + | fail) +} + +# Asserts that two values are not equal. +# Arguments: +# $1 - The expected value. +# $2 - The actual value. +assert_neq() { + expected=$1 + if [[ $# < 2 ]]; then + actual=$output + else + actual=$2 + fi + + [[ "$actual" != "$expected" ]] || \ + (batslib_print_kv_single_or_multi 10 "expected" $expected "actual" $actual \ + | batslib_decorate "output does not match" \ + | fail) +} diff --git a/nix/e2e-tests.nix b/nix/e2e-tests.nix index 576010ec03..764f53b477 100644 --- a/nix/e2e-tests.nix +++ b/nix/e2e-tests.nix @@ -6,12 +6,19 @@ , stdenv , killall }: +let batslib = builtins.fetchGit { + url = "ssh://git@github.com/ztombol/bats-support"; + ref = "0.3.0"; + rev = "24a72e14349690bcbf7c151b9d2d1cdd32d36eb1"; +}; in runCommandNoCC "e2e-tests" { - buildInputs = [ bats coreutils curl dfinity-sdk.packages.rust-workspace-debug stdenv.cc killall ]; + buildInputs = [ bats batslib coreutils curl dfinity-sdk.packages.rust-workspace-debug stdenv.cc killall ]; } '' # We want $HOME/.cache to be in a new temporary directory. - HOME=$(mktemp -d -t dfx-e2e-home-XXXX) + export HOME=$(mktemp -d -t dfx-e2e-home-XXXX) + # We use BATSLIB in our scripts to find the root of the BATSLIB repo. + export BATSLIB="${batslib}" # Timeout of 10 minutes is enough for now. Reminder; CI might be running with # less resources than a dev's computer, so e2e might take longer.