From 7299e2a7e77b7ba290371d35fc5870b123e8baa1 Mon Sep 17 00:00:00 2001 From: David Caro Date: Fri, 30 Jun 2023 22:21:40 +0200 Subject: [PATCH] ci: first api tests Signed-off-by: David Caro --- .github/workflows/test.yaml | 17 ++++-- api-tests/helpers.bash | 85 ++++++++++++++++++++++++++ api-tests/test_expenses.bats | 114 +++++++++++++++++++++++++++++++++++ db/fake_data.sql | 10 --- db/schema.sql | 13 +--- scripts/start_dev.sh | 13 +++- scripts/start_devdb.sh | 2 +- 7 files changed, 223 insertions(+), 31 deletions(-) create mode 100644 api-tests/helpers.bash create mode 100644 api-tests/test_expenses.bats diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8c650d5..fca261a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,10 +10,13 @@ jobs: runs-on: ubuntu-latest steps: - - name: Setup Podman + - name: Install packages run: | sudo apt update - sudo apt-get -y install podman + sudo apt-get -y install podman python3-virtualenv jq + python -m virtualenv tests-venv + source tests-venv/bin/activate + pip install bats-core-pkg - name: Get source uses: actions/checkout@v3 with: @@ -22,11 +25,15 @@ jobs: run: | set -x cd expenses-server - ./scripts/start_devdb.sh populate + ./scripts/start_devdb.sh # fake accoutns config mkdir -p ../home-lab-secrets/home_automation/expenses/ echo "accounts-config: {'accounts': []}" > ../home-lab-secrets/home_automation/expenses/application-accounts.yml - podman run --rm mariadb - ./scripts/start_dev.sh + ./scripts/start_dev.sh notail + - name: run tests + run: | + source tests-venv/bin/activate + cd expenses-server + bats_core_pkg --verbose-run --trace api-tests/ diff --git a/api-tests/helpers.bash b/api-tests/helpers.bash new file mode 100644 index 0000000..6b78200 --- /dev/null +++ b/api-tests/helpers.bash @@ -0,0 +1,85 @@ +#!/usr/bin/env bats +BASE_URL=http://127.0.0.1:8080 + +do_post() { + local path="${1?}" + local data="${2?}" + curl \ + -X POST \ + --silent \ + "${BASE_URL}/${path}" \ + -H 'Content-Type: application/json' \ + -d "$data" +} + +do_put() { + local path="${1?}" + local data="${2?}" + curl \ + -X PUT \ + --silent \ + "${BASE_URL}/${path}" \ + -H 'Content-Type: application/json' \ + -d "$data" +} + +do_get() { + local path="${1?}" + curl \ + -X GET \ + --silent \ + "${BASE_URL}/${path}" \ + -H 'Content-Type: application/json' +} + +do_delete() { + local path="${1?}" + curl \ + -X DELETE \ + --silent \ + "${BASE_URL}/${path}" \ + -H 'Content-Type: application/json' +} + +is_equal() { + local left="${1?}" + local right="${2?}" + diff <( printf '%s' "$left" ) <( printf "%s" "$right" ) \ + && return 0 + echo -e "is_equal failed\nleft: $left\nright: $right" >&2 + return 1 +} + +match_regex() { + local regex="${1?}" + local what="${2?}" + [[ "$what" =~ $regex ]] && return 0 + echo -e "match_regex failed\nregex: '$regex'\nwhat: $what" >&2 + return 1 +} + +json_has_equal() { + local key="${1?}" + local value="${2?}" + local data="${3?}" + local cur_value=$(echo "$data" | jq -r ".$key") \ + && is_equal "$cur_value" "$value" \ + && return 0 + echo -e "json_has_equal: key '$key' with value '$value' not found in \n$data" >&2 + return 1 +} + +json_has_match() { + local key="${1?}" + local match="${2?}" + local data="${3?}" + local cur_value=$(echo "$data" | jq -r ".$key") + match_regex "$match" "$cur_value" && return 0 + echo -e "json_has_match: key '$key' value '$cur_value' does not match '$match'" >&2 + return 1 +} + +json_get() { + local key="${1?}" + jq -r ".$key" +} \ No newline at end of file diff --git a/api-tests/test_expenses.bats b/api-tests/test_expenses.bats new file mode 100644 index 0000000..682c9ad --- /dev/null +++ b/api-tests/test_expenses.bats @@ -0,0 +1,114 @@ +#!/usr/bin/env bats + +# per whole file +setup_file() { + curdir="${BATS_TEST_FILENAME%/*}" + db_host="127.0.0.1" + test_db="expenses" + test_db_pass="dummypass" + test_db_user="expenses" + mysql="mysql \ + --host=$db_host \ + --port=3306 \ + --protocol=tcp \ + --user=$test_db_user \ + --password=$test_db_pass" + + $mysql -e "drop database if exists $test_db" + $mysql -e "create database $test_db" + $mysql "$test_db" < $curdir/../db/schema.sql + $mysql -e "insert into accounts values (0, 'Test account 0'),(1, 'Test account 1')" "$test_db" + $mysql -e "select * from expenses;" "$test_db" +} + + +# per test +setup() { + load helpers +} + +@test "I can get the expenses (empty database)" { + run do_get "api/expenses" + + [[ "$status" == "0" ]] + is_equal \ + '[]' \ + "$output" +} + +@test "I can create a new expense" { + new_expense='{ + "currency": "EUR", + "amount": "42.0", + "category": "eating-out", + "description": "Some fake expense number 1", + "accountId": 1, + "timestamp": "'"$(date +%Y-%m-%dT%H:%M:%SZ)"'" + }' + run do_post "api/expenses" "$new_expense" + + [[ "$status" == "0" ]] + json_has_match 'amount' '42.0' "$output" + json_has_match 'category' 'eating-out' "$output" + json_has_match 'description' 'Some fake expense number 1' "$output" + json_has_match 'account.id' '1' "$output" +} + +@test "I can update an expense" { + new_expense='{ + "currency": "EUR", + "amount": "42.0", + "category": "eating-out", + "description": "Some fake expense number 1", + "accountId": 1, + "timestamp": "'"$(date +%Y-%m-%dT%H:%M:%SZ)"'" + }' + run do_post "api/expenses" "$new_expense" + + [[ "$status" == "0" ]] + json_has_match 'amount' '42.0' "$output" + + new_expense_id="$(echo "$output" | json_get "id")" + modified_expense='{ + "currency": "EUR", + "amount": "84.0", + "category": "eating-out", + "description": "Some fake expense number 1", + "accountId": 1, + "timestamp": "'"$(date +%Y-%m-%dT%H:%M:%SZ)"'" + }' + run do_put "api/expenses/$new_expense_id" "$modified_expense" + + [[ "$status" == "0" ]] + json_has_match "amount" '84.0' "$output" + + run do_get "api/expenses" + + json_has_match "[] | select( .id == $new_expense_id) | .amount" '84.0' "$output" +} + +@test "I can delete an expense" { + new_expense='{ + "currency": "EUR", + "amount": "42.0", + "category": "eating-out", + "description": "Some fake expense number 1", + "accountId": 1, + "timestamp": "'"$(date +%Y-%m-%dT%H:%M:%SZ)"'" + }' + run do_post "api/expenses" "$new_expense" + + [[ "$status" == "0" ]] + json_has_match 'amount' '42.0' "$output" + + new_expense_id="$(echo "$output" | json_get "id")" + + run do_delete "api/expenses/$new_expense_id" + + [[ "$status" == "0" ]] + [[ "$output" == "$new_expense_id" ]] + + run do_get "api/expenses" + + json_has_match "[] | select( .id == $new_expense_id)" '' "$output" +} \ No newline at end of file diff --git a/db/fake_data.sql b/db/fake_data.sql index 16e6573..d17051b 100644 --- a/db/fake_data.sql +++ b/db/fake_data.sql @@ -45,16 +45,6 @@ INSERT INTO `expenses` VALUES (2,29.90,'EUR','2021-08-14 00:00:00','eating-out', /*!40000 ALTER TABLE `expenses` ENABLE KEYS */; UNLOCK TABLES; --- --- Dumping data for table `hibernate_sequence` --- - -LOCK TABLES `hibernate_sequence` WRITE; -/*!40000 ALTER TABLE `hibernate_sequence` DISABLE KEYS */; -INSERT INTO `hibernate_sequence` VALUES (1); -/*!40000 ALTER TABLE `hibernate_sequence` ENABLE KEYS */; -UNLOCK TABLES; - /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/db/schema.sql b/db/schema.sql index 5e747ff..ca6400f 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -61,6 +61,7 @@ DROP TABLE IF EXISTS `expense_sequence`; CREATE TABLE `expense_sequence` ( `next_val` bigint(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +INSERT INTO `expense_sequence` VALUES (1); /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -108,18 +109,6 @@ CREATE TABLE `expenses_in_review` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Table structure for table `hibernate_sequence` --- - -DROP TABLE IF EXISTS `hibernate_sequence`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `hibernate_sequence` ( - `next_val` bigint(20) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `user` -- diff --git a/scripts/start_dev.sh b/scripts/start_dev.sh index f1e145d..dba310d 100755 --- a/scripts/start_dev.sh +++ b/scripts/start_dev.sh @@ -24,12 +24,17 @@ check_if_backend_alive() { main() { mkdir -p .gradle $DOCKER build -f Dockerfile.dev -t expenses-server:dev - $DOCKER rm -f expenses-server-dev || : - db_host=$(hostname -i | grep -Po '192.168.1.\w*(?:$| )') + $DOCKER rm -f expenses-server-dev || : 2>/dev/null + db_host="$(hostname -i | grep -Po '192.168.1.\w*(?:$| )' || :)" + do_tail=yes if [[ $db_host == "" ]]; then # fallback in case we run on ci db_host=$(hostname -i | awk '{print $1}') fi + if [[ "${1:-}" == "notail" ]]; then + do_tail=no + shift + fi $DOCKER run \ --tty \ --interactive \ @@ -60,7 +65,9 @@ main() { echo "Checking again in 5s..." sleep 5 done - $DOCKER logs -f expenses-server-dev + if [[ "$do_tail" == "yes" ]]; then + $DOCKER logs -f expenses-server-dev + fi } main "$@" \ No newline at end of file diff --git a/scripts/start_devdb.sh b/scripts/start_devdb.sh index afea86a..891fa9a 100755 --- a/scripts/start_devdb.sh +++ b/scripts/start_devdb.sh @@ -27,7 +27,7 @@ check_if_db_alive() { start_mariadb() { local name="expenses_devdb" - $DOCKER rm -f "$name" || : + $DOCKER rm -f "$name" || : 2>/dev/null $DOCKER run \ --name="$name" \ --detach \