diff --git a/Dockerfile.bin b/Dockerfile.bin new file mode 100644 index 0000000..6835e02 --- /dev/null +++ b/Dockerfile.bin @@ -0,0 +1,17 @@ +FROM python:3.4-slim + +RUN apt-get update -y \ + && apt-get install -y curl \ + && apt-get clean + +RUN mkdir -p ~/.clusterrunner/dist \ + && cd ~/.clusterrunner \ + && curl -L https://cloud.box.com/shared/static/2pl4pi6ykvrbb9d06t4m.tgz > clusterrunner.tgz \ + && tar -zxvf clusterrunner.tgz -C ./dist \ + && cp ./dist/conf/default_clusterrunner.conf clusterrunner.conf \ + && chmod 600 clusterrunner.conf \ + && rm -f clusterrunner.tgz + +EXPOSE 43000 43001 + +ENTRYPOINT ["/root/.clusterrunner/dist/clusterrunner"] diff --git a/Dockerfile.src b/Dockerfile.src new file mode 100644 index 0000000..94ec236 --- /dev/null +++ b/Dockerfile.src @@ -0,0 +1,13 @@ +FROM python:3.4-slim + +RUN apt-get update -y \ + && apt-get install -y gcc git \ + && apt-get clean + +ADD . /src + +RUN pip install --upgrade -r /src/requirements.txt + +EXPOSE 43000 43001 + +ENTRYPOINT ["/src/main.py"] diff --git a/docker-compose-bin.yml b/docker-compose-bin.yml new file mode 100644 index 0000000..4f0a0b6 --- /dev/null +++ b/docker-compose-bin.yml @@ -0,0 +1,11 @@ +version: "2.1" +services: + cluserrunner: + build: + context: . + dockerfile: Dockerfile.bin + image: yamaszone/clusterrunner:bin + ports: + - "43000:43000" + - "43001:43001" + diff --git a/docker-compose-src.yml b/docker-compose-src.yml new file mode 100644 index 0000000..2cfebb3 --- /dev/null +++ b/docker-compose-src.yml @@ -0,0 +1,7 @@ +version: "2.1" +services: + cluserrunner: + build: + dockerfile: Dockerfile.src + image: yamaszone/clusterrunner:src + diff --git a/docs/docker/README.md b/docs/docker/README.md new file mode 100644 index 0000000..84f9379 --- /dev/null +++ b/docs/docker/README.md @@ -0,0 +1,57 @@ +## TODO for Dockerization +* Add maintainer(s) + - Maintainer for CR Dockerfile needs to be added after pull request is merged. Ideally the maintainer should be someone from official team with appropriate Docker Hub permission to tag image as `box/clusterrunner:latest`. I can volunteer if needed but that will require collaborator permission for Docker Hub. +* Lean Docker image (consider tiny base e.g. busybox, alpine, etc.) + - Currently `Dockerfile.src` uses `python:3.4-slim` as the base. I attempted `python:alpine` as the base but it didn't feel like worth the [trouble](https://github.com/docker/docker/issues/27940). My docker version is `1.12.6` but the problem might have been fixed in `1.13.0+`. I will revisit this in the future. +* Reorganize directory structure (cleaner project root preferred) + - I was considering potential restructure of CR project root directory similar to [this](https://gist.github.com/yamaszone/6a4304069652a4a01ecdacdd4e7c7df1) so that: + - Only relevant project artefacts can be easily added into Docker image excluding non-PROD dependencies like tests, docs, Dockerfiles, docker-compose.yaml, and so on... + - Non-PROD tools/libs installation inside Docker container is excluded by splitting `requirements.txt` into `requirements-prod.txt` and `requirements-non-prod.txt` to deal with security/maintenance aspects of production dependencies as a priority basis + - Readability can be improved + - ... :) + - I will hold off on this as it will potentially require breaking changes. Also allowing more time to rethink this! + +## User Guide +#### Usage Scenarios +[ClusterRunner container](https://hub.docker.com/r/yamaszone/clusterrunner/) is a good choice if your application runtime environment supports [Docker Engine](https://docs.docker.com/engine/installation/). For example, if your application under test is running on macOS with Docker Engine installed, then you can easily use the ClusterRunner Docker container to run tests in parallel. +To see ClusterRunner CLI help, run the following command: + +`docker run --rm -v /path/to/system/under/test:/sut -w /sut yamaszone/clusterrunner:bin -h` + +See [Quick Start](http://www.clusterrunner.com/docs/quickstart/) guide to configure your project to use ClusterRunner. + +**NOTE**: ClusterRunner container is yet to add parallel testing support easily for applications that run as containers. + +## Developer Guide +Currently we are maintaining two flavors of Dockerfiles for ClusterRunner: +* `Dockerfile.bin`: Build ClusterRunner image from binary with minimal image size +* `Dockerfile.src`: Build ClusterRunner image from source +In the future, we plan to maintain `Dockerfile.src` only after simplifying some dependencies mentioned [here](https://github.com/box/ClusterRunner/issues/328). + +#### Requirements +* [Docker Engine](https://docs.docker.com/engine/installation/) version 1.12.x+ +* [Docker Compose](https://docs.docker.com/compose/) version 1.11.x+ with API version 2.1 + +#### Requirements Setup +* Install docker-compose on CoreOS +```sh +$ sudo su - +$ curl -L https://github.com/docker/compose/releases/download/1.11.2/docker-compose-`uname -s`-`uname -m` > /opt/bin/docker-compose +$ chmod +x /opt/bin/docker-compose +``` +* For other platforms, see instructions [here](https://docs.docker.com/compose/install/) +``` +$ curl -L "https://github.com/docker/compose/releases/download/1.11.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +$ chmod +x /usr/local/bin/docker-compose +``` + +#### Development/Test Workflow +We use `docker-compose` to maintain two flavors of Docker images: `docker-compose-bin.yml` for `Dockerfile.bin` and `docker-compose-src.yml` for `Dockerfile.src`. Project root contains a convenient script `run-cr` to easily build/test/push Docker images (see `./run-cr help`): +* Build + - Run `./run-cr build` to build both flavors of ClusterRunner containers +* Test + - Run `./run-cr test-bats` to run sanity tests for the ClusterRunner containers. We use lightweight [BATS](https://github.com/sstephenson/bats) framework to write automated tests (see `test/bats`) for the ClusterRunner images. +* Push + - Run `./run-cr push` to push ClusterRunner images to Docker Hub +* Image Versions + - Currently [ClusterRunner images](https://hub.docker.com/r/yamaszone/clusterrunner/tags/) are tagged as `bin` and `src`. To improve maintainability of these images, we will concatenate ClusterRunner versions with the container tags in the future. diff --git a/run-cr b/run-cr new file mode 100755 index 0000000..5eee064 --- /dev/null +++ b/run-cr @@ -0,0 +1,78 @@ +#!/bin/bash + +# This script automates the workflow around packaging CR using Docker. + +# yamaszone should be replaced with official user +DR_USR=yamaszone +CR_BIN=$DR_USR/clusterrunner:bin +CR_SRC=$DR_USR/clusterrunner:src + +build_images(){ + # *-src.yml only overrides 'dockerfile:' flavor and corresponding 'image:' name + docker-compose -f docker-compose-bin.yml -f docker-compose-src.yml build +} + +push_images(){ + # Uses Docker Hub as default + docker push $CR_BIN + docker push $CR_SRC +} + +help(){ + echo "Usage:" + printf "\t bin\t\t: Run ClusterRunner from binary.\n" + printf "\t build\t\t: Build ClusterRunner Docker images.\n" + printf "\t help\t\t: Show this help.\n" + printf "\t push\t\t: Push ClusterRunner images to Docker Hub.\n" + printf "\t src\t\t: Run ClusterRunner from source.\n" + printf "\t test-bats\t: Run BATS tests to validate image builds.\n" + exit 0 +} + +install_bats(){ + # Install BATS framework if not installed already + type bats >/dev/null 2>&1 + if [[ "$?" -ne "0" ]]; then + git clone -b v0.4.0 https://github.com/sstephenson/bats.git /tmp/bats + OS_NAME=$(uname -a) + echo $OS_NAME + if [[ $OS_NAME == *"coreos"* ]]; then + cd /tmp/bats && sudo ./install.sh /opt + else + cd /tmp/bats && sudo ./install.sh /usr/local + fi + cd - + rm -rf /tmp/bats + fi +} + +if [[ -z $1 ]];then + help + exit 0 +fi +# Inputs to pass on to CR via this script; exclude $1 +INPUTS=`echo "${@:2}"` + +case $1 in + bin) + docker run --rm $CR_BIN $INPUTS + ;; + build) + build_images + ;; + push) + push_images + ;; + src) + docker run --rm $CR_SRC $INPUTS + ;; + # TechDebt: Could be a target in Makefile + # CoreOS doesn't come with Make; so staying within it's limits + test-bats) + install_bats + bats test/bats/ + ;; + * | help) + help + ;; +esac diff --git a/test/bats/fixtures/.gitignore b/test/bats/fixtures/.gitignore new file mode 100644 index 0000000..0523022 --- /dev/null +++ b/test/bats/fixtures/.gitignore @@ -0,0 +1 @@ +#ignore diff --git a/test/bats/helpers/assertions/LICENSE b/test/bats/helpers/assertions/LICENSE new file mode 100644 index 0000000..80955a7 --- /dev/null +++ b/test/bats/helpers/assertions/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jason Karns + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/test/bats/helpers/assertions/README.md b/test/bats/helpers/assertions/README.md new file mode 100644 index 0000000..c9049c0 --- /dev/null +++ b/test/bats/helpers/assertions/README.md @@ -0,0 +1,170 @@ +# bats-assert +Assertion library for BATS (Bash Automated Testing System) + +## Installation + +Recommended installation is via git submodule. Assuming your project's bats +tests are in `test`: + +``` sh +git submodule add https://github.com/jasonkarns/bats-assert test/helpers/assertions +git commit -am 'added bats-assert module' +``` + +then in `test/test_helper.bash`: + +``` bash +load helpers/assertions/all +``` + +(Optionally configure [sparse-checkout](http://git-scm.com/docs/git-read-tree#_sparse_checkout) if you're concerned with all the non-essential files being in your repo) + +Also available as an [npm module](https://www.npmjs.com/package/bats-assert) if you're into that sort of thing. + +``` sh +npm install --save-dev bats-assert +``` + +then in `test/test_helper.bash`: + +``` bash +load ../node_modules/bats-assert/all +``` + +## Assertion API + +### flunk +forces a test failure with an optional message + +``` bash +flunk +# or +flunk "expected blue skies" +``` + +### assert +asserts command returns successfully + +``` bash +assert my-command +assert [ 2 -eq 2 ] +``` + +### refute +asserts command returns unsuccessfully + +``` bash +refute invalid-command +refute [ 2 -eq 3 ] +``` + +### assert_success +asserts successful exit `$status` with (optional) `$output` + +``` bash +run my-command + +assert_success +# or +assert_success "expected output" +``` + +### assert_failure +asserts unsuccessful exit `$status` with (optional) `$output` + +``` bash +run my-command + +assert_failure +# or +assert_failure "expected output" +``` + +### assert_equal +asserts equality + +``` bash +actual="$(my-command)" +expected="my results" + +assert_equal expected actual +``` + +### assert_contains +asserts x contains y + +``` +assert_contains foobar oo +``` + +### refute_contains +asserts x does not contain y + +``` +refute_contains foobar baz +``` + +### assert_starts_with +asserts x starts with y + +``` +assert_starts_with foobar foo +``` + +### assert_output +asserts `$output` + +``` +run my-command + +assert_output "my results" +``` + +### assert_output_contains +asserts `$output` contains argument + +``` +run my-command + +assert_output_contains "results" +``` + +### refute_output_contains +asserts `$output` does not contain argument + +``` +run my-command + +refute_output_contains "unicorn" +``` + +### assert_line +asserts `$output` contains given line (at optional line index) + +``` +run my-command + +assert_line "my results" +# or +assert_line 0 "my results" +``` + +### refute_line +asserts `$output` does *not* contain given line + +``` +run my-command + +refute_line "thirsty rando" +``` + +## Credits + +Assertion functions taken from the test_helpers of [rbenv][], [ruby-build][], +and [rbenv-aliases][]. Many thanks to their authors and contributors: [Sam +Stephenson](https://github.com/sstephenson), [Mislav +Marohnić](https://github.com/mislav), and [Tim Pope](https://github.com/tpope). + +[rbenv]:https://github.com/sstephenson/rbenv +[ruby-build]:https://github.com/sstephenson/ruby-build +[rbenv-aliases]:https://github.com/tpope/rbenv-aliases diff --git a/test/bats/helpers/assertions/all.bash b/test/bats/helpers/assertions/all.bash new file mode 100644 index 0000000..952064c --- /dev/null +++ b/test/bats/helpers/assertions/all.bash @@ -0,0 +1,139 @@ +flunk() { + { if [ "$#" -eq 0 ]; then cat - + else echo "$@" + fi + } | sed "s:${BATS_TMPDIR}:\${BATS_TMPDIR}:g" >&2 + return 1 +} + +assert() { + if ! "$@"; then + flunk "failed: $@" + fi +} + +refute() { + if "$@"; then + flunk "expected to fail: $@" + fi +} + +assert_success() { + if [ "$status" -ne 0 ]; then + { echo "command failed with exit status $status" + echo "output: $output" + } | flunk + elif [ "$#" -gt 0 ]; then + assert_output "$1" + fi +} + +assert_failure() { + if [ "$status" -eq 0 ]; then + flunk "expected failed exit status" + elif [ "$#" -gt 0 ]; then + assert_output "$1" + fi +} + +assert_equal() { + if [ "$1" != "$2" ]; then + { echo "expected: $1" + echo "actual: $2" + } | flunk + fi +} + +refute_equal() { + if [ "$1" = "$2" ]; then + flunk "unexpectedly equal: $1" + fi +} + +assert_not_equal() { + refute_equal "$@" +} + +assert_contains() { + local haystack="$1" + local needle="$2" + echo "$haystack" | $(type -p ggrep grep | head -1) -F "$needle" >/dev/null || { + { echo "expected: $haystack" + echo "to contain: $needle" + } | flunk + } +} + +refute_contains() { + local haystack="$1" + local needle="$2" + ! assert_contains "$haystack" "$needle" || { + { echo "expected: $haystack" + echo "not to contain: $needle" + } | flunk + } +} + +assert_starts_with() { + if [ "$1" = "${1#${2}}" ]; then + { echo "expected: $1" + echo "to start with: $2" + } | flunk + fi +} + +assert_output() { + local expected + if [ $# -eq 0 ]; then expected="$(cat -)" + else expected="$1" + fi + assert_equal "$expected" "$output" +} + +assert_output_contains() { + local expected + if [ $# -eq 0 ]; then expected="$(cat -)" + else expected="$1" + fi + assert_contains "$output" "$expected" +} + +refute_output_contains() { + local expected + if [ $# -eq 0 ]; then expected="$(cat -)" + else expected="$1" + fi + refute_contains "$output" "$expected" +} + +assert_line() { + if [ "$1" -ge 0 ] 2>/dev/null; then + assert_equal "$2" "${lines[$1]}" + else + local line + for line in "${lines[@]}"; do + if [ "$line" = "$1" ]; then return 0; fi + done + { echo "expected line: $1" + echo "to be found in:" + ( IFS=$'\n'; echo "${lines[*]}" ) + } | flunk + fi +} + +refute_line() { + if [ "$1" -ge 0 ] 2>/dev/null; then + refute_equal "$2" "${lines[$1]}" + else + local line + for line in "${lines[@]}"; do + if [ "$line" = "$1" ]; then + { echo "expected to not find line: $line" + echo "in:" + ( IFS=$'\n'; echo "${lines[*]}" ) + } | flunk + return $? # in case flunk didn't exit the loop + fi + done + fi +} diff --git a/test/bats/helpers/assertions/package.json b/test/bats/helpers/assertions/package.json new file mode 100644 index 0000000..ba3b471 --- /dev/null +++ b/test/bats/helpers/assertions/package.json @@ -0,0 +1,45 @@ +{ + "name": "bats-assert", + "version": "1.1.0", + "description": "Assertion library for BATS (Bash Automated Testing System)", + "author": "Mislav Marohnić (http://mislav.net)", + "contributors": [ + "Sam Stephenson (http://sstephenson.us/)", + "Jason Karns (http://jason.karns.name/)" + ], + "homepage": "https://github.com/jasonkarns/bats-assert#readme", + "repository": { + "type": "git", + "url": "https://github.com/jasonkarns/bats-assert.git" + }, + "bugs": { + "url": "https://github.com/jasonkarns/bats-assert/issues" + }, + "scripts": { + "test": "bats test" + }, + "main": "all.bash", + "files": [ + "all.bash" + ], + "directories": { + "test": "test" + }, + "peerDependencies": { + "bats": "^0.4.2" + }, + "devDependencies": { + "bats": "^0.4.2" + }, + "license": "MIT", + "keywords": [ + "bash", + "bats", + "assert", + "assertion", + "expectation", + "shell", + "test", + "unit" + ] +} diff --git a/test/bats/helpers/assertions/test/assert.bats b/test/bats/helpers/assertions/test/assert.bats new file mode 100755 index 0000000..2a86e96 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load ../all + +@test "assert should pass with successful command" { + set +e + assert "true" + status=$? + set -e + + test $status = 0 +} + +@test "assert should fail for failed commands" { + set +e + assert "false" + status=$? + set -e + + test $status = 1 +} + +@test "assert should emit failure message for failed commands" { + set +e + stderr=$( { assert "false"; } 2>&1 ) + set -e + + test "$stderr" = "failed: false" +} diff --git a/test/bats/helpers/assertions/test/assert_contains.bats b/test/bats/helpers/assertions/test/assert_contains.bats new file mode 100755 index 0000000..c79764f --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_contains.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load ../all + +@test "assert_contains should pass when it matches" { + set +e + assert_contains foobar bar + status=$? + set -e + + test $status = 0 +} + +@test "assert_contains should fail when it doesn't match" { + set +e + assert_contains foo bar + status=$? + set -e + + test $status = 1 +} + +@test "assert_contains should emit error message when fails" { + set +e + stderr=$( { assert_contains foo bar; } 2>&1 ) + set -e + + test "$stderr" = $'expected: foo\nto contain: bar' +} diff --git a/test/bats/helpers/assertions/test/assert_equal.bats b/test/bats/helpers/assertions/test/assert_equal.bats new file mode 100755 index 0000000..4e7b24e --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_equal.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load ../all + +@test "assert_equal should pass when equal" { + set +e + assert_equal foo foo + status=$? + set -e + + test $status = 0 +} + +@test "assert_equal should fail when not equal" { + set +e + assert_equal foo bar + status=$? + set -e + + test $status = 1 +} + +@test "assert_equal should emit message on failure" { + set +e + stderr=$( { assert_equal foo bar; } 2>&1 ) + set -e + + test "$stderr" = $'expected: foo\nactual: bar' +} diff --git a/test/bats/helpers/assertions/test/assert_failure.bats b/test/bats/helpers/assertions/test/assert_failure.bats new file mode 100755 index 0000000..34c2d58 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_failure.bats @@ -0,0 +1,57 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + status=7 + output="my bad" +} + +@test "assert_failure should pass on non-zero \$status" { + set +e + assert_failure + status=$? + set -e + + test $status = 0 +} + +@test "assert_failure should fail on 0 \$status" { + status=0 + + set +e + assert_failure + status=$? + set -e + + test $status = 1 +} + +@test "assert_failure should emit message on failure" { + status=0 + + set +e + stderr=$( { assert_failure; } 2>&1 ) + set -e + + test "$stderr" = $'expected failed exit status' +} + +@test "assert_failure should pass when output matching argument" { + set +e + assert_failure "my bad" + status=$? + set -e + + test $status = 0 +} + +@test "assert_failure should fail when output doesn't match argument" { + set +e + stderr=$( { assert_failure "good job"; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'expected: good job\nactual: my bad' +} diff --git a/test/bats/helpers/assertions/test/assert_line.bats b/test/bats/helpers/assertions/test/assert_line.bats new file mode 100755 index 0000000..4c3e3e2 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_line.bats @@ -0,0 +1,52 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + lines=('one fish' 'two fish' 'red fish' 'blue fish') +} + +@test "assert_line should pass when the given line is found" { + set +e + assert_line "red fish" + status=$? + set -e + + test $status = 0 +} + +@test "assert_line should fail when the given line isn't found" { + set +e + assert_line "green eggs and ham" + status=$? + set -e + + test $status = 1 +} + +@test "assert_line should emit error message when it fails" { + set +e + stderr=$( { assert_line "green eggs and ham"; } 2>&1 ) + set -e + + test "$stderr" = $'expected line: green eggs and ham\nto be found in:\none fish\ntwo fish\nred fish\nblue fish' +} + +@test "assert_line can match against a given line index" { + # success + set +e + assert_line 2 "red fish" + status=$? + set -e + + test $status = 0 + + # failure + set +e + stderr=$( { assert_line 0 "red fish"; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'expected: red fish\nactual: one fish' +} diff --git a/test/bats/helpers/assertions/test/assert_output.bats b/test/bats/helpers/assertions/test/assert_output.bats new file mode 100755 index 0000000..bec19f0 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_output.bats @@ -0,0 +1,43 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + output=foo +} + +@test "assert_output should pass when it matches" { + set +e + assert_output foo + status=$? + set -e + + test $status = 0 +} + +@test "assert_output should fail when it doesn't match" { + set +e + assert_output bar + status=$? + set -e + + test $status = 1 +} + +@test "assert_output should emit error message when fails" { + set +e + stderr=$( { assert_output bar; } 2>&1 ) + set -e + + test "$stderr" = $'expected: bar\nactual: foo' +} + +@test "assert_output can take argument from STDIN" { + set +e + stderr=$( { echo bar | assert_output; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'expected: bar\nactual: foo' +} diff --git a/test/bats/helpers/assertions/test/assert_output_contains.bats b/test/bats/helpers/assertions/test/assert_output_contains.bats new file mode 100755 index 0000000..49eaf19 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_output_contains.bats @@ -0,0 +1,43 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + output=foobar +} + +@test "assert_output_contains should pass when true" { + set +e + assert_output_contains bar + status=$? + set -e + + test $status = 0 +} + +@test "assert_output_contains should fail when it doesn't match" { + set +e + assert_output_contains baz + status=$? + set -e + + test $status = 1 +} + +@test "assert_output_contains should emit error message when fails" { + set +e + stderr=$( { assert_output_contains baz; } 2>&1 ) + set -e + + test "$stderr" = $'expected: foobar\nto contain: baz' +} + +@test "assert_output_contains can take argument from STDIN" { + set +e + stderr=$( { echo baz | assert_output_contains; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'expected: foobar\nto contain: baz' +} diff --git a/test/bats/helpers/assertions/test/assert_starts_with.bats b/test/bats/helpers/assertions/test/assert_starts_with.bats new file mode 100755 index 0000000..2d70875 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_starts_with.bats @@ -0,0 +1,47 @@ +#!/usr/bin/env bats + +load ../all + +@test "assert_starts_with should pass when it matches" { + set +e + assert_starts_with foobar foo + status=$? + set -e + + test $status = 0 +} + +@test "assert_starts_with should fail when it doesn't match" { + set +e + assert_starts_with foo bar + status=$? + set -e + + test $status = 1 +} + +@test "assert_starts_with should emit error message when fails" { + set +e + stderr=$( { assert_starts_with foo bar; } 2>&1 ) + set -e + + test "$stderr" = $'expected: foo\nto start with: bar' +} + +@test "assert_starts_with should not match empty string" { + set +e + assert_starts_with foo "" + status=$? + set -e + + test $status = 1 +} + +@test "assert_starts_with should match original string" { + set +e + assert_starts_with foo foo + status=$? + set -e + + test $status = 0 +} diff --git a/test/bats/helpers/assertions/test/assert_success.bats b/test/bats/helpers/assertions/test/assert_success.bats new file mode 100755 index 0000000..5a63c17 --- /dev/null +++ b/test/bats/helpers/assertions/test/assert_success.bats @@ -0,0 +1,58 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + status=0 + output="good job" +} + +@test "assert_success should pass on 0 \$status" { + set +e + assert_success + status=$? + set -e + + test $status = 0 +} + +@test "assert_success should fail on non-zero \$status" { + status=1 + + set +e + assert_success + status=$? + set -e + + test $status = 1 +} + +@test "assert_success should emit message on failure" { + status=7 + output="my bad" + + set +e + stderr=$( { assert_success; } 2>&1 ) + set -e + + test "$stderr" = $'command failed with exit status 7\noutput: my bad' +} + +@test "assert_success should pass when output matches argument" { + set +e + assert_success "good job" + status=$? + set -e + + test $status = 0 +} + +@test "assert_success should fail when output doesn't match argument" { + set +e + stderr=$( { assert_success "my bad"; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'expected: my bad\nactual: good job' +} diff --git a/test/bats/helpers/assertions/test/flunk.bats b/test/bats/helpers/assertions/test/flunk.bats new file mode 100755 index 0000000..32d397d --- /dev/null +++ b/test/bats/helpers/assertions/test/flunk.bats @@ -0,0 +1,36 @@ +#!/usr/bin/env bats + +load ../all + +@test "flunk returns status code of 1" { + set +e + flunk msg + status=$? + set -e + + test $status = 1 +} + +@test "flunk emits the given message to STDERR" { + set +e + stderr=$( { flunk message; } 2>&1 ) + set -e + + test "$stderr" = "message" +} + +@test "flunk accepts error message on STDIN" { + set +e + stderr=$( { echo message | flunk; } 2>&1 ) + set -e + + test "$stderr" = "message" +} + +@test "flunk replaces \$BATS_TMPDIR" { + set +e + stderr=$( { flunk "bats tmpdir: $BATS_TMPDIR"; } 2>&1 ) + set -e + + test "$stderr" = "bats tmpdir: \${BATS_TMPDIR}" +} diff --git a/test/bats/helpers/assertions/test/refute.bats b/test/bats/helpers/assertions/test/refute.bats new file mode 100755 index 0000000..2c57681 --- /dev/null +++ b/test/bats/helpers/assertions/test/refute.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load ../all + +@test "refute should pass with failed command" { + set +e + refute "false" + status=$? + set -e + + test $status = 0 +} + +@test "refute should fail for successful commands" { + set +e + refute "true" + status=$? + set -e + + test $status = 1 +} + +@test "refute should emit failure message for successful commands" { + set +e + stderr=$( { refute "true"; } 2>&1 ) + set -e + + test "$stderr" = "succeeded: true" +} diff --git a/test/bats/helpers/assertions/test/refute_contains.bats b/test/bats/helpers/assertions/test/refute_contains.bats new file mode 100644 index 0000000..b8453d3 --- /dev/null +++ b/test/bats/helpers/assertions/test/refute_contains.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load ../all + +@test "refute_contains should pass when needle isn't found" { + set +e + refute_contains foobar baz + status=$? + set -e + + test $status = 0 +} + +@test "refute_contains should fail when needle is found" { + set +e + refute_contains foobar bar + status=$? + set -e + + test $status = 1 +} + +@test "refute_contains should emit error message when fails" { + set +e + stderr=$( { refute_contains foobar bar; } 2>&1 ) + set -e + + test "$stderr" = $'expected: foobar\nnot to contain: bar' +} diff --git a/test/bats/helpers/assertions/test/refute_equal.bats b/test/bats/helpers/assertions/test/refute_equal.bats new file mode 100755 index 0000000..885c03c --- /dev/null +++ b/test/bats/helpers/assertions/test/refute_equal.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load ../all + +@test "refute_equal should fail when equal" { + set +e + refute_equal foo foo + status=$? + set -e + + test $status = 1 +} + +@test "refute_equal should pass when not equal" { + set +e + refute_equal foo bar + status=$? + set -e + + test $status = 0 +} + +@test "refute_equal should emit message on failure" { + set +e + stderr=$( { refute_equal foo foo; } 2>&1 ) + set -e + + test "$stderr" = $'unexpectedly equal: foo' +} diff --git a/test/bats/helpers/assertions/test/refute_line.bats b/test/bats/helpers/assertions/test/refute_line.bats new file mode 100755 index 0000000..f41706d --- /dev/null +++ b/test/bats/helpers/assertions/test/refute_line.bats @@ -0,0 +1,52 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + lines=('one fish' 'two fish' 'red fish' 'blue fish') +} + +@test "refute_line should fail when the given line is found" { + set +e + refute_line "red fish" + status=$? + set -e + + test $status = 1 +} + +@test "refute_line should pass when the given line isn't found" { + set +e + refute_line "green eggs and ham" + status=$? + set -e + + test $status = 0 +} + +@test "refute_line should emit error message when it fails" { + set +e + stderr=$( { refute_line "red fish"; } 2>&1 ) + set -e + + test "$stderr" = $'expected to not find line: red fish\nin:\none fish\ntwo fish\nred fish\nblue fish' +} + +@test "refute_line can match against a given line index" { + # success + set +e + refute_line 0 "red fish" + status=$? + set -e + + test $status = 0 + + # failure + set +e + stderr=$( { refute_line 2 "red fish"; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'unexpectedly equal: red fish' +} diff --git a/test/bats/helpers/assertions/test/refute_output_contains.bats b/test/bats/helpers/assertions/test/refute_output_contains.bats new file mode 100644 index 0000000..cb6bda1 --- /dev/null +++ b/test/bats/helpers/assertions/test/refute_output_contains.bats @@ -0,0 +1,43 @@ +#!/usr/bin/env bats + +load ../all + +setup() { + output=foobar +} + +@test "refute_output_contains should pass when output doesn't contain needle" { + set +e + refute_output_contains baz + status=$? + set -e + + test $status = 0 +} + +@test "refute_output_contains should fail when output contains needle" { + set +e + refute_output_contains bar + status=$? + set -e + + test $status = 1 +} + +@test "refute_output_contains should emit error message when fails" { + set +e + stderr=$( { refute_output_contains bar; } 2>&1 ) + set -e + + test "$stderr" = $'expected: foobar\nnot to contain: bar' +} + +@test "refute_output_contains can take argument from STDIN" { + set +e + stderr=$( { echo bar | refute_output_contains; } 2>&1 ) + status=$? + set -e + + test $status = 1 + test "$stderr" = $'expected: foobar\nnot to contain: bar' +} diff --git a/test/bats/run-cr.bats b/test/bats/run-cr.bats new file mode 100644 index 0000000..d8f329f --- /dev/null +++ b/test/bats/run-cr.bats @@ -0,0 +1,32 @@ +#!/usr/bin/env bats + +load test_helper + +CR_SRC=yamaszone/clusterrunner:src +CR_DEMO_REPO=https://github.com/boxengservices/ClusterRunnerDemo.git + +@test "'run-cr' script displays help." { + run ./run-cr + assert_contains "$output" "Usage:" +} + +@test "CR-BIN: Help msg displayed by image built from binary." { + run ./run-cr bin -h + assert_contains "$output" "usage: clusterrunner" +} + +@test "CR-SRC: Help msg displayed by image built from source." { + run ./run-cr src -h + assert_contains "$output" "usage: main.py" +} + +#slow-test +@test "CR-SRC: Image built from source can build job properly." { + git clone $CR_DEMO_REPO /tmp/cr-demo + cd /tmp/cr-demo + run docker run --rm -v $PWD:/sut -w /sut $CR_SRC build --job-name Simple + assert_contains "$output" "NO_FAILURES" + cd - + # Needs super user privilege as build artefacts are written as root + sudo rm -rf /tmp/cr-demo +} diff --git a/test/bats/test_helper.bash b/test/bats/test_helper.bash new file mode 100644 index 0000000..8eb058b --- /dev/null +++ b/test/bats/test_helper.bash @@ -0,0 +1,18 @@ +load helpers/assertions/all + +fixtures() { + FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures/$1" + RELATIVE_FIXTURE_ROOT="$(bats_trim_filename "$FIXTURE_ROOT")" +} + +setup() { + export TMP="$BATS_TEST_DIRNAME/tmp" +} + +filter_control_sequences() { + "$@" | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g' +} + +teardown() { + [ -d "$TMP" ] && rm -f "$TMP"/* +} diff --git a/test/bats/tmp/.gitignore b/test/bats/tmp/.gitignore new file mode 100644 index 0000000..0523022 --- /dev/null +++ b/test/bats/tmp/.gitignore @@ -0,0 +1 @@ +#ignore