diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 699e603..2f98819 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -44,11 +44,8 @@ jobs: - name: Shellcheck uses: ludeeus/action-shellcheck@master - - name: Install shfmt - uses: mfinelli/setup-shfmt@master - - - name: Run shfmt - run: shfmt -d bin/* test/run + - name: Run Cargo Test + run: cargo test - name: Setup Node uses: actions/setup-node@v3 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c2b407..1593235 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,5 @@ +# Overview + * If you have found a discrepancy in documented and observed behaviour, that is a bug. Feel free to [report it as an issue](https://github.com/mpalmer/action-validator/issues), providing @@ -8,3 +10,84 @@ request](https://github.com/mpalmer/action-validator/pulls). * At all times, abide by the Code of Conduct (CODE_OF_CONDUCT.md). + +--- + +# Environment Setup + +## Install Rust +Firstly, you'll need make any changes to the core functionality of this project. We recommend use `rustup`, on the recommendation of the rust team. You can find the installation instructions [here](https://www.rust-lang.org/tools/install). + +To confirm that rust is installed, run the `cargo` command. If you don't receive the help docs output, you may need to add rust to your shell rc file. + +## Git Submodule Setup +This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). Specifically for the use of [schemastore](https://github.com/SchemaStore/schemastore). + +To setup the git submodule after cloning this repo to your local, you'll want to run the following commands: +1. `git submodule init` +2. `git submodule update` + +It should look similar to the output below. + +```bash +❯ git submodule init +Submodule 'src/schemastore' (https://github.com/SchemaStore/schemastore) registered for path 'src +/schemastore' +❯ git submodule update +Cloning into '/Users/someuser/action-validator/src/schemastore'... +Submodule path 'src/schemastore': checked out 'd3e6ab7727380b214acbab05570fb09a3e5d2dfc' +``` + +At this point, you should be all set to `cargo run`! If you run into any issues here, please [create an issue](https://github.com/mpalmer/action-validator/issues/new/choose). + +# Running the Validator Locally + +## `cargo run [FILE] -- [OPTIONS]` +`cargo run` will create a _debug_ executable and run the project. If this is your first time running the command, cargo will compile the development binary with `cargo build`. This will install all of the dependencies and create the debug binary `action-validator` in the `/target/debug/` directory. `cargo run` will then invoke this binary after creation. + +One caveat if you're running with `cargo run`: if you want to supply the program with options, you need to use the `--` operator between `cargo run` and your provided options. This let's cargo know which flags are meant for cargo, and which are meant for the executable. + +## `cargo build` && `./target/debug/action-validator [OPTIONS]` +As discussed in the prior section, `cargo build` install dependencies (if they're not cached) and build the development binary. This binary can then be invoked directly by running `./target/debug/action-validator`. This does **not** require the use of the `--` operator in between the binary and any provided options. + +## Try It Yourself! + +Run the command `cargo run -- --help`. You should see an output similar to the following. +```bash +❯ cargo run -- --help + Finished dev [unoptimized + debuginfo] target(s) in 0.05s + Running `target/debug/action-validator --help` +A validator for GitHub Action and Workflow YAML files + +Usage: action-validator [OPTIONS] [path_to_action_yaml]... + +Arguments: + [path_to_action_yaml]... Input file + +Options: + -v, --verbose Be more verbose + -h, --help Print help information + -V, --version Print version information +``` + +# Writing Tests +All tests live in the `tests` directory. Currently, this project implements snapshot testing, +but that's not to say you couldn't write unit or integration tests with the current structure. +To run the tests, simply run `cargo test` from the root directory. If you want to test a specific +feature, you can add the `-F {feature}` flag (e.g. `cargo test -F remote-checks`). + +## Unit/Integration Tests +As of this writing, there are no unit or integration tests. If you are looking to write some, please +follow the directions in [this guide](https://doc.rust-lang.org/book/ch11-01-writing-tests.html). + +## Snapshot Tests +A snapshot test is performed when we execute the cli and capture `stdout`, `stderr`, and/or an exit code. +When the tests is run, the results of the test must exactly match those of the previous run. For this project, +the snapshot tests are named in the format `{next_id}_{whats_being_tested}` (e.g. `011_remote_checks_failure`). + +If you have made changes which will change the output of the program and cause snapshots to fail, you can run +`cargo test -F save-snapshots`. This feature causes the executed command to save the `stdout`, `stderr`, and/or +exit code to the specified testing directory. + +If you are writing a net new test, you will need to create the test directory with your workflow or action file. +Once you're done, you can save the results to that directy by running `cargo test -F save-snapshots`. diff --git a/Cargo.lock b/Cargo.lock index dc638ed..bc15b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,9 +6,11 @@ version = 3 name = "action-validator" version = "0.0.0-git" dependencies = [ + "assert_cmd", "clap", "console_error_panic_hook", "glob", + "rstest", "serde", "serde-wasm-bindgen", "serde_json", @@ -46,6 +48,27 @@ dependencies = [ "libc", ] +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "assert_cmd" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -79,6 +102,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +dependencies = [ + "memchr", + "once_cell", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -216,12 +251,30 @@ dependencies = [ "syn", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "downcast-rs" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "erased-serde" version = "0.3.24" @@ -272,6 +325,101 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -379,6 +527,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -536,6 +693,46 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "predicates" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +dependencies = [ + "anstyle", + "difflib", + "itertools", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -619,12 +816,53 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rstest" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-ident", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.36.6" @@ -657,6 +895,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.152" @@ -718,6 +962,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + [[package]] name = "strsim" version = "0.10.0" @@ -744,6 +997,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "tinyvec" version = "1.5.0" @@ -853,6 +1112,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 3dfd387..cdf264b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,11 @@ include = [ "/src/schemastore/src/schemas/json/github-action.json" ] version = "0.0.0-git" -authors = ["Matt Palmer "] +authors = [ + "Matt Palmer ", + "Ben Heidemann ", + "Austin Ward " +] edition = "2021" # If this is changed, .github/workflows/qa.yml build matrix needs updating as well rust-version = "1.60.0" @@ -21,6 +25,7 @@ crate-type = ["cdylib", "rlib"] [features] js = ["console_error_panic_hook", "valico/js"] +save-snapshots = [] [dependencies] clap = { version = "4.0", features = ["derive"] } @@ -43,6 +48,8 @@ serde-wasm-bindgen = "0.4.5" [dev-dependencies] wasm-bindgen-test = "0.3.13" +rstest = "0.16.0" +assert_cmd = "2.0.8" [profile.release] # Tell `rustc` to optimize for small code size. diff --git a/package.json b/package.json index 5a176c4..7b0d7d0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "GPL-3.0-only", "scripts": { "build": "npx wasm-pack build --out-dir target/wasm-pack/build --no-typescript --target nodejs --features js && cp target/wasm-pack/build/action_validator_bg.wasm packages/core/ && cp target/wasm-pack/build/action_validator.js packages/core/ && cp target/wasm-pack/build/action_validator.js packages/core/", - "test": "node test/run.mjs", + "test": "node tests/run.mjs", "lint": "prettier --check .", "format": "prettier --write ." }, diff --git a/packages/cli/package.json b/packages/cli/package.json index 3b08d82..7ef99d5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -21,7 +21,7 @@ }, "scripts": { "build": "npx wasm-pack build --out-dir target/wasm-pack/build --no-typescript --target nodejs --features js && cp target/wasm-pack/build/action_validator_bg.wasm dist/ && cp target/wasm-pack/build/action_validator.js dist/ && cp target/wasm-pack/build/action_validator.js dist/", - "test": "node test/run.mjs", + "test": "node tests/run.mjs", "lint": "prettier --check .", "format": "prettier --write ." }, diff --git a/packages/core/package.json b/packages/core/package.json index 7ef9b17..3ee260c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,7 +21,7 @@ "types": "action_validator.d.ts", "scripts": { "build": "npx wasm-pack build --out-dir target/wasm-pack/build --no-typescript --target nodejs --features js && cp target/wasm-pack/build/action_validator_bg.wasm dist/ && cp target/wasm-pack/build/action_validator.js dist/ && cp target/wasm-pack/build/action_validator.js dist/", - "test": "node test/run.mjs", + "test": "node tests/run.mjs", "lint": "prettier --check .", "format": "prettier --write ." } diff --git a/test/004_failing_globs/stdout b/test/004_failing_globs/stdout deleted file mode 100644 index 4179a2f..0000000 --- a/test/004_failing_globs/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 004_failing_globs/glob.yml diff --git a/test/007_funky_syntax/stdout b/test/007_funky_syntax/stdout deleted file mode 100644 index f57da30..0000000 --- a/test/007_funky_syntax/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 007_funky_syntax/rust-check.yml diff --git a/test/008_job_dependencies/stdout b/test/008_job_dependencies/stdout deleted file mode 100644 index 73dc3ee..0000000 --- a/test/008_job_dependencies/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 008_job_dependencies/test.yml diff --git a/test/009_multi_file/stdout b/test/009_multi_file/stdout deleted file mode 100644 index ec1a3fd..0000000 --- a/test/009_multi_file/stdout +++ /dev/null @@ -1 +0,0 @@ -Fatal error validating 009_multi_file/xinvalid.yml diff --git a/test/run b/test/run deleted file mode 100755 index f249e41..0000000 --- a/test/run +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -cd "$(dirname "${BASH_SOURCE[0]}")" - -readonly cmd="../target/debug/action-validator" - -if ! [ -x "$cmd" ]; then - echo "No action-validator to test; have you run 'cargo build' lately?" >&2 - exit 1 -fi - -for t in [0-9]*; do - expected_stdout="$(cat "$t/stdout" 2>/dev/null || true)" - expected_stderr="$(cat "$t/stderr" 2>/dev/null || true)" - expected_exitcode="$(cat "$t/exitcode" 2>/dev/null || echo 0)" - - dir="$(mktemp -d)" - trap 'rm -rf "$dir"' EXIT - - failed=n - rv=0 - - readarray -t -d '' testfiles < <(find "$t"/ -name '*.yml' -print0) - - "$cmd" "${testfiles[@]}" >"$dir/stdout" 2>"$dir/stderr" || rv="$?" - stdout="$(cat "$dir"/stdout)" - stderr="$(cat "$dir"/stderr)" - - if [ "$rv" != "$expected_exitcode" ]; then - echo "Test $t exited with unexpected code: expected $expected_exitcode, got $rv" >&2 - failed=y - fi - - if [ "$stdout" != "$expected_stdout" ]; then - echo "Test $t produced unexpected stdout:" >&2 - diff -u <(echo "$expected_stdout") <(echo "$stdout") >&2 || true - failed=y - fi - - if [ "$stderr" != "$expected_stderr" ]; then - echo "Test $t produced unexpected stderr:" >&2 - diff -u <(echo "$expected_stderr") <(echo "$stderr") >&2 || true - failed=y - fi - - rm -rf "$dir" - trap "" EXIT - - if [ "$failed" = "y" ]; then - exit 1 - fi -done - -echo "All tests passed." diff --git a/test/001_basic_workflow/test.yml b/tests/001_basic_workflow/test.yml similarity index 100% rename from test/001_basic_workflow/test.yml rename to tests/001_basic_workflow/test.yml diff --git a/test/001_basic_workflow/validation_state.snap.json b/tests/001_basic_workflow/validation_state.snap.json similarity index 100% rename from test/001_basic_workflow/validation_state.snap.json rename to tests/001_basic_workflow/validation_state.snap.json diff --git a/test/002_basic_action/action.yml b/tests/002_basic_action/action.yml similarity index 100% rename from test/002_basic_action/action.yml rename to tests/002_basic_action/action.yml diff --git a/test/002_basic_action/validation_state.snap.json b/tests/002_basic_action/validation_state.snap.json similarity index 100% rename from test/002_basic_action/validation_state.snap.json rename to tests/002_basic_action/validation_state.snap.json diff --git a/test/003_successful_globs/glob.yml b/tests/003_successful_globs/glob.yml similarity index 88% rename from test/003_successful_globs/glob.yml rename to tests/003_successful_globs/glob.yml index 6608e8b..2981f5b 100644 --- a/test/003_successful_globs/glob.yml +++ b/tests/003_successful_globs/glob.yml @@ -3,7 +3,7 @@ name: Test on: push: paths: - - 003_successful_globs/* + - ./tests/003_successful_globs/* defaults: run: diff --git a/test/003_successful_globs/validation_state.snap.json b/tests/003_successful_globs/validation_state.snap.json similarity index 100% rename from test/003_successful_globs/validation_state.snap.json rename to tests/003_successful_globs/validation_state.snap.json diff --git a/test/004_failing_globs/exitcode b/tests/004_failing_globs/exitcode similarity index 100% rename from test/004_failing_globs/exitcode rename to tests/004_failing_globs/exitcode diff --git a/test/004_failing_globs/glob.yml b/tests/004_failing_globs/glob.yml similarity index 89% rename from test/004_failing_globs/glob.yml rename to tests/004_failing_globs/glob.yml index 7c4d27e..fa2a7c8 100644 --- a/test/004_failing_globs/glob.yml +++ b/tests/004_failing_globs/glob.yml @@ -3,7 +3,7 @@ name: Bad globs, no biscuit on: push: paths: - - 004_bad_globs/*.txt + - ./tests/004_bad_globs/*.txt defaults: run: diff --git a/test/004_failing_globs/stderr b/tests/004_failing_globs/stderr similarity index 70% rename from test/004_failing_globs/stderr rename to tests/004_failing_globs/stderr index 2171874..f7e987a 100644 --- a/test/004_failing_globs/stderr +++ b/tests/004_failing_globs/stderr @@ -3,13 +3,13 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "004_failing_globs/glob.yml", + "./tests/004_failing_globs/glob.yml", ), errors: [ NoFilesMatchingGlob { code: "glob_not_matched", detail: Some( - "Glob \"004_bad_globs/*.txt\" in /on/push/paths does not match any files", + "Glob \"./tests/004_bad_globs/*.txt\" in /on/push/paths does not match any files", ), path: "/on/push/paths", title: "Glob does not match any files", diff --git a/tests/004_failing_globs/stdout b/tests/004_failing_globs/stdout new file mode 100644 index 0000000..2455d96 --- /dev/null +++ b/tests/004_failing_globs/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/004_failing_globs/glob.yml diff --git a/test/004_failing_globs/validation_state.snap.json b/tests/004_failing_globs/validation_state.snap.json similarity index 100% rename from test/004_failing_globs/validation_state.snap.json rename to tests/004_failing_globs/validation_state.snap.json diff --git a/test/005_conditional_step_in_action/action.yml b/tests/005_conditional_step_in_action/action.yml similarity index 100% rename from test/005_conditional_step_in_action/action.yml rename to tests/005_conditional_step_in_action/action.yml diff --git a/test/005_conditional_step_in_action/validation_state.snap.json b/tests/005_conditional_step_in_action/validation_state.snap.json similarity index 100% rename from test/005_conditional_step_in_action/validation_state.snap.json rename to tests/005_conditional_step_in_action/validation_state.snap.json diff --git a/test/006_workflow_dispatch_inputs_options/test.yml b/tests/006_workflow_dispatch_inputs_options/test.yml similarity index 100% rename from test/006_workflow_dispatch_inputs_options/test.yml rename to tests/006_workflow_dispatch_inputs_options/test.yml diff --git a/test/006_workflow_dispatch_inputs_options/validation_state.snap.json b/tests/006_workflow_dispatch_inputs_options/validation_state.snap.json similarity index 100% rename from test/006_workflow_dispatch_inputs_options/validation_state.snap.json rename to tests/006_workflow_dispatch_inputs_options/validation_state.snap.json diff --git a/test/007_funky_syntax/exitcode b/tests/007_funky_syntax/exitcode similarity index 100% rename from test/007_funky_syntax/exitcode rename to tests/007_funky_syntax/exitcode diff --git a/test/007_funky_syntax/rust-check.yml b/tests/007_funky_syntax/rust-check.yml similarity index 100% rename from test/007_funky_syntax/rust-check.yml rename to tests/007_funky_syntax/rust-check.yml diff --git a/test/007_funky_syntax/stderr b/tests/007_funky_syntax/stderr similarity index 91% rename from test/007_funky_syntax/stderr rename to tests/007_funky_syntax/stderr index 17c1d62..fbafbf5 100644 --- a/test/007_funky_syntax/stderr +++ b/tests/007_funky_syntax/stderr @@ -3,7 +3,7 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "007_funky_syntax/rust-check.yml", + "./tests/007_funky_syntax/rust-check.yml", ), errors: [ Parse { diff --git a/tests/007_funky_syntax/stdout b/tests/007_funky_syntax/stdout new file mode 100644 index 0000000..a8b577f --- /dev/null +++ b/tests/007_funky_syntax/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/007_funky_syntax/rust-check.yml diff --git a/test/007_funky_syntax/validation_state.snap.json b/tests/007_funky_syntax/validation_state.snap.json similarity index 100% rename from test/007_funky_syntax/validation_state.snap.json rename to tests/007_funky_syntax/validation_state.snap.json diff --git a/test/008_job_dependencies/exitcode b/tests/008_job_dependencies/exitcode similarity index 100% rename from test/008_job_dependencies/exitcode rename to tests/008_job_dependencies/exitcode diff --git a/test/008_job_dependencies/stderr b/tests/008_job_dependencies/stderr similarity index 88% rename from test/008_job_dependencies/stderr rename to tests/008_job_dependencies/stderr index a8d64bf..d859143 100644 --- a/test/008_job_dependencies/stderr +++ b/tests/008_job_dependencies/stderr @@ -3,7 +3,7 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "008_job_dependencies/test.yml", + "./tests/008_job_dependencies/test.yml", ), errors: [ UnresolvedJob { diff --git a/tests/008_job_dependencies/stdout b/tests/008_job_dependencies/stdout new file mode 100644 index 0000000..3576abf --- /dev/null +++ b/tests/008_job_dependencies/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/008_job_dependencies/test.yml diff --git a/test/008_job_dependencies/test.yml b/tests/008_job_dependencies/test.yml similarity index 100% rename from test/008_job_dependencies/test.yml rename to tests/008_job_dependencies/test.yml diff --git a/test/008_job_dependencies/validation_state.snap.json b/tests/008_job_dependencies/validation_state.snap.json similarity index 100% rename from test/008_job_dependencies/validation_state.snap.json rename to tests/008_job_dependencies/validation_state.snap.json diff --git a/test/009_multi_file/exitcode b/tests/009_multi_file/exitcode similarity index 100% rename from test/009_multi_file/exitcode rename to tests/009_multi_file/exitcode diff --git a/test/009_multi_file/stderr b/tests/009_multi_file/stderr similarity index 92% rename from test/009_multi_file/stderr rename to tests/009_multi_file/stderr index b4fe130..6e52470 100644 --- a/test/009_multi_file/stderr +++ b/tests/009_multi_file/stderr @@ -3,7 +3,7 @@ Validation failed: ValidationState { Workflow, ), file_path: Some( - "009_multi_file/xinvalid.yml", + "./tests/009_multi_file/xinvalid.yml", ), errors: [ Parse { diff --git a/tests/009_multi_file/stdout b/tests/009_multi_file/stdout new file mode 100644 index 0000000..8e003c7 --- /dev/null +++ b/tests/009_multi_file/stdout @@ -0,0 +1 @@ +Fatal error validating ./tests/009_multi_file/xinvalid.yml diff --git a/test/009_multi_file/valid.yml b/tests/009_multi_file/valid.yml similarity index 100% rename from test/009_multi_file/valid.yml rename to tests/009_multi_file/valid.yml diff --git a/test/009_multi_file/validation_state.snap.json b/tests/009_multi_file/validation_state.snap.json similarity index 100% rename from test/009_multi_file/validation_state.snap.json rename to tests/009_multi_file/validation_state.snap.json diff --git a/test/009_multi_file/xinvalid.yml b/tests/009_multi_file/xinvalid.yml similarity index 100% rename from test/009_multi_file/xinvalid.yml rename to tests/009_multi_file/xinvalid.yml diff --git a/test/run.mjs b/tests/run.mjs similarity index 97% rename from test/run.mjs rename to tests/run.mjs index 7b026e6..6956c28 100644 --- a/test/run.mjs +++ b/tests/run.mjs @@ -9,9 +9,9 @@ const update = process.argv.includes("--update") || process.argv.includes("-u"); const start = Date.now(); const passed = fs - .readdirSync("test") - // get all directories in test/ - .map((entry) => `test/${entry}`) + .readdirSync("tests") + // get all directories in tests/ + .map((entry) => `tests/${entry}`) .filter((entry) => fs.statSync(entry).isDirectory()) // get the first .yml files in each directory .map((testDir) => [testDir, ...fs.readdirSync(testDir)]) diff --git a/tests/snapshot_tests.rs b/tests/snapshot_tests.rs new file mode 100644 index 0000000..568bd8d --- /dev/null +++ b/tests/snapshot_tests.rs @@ -0,0 +1,131 @@ +use std::{fs::{self, File}, ffi::{OsString, OsStr}}; +use std::io::prelude::*; + +use rstest::rstest; +use assert_cmd::Command; + + +#[derive(Debug)] +struct SnapshotTest { + test_dir: String, + exitcode: i32, + stderr: String, + stdout: String, + test_files: Vec, +} + +impl SnapshotTest { + fn new(test_dir: String) -> Self { + let stderr = fs::read_to_string( + format!("./tests/{test_dir}/stderr"), + ).unwrap_or(String::from("")); + + let stdout = fs::read_to_string( + format!("./tests/{test_dir}/stdout"), + ).unwrap_or(String::from("")); + + let exitcode: i32 = fs::read_to_string( + format!("./tests/{test_dir}/exitcode"), + ).map(|s| { + s + .strip_suffix("\n") + .unwrap_or(s.as_str()) + .parse::() + .unwrap_or(0) + }).unwrap_or(0); + + let test_files = Self::_get_files(&test_dir); + + SnapshotTest { + test_dir, + exitcode, + stderr, + stdout, + test_files, + } + } + + #[cfg(not(feature = "save-snapshots"))] + fn execute(self) { + Command::cargo_bin( + env!("CARGO_PKG_NAME"), + ) + .expect("binary to execute") + .args(self.test_files) + .assert() + .stdout(self.stdout) + .stderr(self.stderr) + .code(self.exitcode); + } + + #[cfg(feature = "save-snapshots")] + fn execute(&self) { + let test_dir = self.test_dir.to_owned(); + let test_files = self.test_files.to_owned(); + let result = Command::cargo_bin( + env!("CARGO_PKG_NAME"), + ) + .expect("binary to execute") + .args(test_files).ok().unwrap_or_else(|e| e.as_output().unwrap().to_owned()); + + if !result.stdout.is_empty() { + self._save_contents( + format!("./tests/{test_dir}/stdout"), + result.stdout, + ); + } + if !result.stderr.is_empty() { + self._save_contents( + format!("./tests/{test_dir}/stderr"), + result.stderr, + ); + } + if let Some(exitcode) = result.status.code() { + if exitcode > 0 { + self._save_contents( + format!("./tests/{test_dir}/exitcode"), + format!("{exitcode}\n").into(), + ); + } + } + } + + fn _save_contents( + &self, + file_name: String, + contents: Vec, + ) { + println!("Saving {}", file_name); + let res = File::create(file_name).unwrap().write_all( + &contents, + ); + assert!(res.is_ok(), "{:?}", res); + } + + fn _get_files(test_dir: &String) -> Vec { + let yml = Some(OsStr::new("yml")); + fs::read_dir( + format!("./tests/{test_dir}"), + ) + .unwrap() + .filter_map(Result::ok) + .filter(|f| f.path().extension() == yml) + .map(|f| f.path().into_os_string()) + .collect::>() + } +} + + +#[rstest] +#[case("001_basic_workflow")] +#[case("002_basic_action")] +#[case("003_successful_globs")] +#[case("004_failing_globs")] +#[case("005_conditional_step_in_action")] +#[case("006_workflow_dispatch_inputs_options")] +#[case("007_funky_syntax")] +#[case("008_job_dependencies")] +#[case("009_multi_file")] +fn snapshot(#[case] dir_name: String) { + SnapshotTest::new(dir_name).execute(); +}