From 3f4c40755fd29eb1330e1336ee631d6dfd3ce4ca Mon Sep 17 00:00:00 2001 From: sylvain-morin Date: Mon, 16 Oct 2023 15:24:38 +0200 Subject: [PATCH] Run specs with docker compose --- Dockerfile | 18 +++++++++++++++++- README.md | 23 +++++++++++++++++++++++ docker-compose.test.yml | 8 ++++---- lib/test_helper.js | 12 ++++++++++++ test/hooks.js | 17 +++++++++++------ 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index b7472fd7..bba8ffc8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ -FROM node:16 +# Platform should be forced to amd64 +# because node-mapnik is not available in arm64 +FROM --platform=linux/amd64 node:16 as base ENV NODE_ENV=development @@ -10,6 +12,20 @@ COPY config_example.js config.js RUN npm install +FROM base as test + +ENV NODE_ENV=test + +RUN apt-get update -qq && apt-get install -y postgresql-client-11 + +COPY . . + +CMD [ "npm", "run", "coverage" ] + +FROM base as development + +ENV NODE_ENV=development + COPY . . EXPOSE 4000 diff --git a/README.md b/README.md index f455fe77..628938df 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,29 @@ Filter by pattern: `NODE_ENV=test ./node_modules/mocha/bin/_mocha --recursive -- You can also add `.only` to a `describe` or `it` call to only run that test when you run `npm test`, e.g. `it.only( "should only run this test" )`. +# Running Tests with Docker + +You can run the tests with Docker Compose. +All required services will be started by the `docker-compose.test.yml` compose file: + +``` +docker compose -f docker-compose.test.yml up -d +``` + +You can follow the tests execution in the logs: + +``` +docker logs -f api-test +``` + +The first time you run the compose file, a local docker image for the API service will be automatically built, from you local GIT checkout. But if later you do some code changes, or update your GIT checkout, you need to re-build the docker image with: + +``` +docker compose -f docker-compose.test.yml build +``` + +This compose build is using the `test` target of the Dockerfile. + # ESLint Please run ESLint to check for syntax formatting errors. To run ESLint, run: `npm run eslint`. Please address any syntax errors before submitting pull requests. ESLint will also run automatically via Github Actions on submitted pull requests along with tests. diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 8879c6a1..da59823f 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -45,7 +45,10 @@ services: api-test: container_name: api-test - build: . + build: + context: . + dockerfile: Dockerfile + target: test environment: NODE_ENV: test INAT_DB_HOST: pg @@ -56,11 +59,8 @@ services: INAT_WEB_HOST: host.docker.internal INAT_DB_NAME: inaturalist_test INAT_ES_INDEX_PREFIX: test - DB_ALREADY_INITIALIZED: true ports: - 4000:4000 - command: - /bin/sh -c "npm install ; npm run coverage" extra_hosts: - "host.docker.internal:host-gateway" diff --git a/lib/test_helper.js b/lib/test_helper.js index 6ef27453..7ddd0dbf 100644 --- a/lib/test_helper.js +++ b/lib/test_helper.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ const fs = require( "fs" ); const _ = require( "lodash" ); const timersPromises = require( "timers/promises" ); @@ -46,6 +47,15 @@ testHelper.waitForES = async numberOfAttempts => { return testHelper.waitForES( numberOfAttempts ); }; +testHelper.closePGConnection = async ( ) => { + await pgClient.connection.end( ); + delete pgClient.connection; +}; + +testHelper.reconnectPGConnection = async ( ) => { + await pgClient.connect( ); +}; + testHelper.forEachIndex = async action => { if ( process.env.NODE_ENV !== "test" ) { return; } const settings = JSON.parse( fs.readFileSync( "schema/settings.js" ) ); @@ -148,6 +158,8 @@ testHelper.testInatJSNoPreload = async ( controller, endpoint, method, done ) => module.exports = { waitForPG: testHelper.waitForPG, waitForES: testHelper.waitForES, + closePGConnection: testHelper.closePGConnection, + reconnectPGConnection: testHelper.reconnectPGConnection, createIndices: testHelper.createIndices, deleteIndices: testHelper.deleteIndices, loadElasticsearchFixtures: testHelper.loadElasticsearchFixtures, diff --git a/test/hooks.js b/test/hooks.js index 039c6392..840f4cf0 100644 --- a/test/hooks.js +++ b/test/hooks.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ const { expect } = require( "chai" ); const { execSync } = require( "child_process" ); const inaturalistjs = require( "inaturalistjs" ); @@ -6,7 +7,7 @@ const testHelper = require( "../lib/test_helper" ); const Taxon = require( "../lib/models/taxon" ); const config = require( "../config" ); -function initializeDb() { +async function initializeDb() { // For tests, we want to make absolutely sure the test database is clean and // new, and we need to make sure that happens before we try to connect to it, // which happens every time we require pg_client, which happens inside of @@ -18,12 +19,18 @@ function initializeDb() { ${config.database.user ? `PGUSER=${config.database.user}` : ""} \ ${config.database.password ? `PGPASSWORD=${config.database.password}` : ""}`; + // Close connection before dropping DB + await testHelper.closePGConnection( ); + console.log( "Dropping existing test database" ); execSync( `${testDbConnectionVar} dropdb --if-exists ${dbname}`, { stdio: [0, 1, 2] } ); console.log( "Creating test database" ); execSync( `${testDbConnectionVar} createdb -O ${config.database.user} ${dbname}`, { stdio: [0, 1, 2] } ); console.log( "Loading test database schema" ); execSync( `${testDbConnectionVar} psql -q -f schema/database.sql -d ${dbname}`, { stdio: [0, 1, 2] } ); + + // Reconnecto to DB + await testHelper.reconnectPGConnection( ); } exports.mochaGlobalSetup = async function () { @@ -31,10 +38,6 @@ exports.mochaGlobalSetup = async function () { console.log( "\n\nINITIALIZING TEST ENVIRONMENT\n\n" ); - if ( !process.env.DB_ALREADY_INITIALIZED ) { - initializeDb( ); - } - // Wait for Postgres console.log( "Waiting for Postgres..." ); await testHelper.waitForPG( 100 ); @@ -43,6 +46,8 @@ exports.mochaGlobalSetup = async function () { console.log( "Waiting for ElasticSearch..." ); await testHelper.waitForES( 100 ); + initializeDb( ); + console.log( "Creating ES indices" ); await testHelper.createIndices( ); console.log( "Loading ES fixtures" ); @@ -57,7 +62,7 @@ exports.mochaGlobalSetup = async function () { exports.mochaHooks = { async beforeAll( ) { - this.timeout( 20000 ); + this.timeout( 60000 ); this.app = await app( ); }, async afterAll( ) {