Repository: shuttle-hq/synth (rust)

Issue:
Improvement: align CI with test harnesses
**Details**
In #231 the CI is made to use the same script as the local testing harness for postgres integration. The idea is to keep to two from becoming out of synth and to make it easy to perform local e2e testing.

The same alignment should happen for:
- [x] mysql (`synth/testing_harness/mysql`) (#246)
- [x] mongo (`synth/testing_harness/mongo`) (#246)
- [ ] sqlite? #185 


Discussion:

Patch:
diff --git a/.github/workflows/scripts/validate_mysql_gen_count.sh b/.github/workflows/scripts/validate_mysql_gen_count.sh
deleted file mode 100644
--- a/.github/workflows/scripts/validate_mysql_gen_count.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-sum_rows_query="SELECT (SELECT count(*) FROM hospitals) +  (SELECT count(*) FROM doctors) + (SELECT count(*) FROM patients)"
-sum=`mysql -h $MYSQL_HOST -u root --password=$MYSQL_ROOT_PASSWORD -P $MYSQL_PORT $MYSQL_DATABASE -e "$sum_rows_query" | grep -o '[[:digit:]]*'`
-if [ "$sum" -lt "10" ]
-then
-    exit 1
-fi
\ No newline at end of file
diff --git a/.github/workflows/synth-mongo.yml b/.github/workflows/synth-mongo.yml
--- a/.github/workflows/synth-mongo.yml
+++ b/.github/workflows/synth-mongo.yml
@@ -3,10 +3,10 @@ name: synth-mongo
 on:
   push:
     branches: [ master ]
-    paths: [ '**/*.rs' ]
+    paths: [ "**/*.rs", "synth/testing_harness/mongodb/**" ]
   pull_request:
     branches: [ master ]
-    paths: [ '**/*.rs' ]
+    paths: [ "**/*.rs", "synth/testing_harness/mongodb/**" ]
 
   workflow_dispatch:
 
@@ -16,42 +16,17 @@ env:
 jobs:
   e2e_tests_mongo:
     runs-on: ubuntu-latest
+    env:
+      PORT: 27017
     steps:
       - uses: actions/checkout@v2
       - run: |
-          docker run -p 27017:27017 --name mongo-test -d mongo:latest
+          docker run -p $PORT:27017 --name mongo-synth-harness -d mongo:latest
       - uses: actions-rs/toolchain@v1
         with:
           toolchain: nightly
       - run: cargo +nightly install --debug --path=synth
-      - run: |
-          echo "Running generate test"
-          cd synth/testing_harness/mongodb/synth
-          synth generate hospital --size 100 --to mongodb://localhost:27017/hospital
-          cd ..
-          COLLECTIONS=(hospitals doctors patients)
-          for collection in "${COLLECTIONS[@]}"
-          do
-            docker exec -i mongo-test mongoexport \
-                --db hospital \
-                --collection "$collection" \
-                --forceTableScan \
-                --jsonArray \
-                | jq 'del(.[]._id)' \
-                | diff -y - "hospital_master_data/$collection.json" || exit 1
-          done
-      - run: |
-          echo "Testing import"
-          docker container restart mongo-test
-          cd synth/testing_harness/mongodb/
-          for collection in "${COLLECTIONS[@]}"
-          do
-            cat "hospital_master_data/$collection.json" \
-            | docker exec -i mongo-test mongoimport \
-            --db hospital \
-            --collection "$collection" \
-            --jsonArray
-          done
-          cd synth
-          synth import --from mongodb://localhost:27017/hospital hospital_temp
-          diff <(jq --sort-keys . hospital_temp/*) <(jq --sort-keys . hospital_master/*)
+      - run: ./e2e.sh test-generate
+        working-directory: synth/testing_harness/mongodb
+      - run: ./e2e.sh test-import
+        working-directory: synth/testing_harness/mongodb
diff --git a/.github/workflows/synth-mysql.yml b/.github/workflows/synth-mysql.yml
--- a/.github/workflows/synth-mysql.yml
+++ b/.github/workflows/synth-mysql.yml
@@ -3,10 +3,10 @@ name: synth-mysql
 on:
   push:
     branches: [ master ]
-    paths: [ '**/*.rs' ]
+    paths: [ "**/*.rs", "synth/testing_harness/mysql/**" ]
   pull_request:
     branches: [ master ]
-    paths: [ '**/*.rs' ]
+    paths: [ "**/*.rs", "synth/testing_harness/mysql/**" ]
 
   workflow_dispatch:
 
@@ -25,6 +25,7 @@ jobs:
         ports:
           - 3306:3306
         options: >-
+          --name mysql-synth-harness
           --health-cmd="mysqladmin ping"
           --health-interval=10s
           --health-timeout=5s
@@ -37,6 +38,7 @@ jobs:
         ports:
           - 3307:3306
         options: >-
+          --name mariadb-synth-harness
           --health-cmd="mysqladmin ping"
           --health-interval=10s
           --health-timeout=5s
@@ -52,37 +54,16 @@ jobs:
       - uses: actions/checkout@v2
       ## Set env. variables for this job
       - run: |
-          echo "MYSQL_HOST=127.0.0.1" >> $GITHUB_ENV
-          echo "MYSQL_USER=root" >> $GITHUB_ENV
-          echo "MYSQL_PORT=${{ matrix.port }}" >> $GITHUB_ENV
-          echo "MYSQL_ROOT_PASSWORD=mysecretpassword" >> $GITHUB_ENV
-          echo "MYSQL_DATABASE=test_db" >> $GITHUB_ENV
-          echo "DB_SCHEME=${{ matrix.db }}" >> $GITHUB_ENV
+          echo "PORT=${{ matrix.port }}" >> $GITHUB_ENV
+          echo "SCHEME=${{ matrix.db }}" >> $GITHUB_ENV
       - run: |
           sudo apt-get update
-          sudo apt-get install --yes --no-install-recommends mysql-client jq
-      - run: >
-          mysql -h "${{ env.MYSQL_HOST }}" -u "${{ env.MYSQL_USER }}" --password="${{ env.MYSQL_ROOT_PASSWORD }}"
-          -P "${{ env.MYSQL_PORT }}" "${{ env.MYSQL_DATABASE }}" < synth/testing_harness/mysql/0_hospital_schema.sql
+          sudo apt-get install --yes --no-install-recommends jq
       - uses: actions-rs/toolchain@v1
         with:
           toolchain: nightly
       - run: cargo +nightly install --debug --path=synth
-      - run: |
-          echo "Running generate test"
-          cd synth/testing_harness/mysql
-          synth generate hospital_master --to ${{ env.DB_SCHEME }}://${{ env.MYSQL_USER }}:${{ env.MYSQL_ROOT_PASSWORD }}@${{ env.MYSQL_HOST }}:${{ env.MYSQL_PORT }}/${{ env.MYSQL_DATABASE }} --size 10
-          bash "${GITHUB_WORKSPACE}/.github/workflows/scripts/validate_mysql_gen_count.sh"
-      ## Clear out and repopulate DB
-      - run: >
-          mysql -h "${{ env.MYSQL_HOST }}" -u "${{ env.MYSQL_USER }}" --password="${{ env.MYSQL_ROOT_PASSWORD }}"
-          -P "${{ env.MYSQL_PORT }}" "${{ env.MYSQL_DATABASE }}" < synth/testing_harness/mysql/0_hospital_schema.sql
-      - run: >
-          mysql -h "${{ env.MYSQL_HOST }}" -u "${{ env.MYSQL_USER }}" --password="${{ env.MYSQL_ROOT_PASSWORD }}"
-          -P "${{ env.MYSQL_PORT }}" "${{ env.MYSQL_DATABASE }}" < synth/testing_harness/mysql/1_hospital_data.sql
-      - run: |
-          echo "Testing import"
-          cd synth/testing_harness/mysql
-          synth init || true
-          synth import --from ${{ env.DB_SCHEME }}://${{ env.MYSQL_USER }}:${{ env.MYSQL_ROOT_PASSWORD }}@${{ env.MYSQL_HOST }}:${{ env.MYSQL_PORT }}/${{ env.MYSQL_DATABASE }} hospital_import
-          diff <(jq --sort-keys . hospital_import/*) <(jq --sort-keys . hospital_master/*)
+      - run: ./e2e.sh test-generate
+        working-directory: synth/testing_harness/mysql
+      - run: ./e2e.sh test-import
+        working-directory: synth/testing_harness/mysql
diff --git a/.github/workflows/synth-postgres.yml b/.github/workflows/synth-postgres.yml
--- a/.github/workflows/synth-postgres.yml
+++ b/.github/workflows/synth-postgres.yml
@@ -3,10 +3,10 @@ name: synth-postgres
 on:
   push:
     branches: [master]
-    paths: ["**/*.rs", "synth/testing_harness/postgres/**"]
+    paths: [ "**/*.rs", "synth/testing_harness/postgres/**" ]
   pull_request:
     branches: [master]
-    paths: ["**/*.rs", "synth/testing_harness/postgres/**"]
+    paths: [ "**/*.rs", "synth/testing_harness/postgres/**" ]
 
   workflow_dispatch:
 
@@ -22,6 +22,7 @@ jobs:
         env:
           POSTGRES_PASSWORD: postgres
         options: >-
+          --name postgres-synth-harness
           -p 5432:5432
           --health-cmd pg_isready
           --health-interval 10s
@@ -34,13 +35,11 @@ jobs:
       - uses: actions/checkout@v2
       - run: |
           sudo apt-get update
-          sudo apt-get install --yes --no-install-recommends postgresql-client jq
+          sudo apt-get install --yes --no-install-recommends jq
       - uses: actions-rs/toolchain@v1
         with:
           toolchain: nightly
       - run: cargo +nightly install --debug --path=synth
-      - run: ./e2e.sh load-schema
-        working-directory: synth/testing_harness/postgres
       - run: ./e2e.sh test-generate
         working-directory: synth/testing_harness/postgres
       - run: ./e2e.sh test-import


Test patch:
diff --git a/synth/testing_harness/mongodb/.env b/synth/testing_harness/mongodb/.env
new file mode 100644
--- /dev/null
+++ b/synth/testing_harness/mongodb/.env
@@ -0,0 +1,2 @@
+PORT=${PORT:=27017}
+NAME=mongo-synth-harness
diff --git a/synth/testing_harness/mongodb/.gitignore b/synth/testing_harness/mongodb/.gitignore
new file mode 100644
--- /dev/null
+++ b/synth/testing_harness/mongodb/.gitignore
@@ -0,0 +1 @@
+hospital_import
diff --git a/synth/testing_harness/mongodb/README.md b/synth/testing_harness/mongodb/README.md
new file mode 100644
--- /dev/null
+++ b/synth/testing_harness/mongodb/README.md
@@ -0,0 +1,15 @@
+Integration Tests for Mongodb
+====================================
+
+This is an integration test that validates the synth generate and synth import commands for Mongodb on a Debian flavored 
+OS. The models in hospital_master are used as a known "golden" set to validate against. The *.json files in
+hospital_data generate the schema and test data within the database.
+
+# Requirements:
+- Docker
+- jq
+
+# Instructions
+
+To run this, execute `e2e.sh test-local` script from the current directory. A non-zero return code denotes failure.
+Note: run this with all dependencies installed in Vagrant with the [Vagrantfile](tools/vagrant/linux/ubuntu/Vagrantfile)
diff --git a/synth/testing_harness/mongodb/e2e.sh b/synth/testing_harness/mongodb/e2e.sh
--- a/synth/testing_harness/mongodb/e2e.sh
+++ b/synth/testing_harness/mongodb/e2e.sh
@@ -1,65 +1,142 @@
 #!/bin/bash
 
-# ------ Begin Setup ------
+set -uo pipefail
 
-# database harness information
-PORT=27017
-NAME=mongo-synth-harness
+# load env vars
+if [ -f .env ]
+then
+  set -a
+  . .env
+  set +a
+fi
 
-# shellcheck disable=SC2064
-trap " { docker kill '$NAME'; docker rm '$NAME'; } " EXIT
-
-# go to script directory
-cd "$( dirname "${BASH_SOURCE[0]}" )/synth" || exit 1
-# delete leftover config
-rm -f .synth/config.toml
-
-synth init || exit 1
-
-docker run -p $PORT:27017 --name $NAME -d mongo:latest || exit 1
-
-echo "Running database with container name $NAME on port $PORT"
-
-sleep 1
-
-# ------ End Setup ------
-
-# ------ Start Test Generate > Mongo ------
-echo "Running generation tests..."
-
-# Generate data into Mongo
-synth generate hospital --size 100 --to mongodb://localhost:27017/hospital || exit 1
-
-cd ..
+SYNTH="synth"
+[ "${CI-false}" == "true" ] || SYNTH="cargo run --bin synth"
 
 COLLECTIONS=(hospitals doctors patients)
 
-# Export collection and compare against golden master
-# We use jq for redacting the MongoDB OID.
-for collection in "${COLLECTIONS[@]}"
-do
-  docker exec -i "$NAME" mongoexport \
-      --db hospital \
-      --collection "$collection" \
-      --forceTableScan \
-      --jsonArray \
-      | jq 'del(.[]._id)' \
-      | diff - "hospital_master_data/$collection.json" || exit 1
-
-done
-
-# ------ End Test Generate > Mongo ------
-
-# ------ Start Test Import < Mongo ------
-echo "Running import tests..."
-
-cd synth || exit 1
-
-synth import --from mongodb://localhost:27017/hospital hospital_temp
-
-diff hospital_temp hospital_master
-
-rm -r hospital_temp || exit 1
-# ------ End Test Import < Mongo ------
-
-echo "Done..."
\ No newline at end of file
+ERROR='\033[0;31m'
+INFO='\033[0;36m'
+DEBUG='\033[0;37m'
+NC='\033[0m' # No Color
+
+function help() {
+  echo "$1 <command>
+
+commands:
+  test-generate|Test generating data to mongodb
+  test-import|Test importing from mongodb data
+  test-local|Run all test on a local machine using the container from 'up' (no need to call 'up' first)
+  up|Starts a local Docker instance for testing
+  down|Stops container started with 'up'
+  cleanup|Cleanup local files after a test run
+" | column -t -L -s"|"
+}
+
+function test-generate() {
+  echo -e "${INFO}Test generate${NC}"
+
+  for collection in "${COLLECTIONS[@]}"
+  do
+    docker exec -i $NAME mongo \
+        hospital \
+        --eval "db.${collection}.drop()" > /dev/null
+  done
+
+  $SYNTH generate hospital --to mongodb://localhost:${PORT}/hospital --size 100 || return 1
+
+  for collection in "${COLLECTIONS[@]}"
+  do
+    docker exec -i $NAME mongoexport \
+        --quiet \
+        --db hospital \
+        --collection "$collection" \
+        --forceTableScan \
+        --jsonArray \
+        | jq 'del(.[]._id)' \
+        | diff - "hospital_data/$collection.json" || { echo -e "${ERROR}Generation '$collection' does not match${NC}"; return 1; }
+  done
+}
+
+function test-import() {
+  echo -e "${INFO}Testing import${NC}"
+
+  for collection in "${COLLECTIONS[@]}"
+  do
+    cat "hospital_data/$collection.json" \
+    | docker exec -i $NAME mongoimport \
+        --quiet \
+        --db hospital \
+        --collection "$collection" \
+        --jsonArray
+  done
+
+  $SYNTH import --from mongodb://localhost:${PORT}/hospital hospital_import || { echo -e "${ERROR}Import failed${NC}"; return 1; }
+  diff <(jq --sort-keys . hospital_import/*) <(jq --sort-keys . hospital_master/*) || { echo -e "${ERROR}Import namespaces do not match${NC}"; return 1; }
+}
+
+function test-local() {
+  up || return 1
+
+  result=0
+  test-generate || result=$?
+  test-import || result=$?
+
+  down
+  cleanup
+
+  echo -e "${DEBUG}Done${NC}"
+
+  return $result
+}
+
+function up() {
+  echo -e "${DEBUG}Starting container${NC}"
+  echo -e "${DEBUG}Running database with container name $NAME on port $PORT${NC}"
+  docker run --rm --name $NAME -p $PORT:27017 -d mongo > /dev/null
+
+  wait_count=0
+  while [ $(docker logs $NAME 2>&1 | grep -c "Waiting for connections") -lt 1 ]
+  do
+    range=$(printf "%${wait_count}s")
+    echo -en "\\r${DEBUG}Waiting for DB to come up${range// /.}${NC}"
+    wait_count=$((wait_count + 1))
+    sleep 1
+  done
+  echo
+}
+
+function down() {
+  echo -e "${DEBUG}Stopping container${NC}"
+  docker stop $NAME > /dev/null
+}
+
+function cleanup() {
+  echo -e "${DEBUG}Cleaning up local files${NC}"
+  rm -Rf hospital_import
+  rm -Rf .synth
+}
+
+case "${1-*}" in
+  test-generate)
+    test-generate || exit 1
+    ;;
+  test-import)
+    test-import || exit 1
+    ;;
+  test-local)
+    test-local || exit 1
+    ;;
+  up)
+    up
+    ;;
+  down)
+    down
+    ;;
+  cleanup)
+    cleanup
+    ;;
+  *)
+    help $0
+    exit 1
+esac
diff --git a/synth/testing_harness/mongodb/synth/hospital/doctors.json b/synth/testing_harness/mongodb/hospital/doctors.json
similarity index 100%
rename from synth/testing_harness/mongodb/synth/hospital/doctors.json
rename to synth/testing_harness/mongodb/hospital/doctors.json
diff --git a/synth/testing_harness/mongodb/synth/hospital/hospitals.json b/synth/testing_harness/mongodb/hospital/hospitals.json
similarity index 100%
rename from synth/testing_harness/mongodb/synth/hospital/hospitals.json
rename to synth/testing_harness/mongodb/hospital/hospitals.json
diff --git a/synth/testing_harness/mongodb/synth/hospital/patients.json b/synth/testing_harness/mongodb/hospital/patients.json
similarity index 100%
rename from synth/testing_harness/mongodb/synth/hospital/patients.json
rename to synth/testing_harness/mongodb/hospital/patients.json
diff --git a/synth/testing_harness/mongodb/hospital_master_data/doctors.json b/synth/testing_harness/mongodb/hospital_data/doctors.json
similarity index 100%
rename from synth/testing_harness/mongodb/hospital_master_data/doctors.json
rename to synth/testing_harness/mongodb/hospital_data/doctors.json
diff --git a/synth/testing_harness/mongodb/hospital_master_data/hospitals.json b/synth/testing_harness/mongodb/hospital_data/hospitals.json
similarity index 100%
rename from synth/testing_harness/mongodb/hospital_master_data/hospitals.json
rename to synth/testing_harness/mongodb/hospital_data/hospitals.json
diff --git a/synth/testing_harness/mongodb/hospital_master_data/patients.json b/synth/testing_harness/mongodb/hospital_data/patients.json
similarity index 100%
rename from synth/testing_harness/mongodb/hospital_master_data/patients.json
rename to synth/testing_harness/mongodb/hospital_data/patients.json
diff --git a/synth/testing_harness/mongodb/synth/hospital_master/doctors.json b/synth/testing_harness/mongodb/hospital_master/doctors.json
similarity index 100%
rename from synth/testing_harness/mongodb/synth/hospital_master/doctors.json
rename to synth/testing_harness/mongodb/hospital_master/doctors.json
diff --git a/synth/testing_harness/mongodb/synth/hospital_master/hospitals.json b/synth/testing_harness/mongodb/hospital_master/hospitals.json
similarity index 100%
rename from synth/testing_harness/mongodb/synth/hospital_master/hospitals.json
rename to synth/testing_harness/mongodb/hospital_master/hospitals.json
diff --git a/synth/testing_harness/mongodb/synth/hospital_master/patients.json b/synth/testing_harness/mongodb/hospital_master/patients.json
similarity index 100%
rename from synth/testing_harness/mongodb/synth/hospital_master/patients.json
rename to synth/testing_harness/mongodb/hospital_master/patients.json
diff --git a/synth/testing_harness/mysql/.env b/synth/testing_harness/mysql/.env
new file mode 100644
--- /dev/null
+++ b/synth/testing_harness/mysql/.env
@@ -0,0 +1,4 @@
+PORT=${PORT:=3306}
+PASSWORD="${PASSWORD:=mysecretpassword}"
+SCHEME="${SCHEME:=mysql}"
+NAME="$SCHEME-synth-harness"
diff --git a/synth/testing_harness/mysql/.gitignore b/synth/testing_harness/mysql/.gitignore
new file mode 100644
--- /dev/null
+++ b/synth/testing_harness/mysql/.gitignore
@@ -0,0 +1 @@
+hospital_import
diff --git a/synth/testing_harness/mysql/.synth/config.toml b/synth/testing_harness/mysql/.synth/config.toml
deleted file mode 100644
diff --git a/synth/testing_harness/mysql/README.md b/synth/testing_harness/mysql/README.md
--- a/synth/testing_harness/mysql/README.md
+++ b/synth/testing_harness/mysql/README.md
@@ -11,8 +11,7 @@ schema and test data within the database.
 
 # Instructions
 
-To run this, execute the `e2e.sh` script from the current directory. A non-zero return code denotes failure. 
+To run this, execute `e2e.sh test-local` script from the current directory. A non-zero return code denotes failure.
 Note: run this with all dependencies installed in Vagrant with the [Vagrantfile](tools/vagrant/linux/ubuntu/Vagrantfile)
 
-By default, the script will test MySql. To test MariaDb, set the environment variable `MARIA_DB_SCHEME=mariadb` before 
-running.
\ No newline at end of file
+By default, the script will test MySql. To test MariaDb, set the environment variable `SCHEME=mariadb` before running.
diff --git a/synth/testing_harness/mysql/e2e.sh b/synth/testing_harness/mysql/e2e.sh
old mode 100644
new mode 100755
--- a/synth/testing_harness/mysql/e2e.sh
+++ b/synth/testing_harness/mysql/e2e.sh
@@ -1,65 +1,124 @@
 #!/bin/bash
 
-DB_HOST=127.0.0.1
-DB_PORT=3306
-DB_USER=root
-DB_PASSWORD="mysecretpassword"
-DB_NAME=test_db
-DB_SCHEME="${MARIA_DB_SCHEME:=mysql}"
-CONTAINER_NAME=mysql-synth-harness
+set -uo pipefail
 
-######### Initialization #########
-
-# Install dependencies
-#apt-get install -y mysql-client
-
-cd "$( dirname "${BASH_SOURCE[0]}" )"
-
-# delete leftover config
-rm -f .synth/config.toml
-
-# 0. init workspace
-synth init || exit 1
-
-# 1. Init DB service
-container_id=$(docker run --name $CONTAINER_NAME -e MYSQL_ROOT_PASSWORD=$DB_PASSWORD -e MYSQL_DATABASE=$DB_NAME -p $DB_PORT:3306 -d $DB_SCHEME:latest)
+# load env vars
+if [ -f .env ]
+then
+  set -a
+  . .env
+  set +a
+fi
 
-# Waits til DB is ready
-while ! mysql -h $DB_HOST -u root --password=$DB_PASSWORD -P $DB_PORT $DB_NAME -e "SELECT 1" > /dev/null 2>&1; do
+SYNTH="synth"
+[ "${CI-false}" == "true" ] || SYNTH="cargo run --bin synth"
+
+ERROR='\033[0;31m'
+INFO='\033[0;36m'
+DEBUG='\033[0;37m'
+NC='\033[0m' # No Color
+
+function help() {
+  echo "$1 <command>
+
+commands:
+  load-schema [--no-data]|Fills DB with test schema - defaults to loading data too
+  test-generate|Test generating data to mysql
+  test-import|Test importing from mysql data
+  test-local|Run all test on a local machine using the container from 'up' (no need to call 'up' first)
+  up|Starts a local Docker instance for testing
+  down|Stops container started with 'up'
+  cleanup|Cleanup local files after a test run
+" | column -t -L -s"|"
+}
+
+function load-schema() {
+  docker exec -i $NAME mysql -h 127.0.0.1 -u root --password=$PASSWORD -P 3306 "test_db" < 0_hospital_schema.sql || return 1
+  [ ${1} == "--no-data" ] || docker exec -i $NAME mysql -h 127.0.0.1 -u root --password=$PASSWORD -P 3306 "test_db" < 1_hospital_data.sql || return 1
+}
+
+function test-generate() {
+  echo -e "${INFO}Test generate${NC}"
+  load-schema --no-data || { echo -e "${ERROR}Failed to load schema${NC}"; return 1; }
+  $SYNTH generate hospital_master --to $SCHEME://root:${PASSWORD}@127.0.0.1:${PORT}/test_db --size 30 || return 1
+
+  sum_rows_query="SELECT (SELECT count(*) FROM hospitals) +  (SELECT count(*) FROM doctors) + (SELECT count(*) FROM patients)"
+  sum=`docker exec -i $NAME mysql -h 127.0.0.1 -u root --password=$PASSWORD -P 3306 "test_db" -e "$sum_rows_query" | grep -o '[[:digit:]]*'`
+  [ "$sum" -gt "30" ] || { echo -e "${ERROR}Generation did not create more than 30 records${NC}"; return 1; }
+}
+
+function test-import() {
+  echo -e "${INFO}Testing import${NC}"
+  load-schema --all || { echo -e "${ERROR}Failed to load schema${NC}"; return 1; }
+  $SYNTH import --from $SCHEME://root:${PASSWORD}@127.0.0.1:${PORT}/test_db hospital_import || { echo -e "${ERROR}Import failed${NC}"; return 1; }
+  diff <(jq --sort-keys . hospital_import/*) <(jq --sort-keys . hospital_master/*) || { echo -e "${ERROR}Import namespaces do not match${NC}"; return 1; }
+}
+
+function test-local() {
+  up || return 1
+
+  result=0
+  test-generate || result=$?
+  test-import || result=$?
+
+  down
+  cleanup
+
+  echo -e "${DEBUG}Done${NC}"
+
+  return $result
+}
+
+function up() {
+  echo -e "${DEBUG}Starting container${NC}"
+  echo -e "${DEBUG}Running database with container name $NAME on port $PORT with password $PASSWORD${NC}"
+  docker run --rm --name $NAME -p $PORT:3306 -e MYSQL_ROOT_PASSWORD=$PASSWORD -e MYSQL_DATABASE="test_db" -d $SCHEME > /dev/null
+
+  wait_count=0
+  while ! docker exec -i $NAME mysql -h 127.0.0.1 -u root --password=$PASSWORD -P 3306 "test_db" -e "SELECT 1" > /dev/null 2>&1
+  do
+    range=$(printf "%${wait_count}s")
+    echo -en "\\r${DEBUG}Waiting for DB to come up${range// /.}${NC}"
+    wait_count=$((wait_count + 1))
     sleep 1
-done
-
-######### Export Test #########
-
-# 2. Populate DB schema
-mysql -h $DB_HOST -u root --password=$DB_PASSWORD -P $DB_PORT $DB_NAME < 0_hospital_schema.sql || exit 1
-
-result=0
-
-# 3. Verify gen to DB crates min. expected rows
-synth generate hospital_master --to $DB_SCHEME://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME --size 1 || result=1
-sum_rows_query="SELECT (SELECT count(*) FROM hospitals) +  (SELECT count(*) FROM doctors) + (SELECT count(*) FROM patients)"
-sum=`mysql -h $DB_HOST -u root --password=$DB_PASSWORD -P $DB_PORT $DB_NAME -e "$sum_rows_query" | grep -o '[[:digit:]]*'`
-[ "$sum" -gt "30" ] || result=1
-
-######### Import Test #########
-
-# 4. Clear out export data and insert known golden master data
-mysql -h $DB_HOST -u root --password=$DB_PASSWORD -P $DB_PORT $DB_NAME < 0_hospital_schema.sql || exit 1
-mysql -h $DB_HOST -u root --password=$DB_PASSWORD -P $DB_PORT $DB_NAME < 1_hospital_data.sql || exit 1
-
-# 5. Import with synth and diff
-synth import --from $DB_SCHEME://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME hospital_import || result=1
-diff <(jq --sort-keys . hospital_import/*) <(jq --sort-keys . hospital_master/*) || result=1
-
-######### Cleanup #########
-
-docker rm -f "${container_id}"
-rm -rf hospital_import
-rm -rf  .synth
-
-# fail if any of the commands failed
-if [ $result -ne 0 ]
-then
+  done
+  echo
+}
+
+function down() {
+  echo -e "${DEBUG}Stopping container${NC}"
+  docker stop $NAME > /dev/null
+}
+
+function cleanup() {
+  echo -e "${DEBUG}Cleaning up local files${NC}"
+  rm -Rf hospital_import
+  rm -Rf .synth
+}
+
+case "${1-*}" in
+  load-schema)
+    load-schema ${2---all} || exit 1
+    ;;
+  test-generate)
+    test-generate || exit 1
+    ;;
+  test-import)
+    test-import || exit 1
+    ;;
+  test-local)
+    test-local || exit 1
+    ;;
+  up)
+    up
+    ;;
+  down)
+    down
+    ;;
+  cleanup)
+    cleanup
+    ;;
+  *)
+    help $0
     exit 1
-fi
+esac
diff --git a/synth/testing_harness/postgres/.env b/synth/testing_harness/postgres/.env
--- a/synth/testing_harness/postgres/.env
+++ b/synth/testing_harness/postgres/.env
@@ -1,3 +1,3 @@
-PORT=${PORT:=5433}
+PORT=${PORT:=5432}
 PASSWORD="${PASSWORD:=mysecretpassword}"
 NAME=postgres-synth-harness
diff --git a/synth/testing_harness/postgres/Dockerfile b/synth/testing_harness/postgres/Dockerfile
deleted file mode 100644
--- a/synth/testing_harness/postgres/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM postgres:latest
-
-# Files copied to this subdirectory on the container will run in
-# alphabetical order
-COPY *.sql /docker-entrypoint-initdb.d/
-
diff --git a/synth/testing_harness/postgres/README.md b/synth/testing_harness/postgres/README.md
new file mode 100644
--- /dev/null
+++ b/synth/testing_harness/postgres/README.md
@@ -0,0 +1,15 @@
+Integration Tests for Postgres
+====================================
+
+This is an integration test that validates the synth generate and synth import commands for Postgres on a Debian flavored 
+OS. The models in hospital_master are used as a known "golden" set to validate against. The *.sql scripts generate the
+schema and test data within the database.
+
+# Requirements:
+- Docker
+- jq
+
+# Instructions
+
+To run this, execute `e2e.sh test-local` script from the current directory. A non-zero return code denotes failure.
+Note: run this with all dependencies installed in Vagrant with the [Vagrantfile](tools/vagrant/linux/ubuntu/Vagrantfile)
diff --git a/synth/testing_harness/postgres/e2e.sh b/synth/testing_harness/postgres/e2e.sh
--- a/synth/testing_harness/postgres/e2e.sh
+++ b/synth/testing_harness/postgres/e2e.sh
@@ -11,6 +11,7 @@ then
 fi
 
 SYNTH="synth"
+[ "${CI-false}" == "true" ] || SYNTH="cargo run --bin synth"
 
 ERROR='\033[0;31m'
 INFO='\033[0;36m'
@@ -21,9 +22,9 @@ function help() {
   echo "$1 <command>
 
 commands:
-  load-schema|Fills DB with test schema
-  test-generate|Test if 'synth generate' is correct
-  test-import|Test importing from postgres data created by 'load-schema'
+  load-schema [--no-data]|Fills DB with test schema - defaults to loading data too
+  test-generate|Test generating data to postgres
+  test-import|Test importing from postgres data
   test-warning|Test integer warnings
   test-local|Run all test on a local machine using the container from 'up' (no need to call 'up' first)
   up|Starts a local Docker instance for testing
@@ -33,25 +34,30 @@ commands:
 }
 
 function load-schema() {
-  psql -f 0_hospital_schema.sql postgres://postgres:$PASSWORD@localhost:$PORT/postgres || return 1
-  psql -f 1_hospital_data.sql postgres://postgres:$PASSWORD@localhost:$PORT/postgres || return 1
+  docker exec -i $NAME psql -q postgres://postgres:$PASSWORD@localhost:5432/postgres < 0_hospital_schema.sql || return 1
+  [ ${1} == "--no-data" ] || docker exec -i $NAME psql -q postgres://postgres:$PASSWORD@localhost:5432/postgres < 1_hospital_data.sql || return 1
 }
 
 function test-generate() {
   echo -e "${INFO}Test generate${NC}"
-  $SYNTH generate hospital_master | jq > hospital_data_generated.json
-  diff hospital_data_generated.json hospital_data_generated_master.json || { echo -e "${ERROR}Generated file does not match master${NC}"; return 1; }
+  load-schema --no-data || { echo -e "${ERROR}Failed to load schema${NC}"; return 1; }
+  $SYNTH generate hospital_master --to postgres://postgres:$PASSWORD@localhost:$PORT/postgres --size 30 || return 1
+
+  sum_rows_query="SELECT (SELECT count(*) FROM hospitals) +  (SELECT count(*) FROM doctors) + (SELECT count(*) FROM patients)"
+  sum=`docker exec -i $NAME psql -tA postgres://postgres:$PASSWORD@localhost:5432/postgres -c "$sum_rows_query"`
+  [ "$sum" -gt "30" ] || { echo -e "${ERROR}Generation did not create more than 30 records${NC}"; return 1; }
 }
 
 function test-import() {
   echo -e "${INFO}Testing import${NC}"
+  load-schema --all || { echo -e "${ERROR}Failed to load schema${NC}"; return 1; }
   $SYNTH import --from postgres://postgres:${PASSWORD}@localhost:${PORT}/postgres hospital_import || { echo -e "${ERROR}Import failed${NC}"; return 1; }
   diff <(jq --sort-keys . hospital_import/*) <(jq --sort-keys . hospital_master/*) || { echo -e "${ERROR}Import namespaces do not match${NC}"; return 1; }
 }
 
 function test-warning() {
   echo -e "${INFO}Testing warnings${NC}"
-  psql -f warnings/0_warnings.sql postgres://postgres:$PASSWORD@localhost:$PORT/postgres
+  docker exec -i $NAME psql postgres://postgres:$PASSWORD@localhost:5432/postgres < warnings/0_warnings.sql
   WARNINGS=$($SYNTH generate --size 10 --to postgres://postgres:$PASSWORD@localhost:$PORT/postgres warnings 2>&1)
   if [[ "$WARNINGS" == *"warnings.int32"* && "$WARNINGS" == *"warnings.int64"* ]]
   then
@@ -68,9 +74,6 @@ function test-warning() {
 }
 
 function test-local() {
-  cargo build --bin synth --release
-  SYNTH="../../../target/release/synth"
-
   up || return 1
 
   result=0
@@ -88,12 +91,11 @@ function test-local() {
 
 function up() {
   echo -e "${DEBUG}Starting container${NC}"
-  docker build -t $NAME . || return 1
   echo -e "${DEBUG}Running database with container name $NAME on port $PORT with password $PASSWORD${NC}"
-  docker run --rm --name $NAME -p $PORT:5432 -e POSTGRES_PASSWORD=$PASSWORD -d $NAME > /dev/null
+  docker run --rm --name $NAME -p $PORT:5432 -e POSTGRES_PASSWORD=$PASSWORD -d postgres > /dev/null
 
   wait_count=0
-  while [ $(docker logs $NAME 2>&1 | grep -c "ready to accept connections") -lt 2 ]
+  while ! docker exec -i $NAME psql postgres://postgres:$PASSWORD@localhost:5432/postgres -c "SELECT 1" > /dev/null 2>&1
   do
     range=$(printf "%${wait_count}s")
     echo -en "\\r${DEBUG}Waiting for DB to come up${range// /.}${NC}"
@@ -110,14 +112,13 @@ function down() {
 
 function cleanup() {
   echo -e "${DEBUG}Cleaning up local files${NC}"
-  rm -f hospital_data_generated.json
   rm -Rf hospital_import
   rm -Rf .synth
 }
 
 case "${1-*}" in
   load-schema)
-    load-schema || exit 1
+    load-schema ${2---all} || exit 1
     ;;
   test-generate)
     test-generate || exit 1
diff --git a/synth/testing_harness/postgres/hospital_data_generated_master.json b/synth/testing_harness/postgres/hospital_data_generated_master.json
deleted file mode 100644
--- a/synth/testing_harness/postgres/hospital_data_generated_master.json
+++ /dev/null
@@ -1,111 +0,0 @@
-{
-  "doctors": [
-    {
-      "date_joined": null,
-      "hospital_id": 1,
-      "id": 1,
-      "name": null
-    },
-    {
-      "date_joined": null,
-      "hospital_id": 1,
-      "id": 2,
-      "name": null
-    },
-    {
-      "date_joined": null,
-      "hospital_id": 1,
-      "id": 3,
-      "name": null
-    },
-    {
-      "date_joined": null,
-      "hospital_id": 1,
-      "id": 4,
-      "name": null
-    },
-    {
-      "date_joined": null,
-      "hospital_id": 1,
-      "id": 5,
-      "name": "F2uRb2noqulsLtX6HMlrAVC6HKHJqsUYlW4ykDizAFLHsbCXU7pJmesZRDNYcFy43p7G1e11ODKsFh8edr"
-    },
-    {
-      "date_joined": "2017-07-14",
-      "hospital_id": 1,
-      "id": 6,
-      "name": null
-    },
-    {
-      "date_joined": null,
-      "hospital_id": 1,
-      "id": 7,
-      "name": null
-    },
-    {
-      "date_joined": "2016-06-26",
-      "hospital_id": 1,
-      "id": 8,
-      "name": null
-    }
-  ],
-  "hospitals": [
-    {
-      "address": "4mcWqH2zCQeBzOgiS4skLnjvARIONor",
-      "hospital_name": null,
-      "id": 1
-    }
-  ],
-  "patients": [
-    {
-      "address": "Y8jRmG6xjnchpv08FQOovJDr0nnq4m8Er3IxAhd0ccccvZciPCulne8nuQX0j8ddHUlNrw6dw6GpIWOLEDLCcJiZDj0fkbB9iV1guzLlgxS8zTqxpJM5Q6CBF1WKhZ7RYWt4a5NwKgihcWiKlU6NHDWhf",
-      "date_joined": null,
-      "doctor_id": 1,
-      "gender": null,
-      "id": 1,
-      "name": null,
-      "phone": "8NQPJ0PUL228jE0",
-      "ssn": "OeE"
-    },
-    {
-      "address": "XwQqX5DJTAYdvXxD53ZAGMjFj6K46UIp8CF9xkuGd3DhU9RJyfK0myw5vJXdAqU9Ns9ycOsChvqaHH3Elg5zJaMuo2uftO97TSisawK2PCkqe45tki0A9IX6lWRkQXUjy6rW6HqgDn1kX3mmKXj7HhjLDRRbw3vmLIlrw26rYtdNMeWlmayTleVzvHPCjZFTJm8SzGOe0iTASsVtZjQShDtfYkVEAZrDoCrO6VK",
-      "date_joined": null,
-      "doctor_id": 2,
-      "gender": null,
-      "id": 2,
-      "name": null,
-      "phone": "0bQBh",
-      "ssn": null
-    },
-    {
-      "address": "SPlQAyXDzayYd5T14t1UdJbN6smOu8wwVrEUgvpp93sJiwQApdmbIrQlUftLVhowjMe0AK1YpCZ4jBfgcME2jCs01",
-      "date_joined": null,
-      "doctor_id": 3,
-      "gender": null,
-      "id": 3,
-      "name": null,
-      "phone": "tNTT4nskvNkjZ",
-      "ssn": "pQ"
-    },
-    {
-      "address": null,
-      "date_joined": "2011-03-23",
-      "doctor_id": 4,
-      "gender": "male",
-      "id": 4,
-      "name": null,
-      "phone": null,
-      "ssn": null
-    },
-    {
-      "address": null,
-      "date_joined": null,
-      "doctor_id": 5,
-      "gender": null,
-      "id": 5,
-      "name": "OFsP1WBZimZVnJLdz6flyRdthjTr5tdowR7GQbZoTdHui7EZTqVUW3snRjy6esftmtawTqGfov4rKIiI1UOaJV29rz0N77XBiD8VovPFXPSG4g3E9ruTlka7QShqhHU3ITYYSISxu2E5r9o8n9Rds89AQOnDikJjvUw5vMoAGfyICJ0Kb0S1aLS21UAHQ",
-      "phone": "MciC2Mq1",
-      "ssn": null
-    }
-  ]
-}