From 624bdbf0c277cd216cb2d0ce9ae008e586abf12d Mon Sep 17 00:00:00 2001 From: Timothy Messier Date: Wed, 11 Aug 2021 13:25:32 -0400 Subject: [PATCH] test(db): Add baseline tests for wh_user_dimension (#1454) This sets up the configuration for running a new suite of tests that are written in sql, to test the sql logic implemented in plpgsql functions and triggers. It is primarily designed to provides tests for the data warehouse functionality that is implemented purely in sql. The infrastructure to run these tests leverages docker. It creates one docker container using the `library/postgres` image and provisions the database with the boundary sql migrations and test helper functions. A separate docker container is run that has the pgtap extension and pg_prove cli to execute the tests. As noted, pgtap is used for the tests. This postgresql extension provides a number of helpful assertion functions to make writing sql tests easier. It also, along with the pg_prove cli, provides readable test output. Along with the new testing infrastructure this commit establishes a baseline of tests for the wh_user_dimension: - Unit tests for individual views and functions: - whx_user_dimension_source - whx_user_dimension_target - wh_upsert_user - Integration tests for that verify that the wh_user_dimension gets populated correctly when sessions are inserted or updated. This ensures that: - the trigger runs on session insert - the trigger results in the correct changes to the wh_user_dimension. This commit provides a `Makefile` to run the tests locally that provides support for: - Setting the postgres docker image to easily test multiple versions. - Passing through options to pg_prove - Passing the test list to run individual tests - Additional targets to separately startup the database image to quickly re-run tests while developing new tests. Lastly configures circleci to run this test suite as a matrix build in parallel with the other tests. The matrix build tests multiple versions of postgres: - latest - 13-alpine - 12-alpine - 11-alpine See: https://pgtap.org/ --- .circleci/config.yml | 44 +++ .circleci/config/jobs/test-sql.yml | 12 + .circleci/config/workflows/default.yml | 4 + Makefile | 3 + internal/db/sqltest/Makefile | 81 ++++++ internal/db/sqltest/README.md | 73 +++++ internal/db/sqltest/initdb.d/00_schema.sh | 33 +++ internal/db/sqltest/initdb.d/01_wtt_load.sql | 30 ++ .../db/sqltest/initdb.d/02_colors_persona.sql | 216 +++++++++++++++ .../sqltest/initdb.d/03_widgets_persona.sql | 261 ++++++++++++++++++ internal/db/sqltest/tests/setup/wtt_load.sql | 18 ++ .../sqltest/tests/setup/wtt_load_colors.sql | 7 + .../sqltest/tests/setup/wtt_load_widgets.sql | 8 + .../sqltest/tests/wh/upsert_user/errors.sql | 18 ++ .../sqltest/tests/wh/upsert_user/insert.sql | 17 ++ .../sqltest/tests/wh/upsert_user/upsert.sql | 37 +++ .../user_dimension/oidc_auth_new_session.sql | 39 +++ .../password_auth_description_change.sql | 76 +++++ .../password_auth_new_session.sql | 39 +++ .../session_multiple_sessions.sql | 44 +++ ...ssion_multiple_sessions_different_auth.sql | 55 ++++ .../wh/user_dimension/session_update.sql | 28 ++ .../tests/wh/user_dimension_views/source.sql | 49 ++++ .../tests/wh/user_dimension_views/target.sql | 46 +++ 24 files changed, 1238 insertions(+) create mode 100644 .circleci/config/jobs/test-sql.yml create mode 100644 internal/db/sqltest/Makefile create mode 100644 internal/db/sqltest/README.md create mode 100755 internal/db/sqltest/initdb.d/00_schema.sh create mode 100644 internal/db/sqltest/initdb.d/01_wtt_load.sql create mode 100644 internal/db/sqltest/initdb.d/02_colors_persona.sql create mode 100644 internal/db/sqltest/initdb.d/03_widgets_persona.sql create mode 100644 internal/db/sqltest/tests/setup/wtt_load.sql create mode 100644 internal/db/sqltest/tests/setup/wtt_load_colors.sql create mode 100644 internal/db/sqltest/tests/setup/wtt_load_widgets.sql create mode 100644 internal/db/sqltest/tests/wh/upsert_user/errors.sql create mode 100644 internal/db/sqltest/tests/wh/upsert_user/insert.sql create mode 100644 internal/db/sqltest/tests/wh/upsert_user/upsert.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension/oidc_auth_new_session.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension/password_auth_description_change.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension/password_auth_new_session.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension/session_multiple_sessions.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension/session_multiple_sessions_different_auth.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension/session_update.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension_views/source.sql create mode 100644 internal/db/sqltest/tests/wh/user_dimension_views/target.sql 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