diff --git a/.circleci/config.yml b/.circleci/config.yml index 4cc0a8436..353281617 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,6 +5,16 @@ # Orb 'circleci/slack@3.4.2' resolved to 'circleci/slack@3.4.2' version: 2 jobs: + test-sql-13-alpine: + machine: + image: ubuntu-1604:201903-01 + steps: + - checkout + - run: + command: | + make test-sql POSTGRES_DOCKER_IMAGE_BASE=docker.mirror.hashicorp.services/postgres PG_DOCKER_TAG=13-alpine + name: Run SQL PgTap Tests + working_directory: ~/boundary bundle-releases: docker: - image: docker.mirror.hashicorp.services/circleci/buildpack-deps @@ -237,6 +247,16 @@ jobs: name: Save package cache paths: - .buildcache/packages/store + test-sql-11-alpine: + machine: + image: ubuntu-1604:201903-01 + steps: + - checkout + - run: + command: | + make test-sql POSTGRES_DOCKER_IMAGE_BASE=docker.mirror.hashicorp.services/postgres PG_DOCKER_TAG=11-alpine + name: Run SQL PgTap Tests + working_directory: ~/boundary build-common-layers: machine: image: ubuntu-1604:202007-01 @@ -865,6 +885,16 @@ jobs: name: Save package cache paths: - .buildcache/packages/store + test-sql-12-alpine: + machine: + image: ubuntu-1604:201903-01 + steps: + - checkout + - run: + command: | + make test-sql POSTGRES_DOCKER_IMAGE_BASE=docker.mirror.hashicorp.services/postgres PG_DOCKER_TAG=12-alpine + name: Run SQL PgTap Tests + working_directory: ~/boundary solaris_amd64_package: docker: - image: docker.mirror.hashicorp.services/circleci/buildpack-deps @@ -1435,6 +1465,16 @@ jobs: name: Save package cache paths: - .buildcache/packages/store + test-sql-latest: + machine: + image: ubuntu-1604:201903-01 + steps: + - checkout + - run: + command: | + make test-sql POSTGRES_DOCKER_IMAGE_BASE=docker.mirror.hashicorp.services/postgres PG_DOCKER_TAG=latest + name: Run SQL PgTap Tests + working_directory: ~/boundary workflows: build-7cb7957a6e75af58: jobs: @@ -1504,6 +1544,10 @@ workflows: default: jobs: - build + - test-sql-latest + - test-sql-11-alpine + - test-sql-12-alpine + - test-sql-13-alpine - algolia-index: filters: branches: diff --git a/.circleci/config/jobs/test-sql.yml b/.circleci/config/jobs/test-sql.yml new file mode 100644 index 000000000..c651d2d5e --- /dev/null +++ b/.circleci/config/jobs/test-sql.yml @@ -0,0 +1,12 @@ +machine: + image: 'ubuntu-1604:201903-01' +working_directory: ~/boundary +parameters: + postgres-version: + type: string +steps: +- checkout +- run: + name: "Run SQL PgTap Tests" + command: | + make test-sql POSTGRES_DOCKER_IMAGE_BASE=docker.mirror.hashicorp.services/postgres PG_DOCKER_TAG=<< parameters.postgres-version >> diff --git a/.circleci/config/workflows/default.yml b/.circleci/config/workflows/default.yml index 94fdf32f4..2f893263f 100644 --- a/.circleci/config/workflows/default.yml +++ b/.circleci/config/workflows/default.yml @@ -1,5 +1,9 @@ jobs: - build + - test-sql: + matrix: + parameters: + postgres-version: ["latest", "11-alpine", "12-alpine", "13-alpine"] - algolia-index: filters: branches: diff --git a/Makefile b/Makefile index eb7c9b50e..efa984273 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,9 @@ website-start: test-ci: install-go ~/.go/bin/go test ./... -v $(TESTARGS) -timeout 120m +test-sql: + $(MAKE) -C internal/db/sqltest/ test + test: ~/.go/bin/go test ./... -timeout 30m diff --git a/internal/db/sqltest/Makefile b/internal/db/sqltest/Makefile new file mode 100644 index 000000000..5305a6bb4 --- /dev/null +++ b/internal/db/sqltest/Makefile @@ -0,0 +1,81 @@ +all: test + +CWD := $(shell pwd) + +# Version of postgres docker image for test database +PG_DOCKER_TAG ?= 13-alpine +# Version of pg_tap docker image +PG_TAP_DOCKER_TAG ?= pg13 + +# Pass through options to pg_prove +# See: https://pgtap.org/pg_prove.html +PROVE_OPTS ?= + +TESTS ?= tests/setup/*.sql \ + tests/wh/*/*.sql + +POSTGRES_DOCKER_IMAGE_BASE ?= postgres + +POSTGRES_DOCKER_IMAGE := $(POSTGRES_DOCKER_IMAGE_BASE):$(PG_DOCKER_TAG) + +PG_TAP_DOCKER_IMAGE_BASE ?= subzerocloud/pgtap +PG_TAP_DOCKER_IMAGE := $(PG_TAP_DOCKER_IMAGE_BASE):$(PG_TAP_DOCKER_TAG) + + +# re-write paths for docker +dockerized_tests = $(patsubst tests/%,/test/%,$(TESTS)) + +test: + @echo Using $(POSTGRES_DOCKER_IMAGE) + @echo Using $(PG_TAP_DOCKER_IMAGE) + @docker run -d \ + --name boundary-sql-tests \ + -e POSTGRES_PASSWORD=boundary \ + -e POSTGRES_USER=boundary \ + -e POSTGRES_DB=boundary \ + -v "$(CWD)/../schema/migrations/postgres":/migrations \ + -v "$(CWD)/initdb.d":/docker-entrypoint-initdb.d/ \ + $(POSTGRES_DOCKER_IMAGE) + @docker run -it --rm \ + --name test \ + --link boundary-sql-tests:db \ + -e DATABASE=boundary \ + -e HOST=db \ + -e PORT=5432 \ + -e USER=boundary \ + -e PASSWORD=boundary \ + -e TESTS="$(PROVE_OPTS) $(dockerized_tests)" \ + -v "$(CWD)/tests":/test \ + $(PG_TAP_DOCKER_IMAGE); \ + (ret=$$?; docker stop boundary-sql-tests &>/dev/null && docker rm -v boundary-sql-tests &>/dev/null && exit $$ret) + +database-up: + @echo Using $(POSTGRES_DOCKER_IMAGE) + @docker run -d \ + --name boundary-sql-tests \ + -e POSTGRES_PASSWORD=boundary \ + -e POSTGRES_USER=boundary \ + -e POSTGRES_DB=boundary \ + -v "$(CWD)/../schema/migrations/postgres":/migrations \ + -v "$(CWD)/initdb.d":/docker-entrypoint-initdb.d/ \ + $(POSTGRES_DOCKER_IMAGE) + +run-tests: + @echo Using $(PG_TAP_DOCKER_IMAGE) + @docker run -it --rm \ + --name test \ + --link boundary-sql-tests:db \ + -e DATABASE=boundary \ + -e HOST=db \ + -e PORT=5432 \ + -e USER=boundary \ + -e PASSWORD=boundary \ + -e TESTS="$(PROVE_OPTS) $(dockerized_tests)" \ + -v "$(CWD)/tests":/test \ + $(PG_TAP_DOCKER_IMAGE) + +clean: + docker stop boundary-sql-tests || true + docker rm -v boundary-sql-tests || true + +.PHONY: all clean test database-up run-tests diff --git a/internal/db/sqltest/README.md b/internal/db/sqltest/README.md new file mode 100644 index 000000000..0c6a99422 --- /dev/null +++ b/internal/db/sqltest/README.md @@ -0,0 +1,73 @@ +# SQL Tests + +This test suite is used to test behavior of the database logic. +In particular the data warehouse implementation is +completely in sql via plpgsql, functions, and triggers. +This test suite is also implemented directly in sql. + +## Organization + +- `initdb.d`: contains init scripts/sql that is run on the test database + when it starts. It ensures the sql migrations are run and creates test helper + functions. +- `tests`: contains the tests. Each file can contain only a single test that + runs in a transaction so it can rollback. + +The tests leverage the +[pgTap](https://pgtap.org/documentation.html) +postgres extension to make assertions and provide +more readable test output. + +## Usage + +To run the test run `make` or `make test`. This will: + +- Start a docker `postgres` container and initialize it. +- Start a docker `pgtap` container to execute the tests. + +When writing new tests +it can be faster to keep the database up +and just re-run the tests. +This can be done by running: + +```bash +# starts database docker image in the background +make database-up + +# runs the tests, call this multiple times while implementing new tests. +make run-tests + +# to clean up +make clean +``` + +You can also run individual tests: + +```bash +# run a single test file +make TEST=tests/setup/wtt_load.sql + +# run a single test file with the database already created. +make run-tests TEST=tests/setup/wtt_load.sql + +# run a directory of tests +make TEST=tests/setup/*.sql +``` + +You can pass through options to `pg_prove`. +See [the docs](https://pgtap.org/pg_prove.html) +for available options, i.e: + +```bash +# run tests in parallel with verbose output +make PROVE_OPTS='-j9 -v' +``` + +Different versions of postgres can easily be tested: + +```bash +make PG_DOCKER_TAG=latest +make PG_DOCKER_TAG=13-alpine +make PG_DOCKER_TAG=12-alpine +make PG_DOCKER_TAG=11-alpine +``` diff --git a/internal/db/sqltest/initdb.d/00_schema.sh b/internal/db/sqltest/initdb.d/00_schema.sh new file mode 100755 index 000000000..b1cc3fdb7 --- /dev/null +++ b/internal/db/sqltest/initdb.d/00_schema.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# This script is designed to run as an init script in a postgres docker container. +# The docker-entrypoint.sh script does not traverse a nested directory +# structue to load sql files at container start. However, it does allow for +# running .sh files. This script should run first to execute the boundary sql +# migration files in the correct order. +# +# See Initialization Scripts https://hub.docker.com/_/postgres +set -e +shopt -s globstar + +## Taken from postgres docker image's docker-entrypoint.sh +## See: https://github.com/docker-library/postgres/blob/517c64f87e6661366b415df3f2273c76cea428b0/docker-entrypoint.sh#L175-L187 +# Execute sql script, passed via stdin (or -f flag of pqsl) +# usage: docker_process_sql [psql-cli-args] +# ie: docker_process_sql --dbname=mydb <<<'INSERT ...' +# ie: docker_process_sql -f my-file.sql +# ie: docker_process_sql