Querying Elasticsearch from E2E tests

We now have all the required knowledge in Elasticsearch to implement our last undefined step definition, which reads from the database to see if our user document has been indexed correctly. We will be using the JavaScript client, which is merely a wrapper around the REST API, with a one-to-one mapping to its endpoints. So first, let's install it:

$ yarn add elasticsearch

Next, import the package into our spec/cucumber/steps/index.js file and create an instance of elasticsearch.Client:

const client = new elasticsearch.Client({
host: `${process.env.ELASTICSEARCH_PROTOCOL}://${process.env.ELASTICSEARCH_HOSTNAME}:${process.env.ELASTICSEARCH_PORT}`,
});

By default, Elasticsearch runs on port 9200. However, to avoid hard-coded values, we have explicitly passed in an options object, specifying the host option, which takes its value from the environment variables. To make this work, add these environment variables to our .env and .env.example files:

ELASTICSEARCH_PROTOCOL=http
ELASTICSEARCH_HOSTNAME=localhost
ELASTICSEARCH_PORT=9200
For a full list of options that the   elasticsearch.Client  constructor function accepts, check out  elastic.co/guide/en/elasticsearch/client/javascript-api/current/configuration.html.

As specified in our Cucumber test scenario, we require the Create User endpoint to return a string, which we store in this.responsePayload. This should be the ID of the user. Therefore, if we can find the user document again using this ID, it means the document is in the database and we have completed our feature.

To find the document by ID, we can use the get method from the Elasticsearch client, which will get a typed JSON document from the index based on its ID. All of the methods in the Elasticsearch client are asynchronous—if we provide a callback, it will invoke the callback; otherwise, it will return a promise.

The result from Elasticsearch would have the following structure:

{ _index: <index>,
_type: <type>,
_id: <id>,
_version: <version>,
found: true,
_source: <document> }

The _source property contains the actual document. To make sure it is the same as the one we sent in the request, we can use the deepEqual method from Node's assert module to compare the _source document with this.requestPayload.

Given this information, try to implement the final step definition yourself, and check back here for the answer:

Then(/^the payload object should be added to the database, grouped under the "([a-zA-Z]+)" type$/, function (type, callback) {
client.get({
index: 'hobnob',
type,
id: this.responsePayload,
}).then((result) => {
assert.deepEqual(result._source, this.requestPayload);
callback();
}).catch(callback);
});
ESLint may complain that _source violates the  no-underscore-dangle rule. Traditionally, underscores in an identifier are used to indicate that the variable or method should be "private", but since there're no truly private variables in JavaScript, this convention is highly controversial.

Here, however, we are using the Elasticsearch client and this is their convention. Therefore, we should add a rule to the project-level .eslintrc file to disable this rule.

Run the tests again, and there should be no undefined step definitions anymore. But, it still fails because we haven't implemented the actual success scenario in our src/index.js yet. So, let's get down to it!