Skip to content

Functional Test Runner - Docker Servers

In order to make it simpler to run some services while the functional tests are running, we've added the ability to execute docker containers while the tests execute for the purpose of exposing services to the tests. These containers are expected to expose an application via a single HTTP port and live for the life of the tests. If the application exits for any reason before the tests complete the tests will abort.

To configure docker servers in your FTR config add the dockerServers key to your config like so:

// import this helper to get TypeScript support for this section of the config
import { defineDockerServersConfig } from '@osd/test';

export default function () {
  return {
    ...

    dockerServers: defineDockerServersConfig({
      // unique names are used in logging and to get the details of this server in the tests
      helloWorld: {
        /** disable this docker server unless the user sets some flag/env var */
        enabled: !!process.env.HELLO_WORLD_PORT,
        /** the docker image to pull and run */
        image: 'vad1mo/hello-world-rest',
        /** The port that this application will be accessible via locally */
        port: process.env.HELLO_WORLD_PORT,
        /** The port that the container binds to in the container */
        portInContainer: 5050,
        /**
         * OPTIONAL: string/regex to look for in the log, when specified the
         * tests won't start until a line containing this string, or matching
         * this expression is found.
         */
        waitForLogLine: /hello/,
        /**
         * OPTIONAL: function that is called when server is started, when defined
         * it is called to give the configuration an option to write custom delay
         * logic. The function is passed a DockerServer object, which is described
         * below, and an observable of the log lines produced by the application
         */
        async waitFor(server, logLine$) {
          await logLine$.pipe(
            filter(line => line.includes('...')),
            tap((line) => {
              console.log('marking server ready because this line was logged:', line);
              console.log('server accessible from url', server.url);
            })
          ).toPromise()
        }
      }
    })
  }
}

To consume the test server, use can use something like supertest to send request. Just make sure that you disable your test suite if the user doesn't choose to enable your docker server:

import makeSupertest from 'supertest-as-promised';
import { FtrProviderContext } from '../ftr_provider_context';

export default function ({ getService }: FtrProviderContext) {
  const dockerServers = getService('dockerServers');
  const log = getService('log');

  const server = dockerServers.get('helloWorld');
  const supertest = makeSupertest(server.url);

  describe('test suite name', function () {
    if (!server.enabled) {
      log.warning(
        'disabling tests because server is not enabled, set HELLO_WORLD_PORT to run them'
      );
      this.pending = true;
    }

    it('test name', async () => {
      await supertest.get('/foo/bar').expect(200);
    });
  });
}

DockerServersService

The docker servers service is a core service that is always available in functional test runner tests. When you call getService('dockerServers') you will receive an instance of the DockerServersService class which has to methods:

has(name: string): boolean

Determine if a name resolves to a known docker server.

isEnabled(name: string): boolean

Determine if a named server is enabled.

get(name: string): DockerServer

Get a DockerServer object for the server with the given name.

DockerServer

The object passed to the waitFor() config function and returned by DockerServersService#get()

{
  url: string;
  name: string;

  portInContainer: number;
  port: number;
  image: string;
  waitForLogLine?: RegExp | string;
  waitFor?: (server: DockerServer, logLine$: Rx.Observable<string>) => Promise<boolean>;
}