Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking a "scroll" search #16

Closed
abrin opened this issue Nov 18, 2020 · 6 comments
Closed

Mocking a "scroll" search #16

abrin opened this issue Nov 18, 2020 · 6 comments

Comments

@abrin
Copy link

abrin commented Nov 18, 2020

Hi,
I really appreciate the mocking library, I've been trying to mock a scroll request... To test it, I want to return the _scroll_id. But, when I include it, I get the error below. Wondering if someone can either point me at an example of how to mock this, or suggest a way forward?

    ResponseError: Response Error

      at Class.<anonymous> (node_modules/@elastic/elasticsearch/lib/Transport.js:257:25)
      at endReadableNT (node_modules/readable-stream/lib/_stream_readable.js:1010:12)

Here's how I'm trying to send the mock in

    esmock.add(
      {
        method: ["GET", "POST"],
        path: "/:index/_search"
      },
      () => {
        return {
          _scroll_id:
            "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAQWSlpLQUZQbDNTSktXMWxWaDNJcnZZUQ==",
          hits: {
            total: { value: 1, relation: "eq" },
            max_score: 1.0,
            hits: [
              {
                _index: "test",
                _type: "doc",
                _id: "component/f745b673-25fe-5b68-9e8a-361b4e13185a",
                _score: 1.0,
                _source: {
                  id: "component/f745b673-25fe-5b68-9e8a-361b4e13185a"
                }
              }
            ]
          }
        };
      }
    );
    esmock.add(
      {
        method: ["GET", "POST"],
        path: "/_search/scroll"
      },
      () => {
        return {
          _scroll_id:
            "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAAQWSlpLQUZQbDNTSktXMWxWaDNJcnZZUQ==",
          hits: { hits: [] }
        };
      }
    );
@delvedor
Copy link
Member

Hello! It depends on how you are sending the scroll request. You can either send the _scroll_id as URL parameter, or in the body. I highly recommend using the body, as scroll ids can be quite long.

// send the scroll_id in the URL
client.scroll({
  scroll_id: '<id>',
  scroll: '30s'
}, console.log)

// send the scroll_id in the body
client.scroll({
  scroll: '30s',
  body: {
    scroll_id: '<id>'
  }
}, console.log)

If you want to use the URL params way, the mock should be updated as follows:

mock.add({
  method: 'GET',
  path: '/_search/scroll/:id'
}, () => {
  return {
    _scroll_id: 'id',
    hits: { hits: [] }
  }
})

@abrin
Copy link
Author

abrin commented Dec 10, 2020

@delvedor I gave that a try, but it doesn't seem to quite work... For clarification (using v7.9.1 of Elasticsearch and 0.3.0 of the mocking library), and following the pattern from here. Below is the full test-case.

// import * as elasticsearch from "./elasticsearch";
const Mock = require("@elastic/elasticsearch-mock");
import { Client } from "@elastic/elasticsearch";
const esmock = new Mock();

const esclient = new Client({
  node: "http://localhost:9200",
  Connection: esmock.getConnection()
});
// elasticsearch.setClient(esclient);

async function* scrollSearch(params) {
  let response = await esclient.search(params);
  while (true) {
    const sourceHits = response.body.hits.hits;

    if (sourceHits.length === 0) {
      break;
    }

    for (const hit of sourceHits) {
      yield hit._source;
    }

    if (!response.body._scroll_id) {
      break;
    }

    response = await esclient.scroll({
      scroll: params.scroll,
      body: {
        scroll_id: response.body._scroll_id
      }
    });
  }
}

describe("it tests elasticsearch", () => {
  it("tests scroll", async () => {
    esmock.add(
      {
        method: ["GET", "POST"],
        path: "/_search"
      },
      () => {
        return {
          _scroll_id: "<id>",
          hits: {
            total: { value: 1, relation: "eq" },
            max_score: 1.0,
            hits: [
              {
                _index: "test",
                _type: "doc",
                _id: "component/f745b673-25fe-5b68-9e8a-361b4e13185a",
                _score: 1.0,
                _source: {
                  id: "component/f745b673-25fe-5b68-9e8a-361b4e13185a"
                }
              }
            ]
          }
        };
      }
    );
    esmock.add(
      {
        method: "GET",
        path: "/_search/scroll/:id"
      },
      () => {
        return {
          hits: [
            {
              _index: "test",
              _type: "doc",
              _id: "component/aaaaaaa-25fe-5b68-9e8a-361b4e13185a",
              _score: 1.0,
              _source: {
                id: "component/aaaaaaa-25fe-5b68-9e8a-361b4e13185a"
              }
            }
          ]
        };
      }
    );
    let params = {
      scroll: "30s",
      size: 1,
      body: {
        query: {
          term: {
            rel_ids: "123"
          }
        }
      }
    };
    let count = 0;
    for await (const hit of scrollSearch(params)) {
      console.log(hit);
      count++;
    }
    expect(count).toEqual(2);
  });
});

The result in jest:

 FAIL  src/indexer/utils/elasticsearch.spec.js
  it tests elasticsearch
    ✕ tests scroll (41ms)

  ● it tests elasticsearch › tests scroll

    ResponseError: Response Error

      at Class.response.on (node_modules/@elastic/elasticsearch/lib/Transport.js:257:25)
      at endReadableNT (node_modules/readable-stream/lib/_stream_readable.js:1010:12)

  console.log src/indexer/utils/elasticsearch.spec.js:100
    { id: 'component/f745b673-25fe-5b68-9e8a-361b4e13185a' }

@delvedor
Copy link
Member

@abrin you are now passing the scroll_id in the body, so you need to mock the POST method as well.

@abrin
Copy link
Author

abrin commented Dec 14, 2020

changing the mock to allow ['GET','POST'] still produces a transport the same error. Simply adding the _scroll_id in the initial search response seems to be what causes it:

    esmock.add(
      {
        method: ["GET", "POST"],
        path: "/_search",
      },
      () => {
        return {
          _scroll_id: "<id>",
          hits: {

@delvedor
Copy link
Member

You must add the POST method to the scroll endpoint mock :)

@abrin
Copy link
Author

abrin commented Dec 14, 2020

Ok! got it... for posterity, and in case it's useful... here's the final constructed test before I cleanup and add local functionality. Thanks!

// setup the mock and the client
const Mock = require("@elastic/elasticsearch-mock");
import { Client } from "@elastic/elasticsearch";
const esmock = new Mock();

const esclient = new Client({
  node: "http://localhost:9200",
  Connection: esmock.getConnection(),
});

// the base scrollSearch method from the documentation
async function* scrollSearch(params) {
  let response = await esclient.search(params);
  while (true) {
    const sourceHits = response.body.hits.hits;

    if (sourceHits.length === 0) {
      break;
    }

    for (const hit of sourceHits) {
      yield hit._source;
    }

    if (!response.body._scroll_id) {
      break;
    }

    response = await esclient.scroll({
      scroll: params.scroll,
      body: {
        scroll_id: response.body._scroll_id,
      },
    });
  }
}

describe("it tests elasticsearch", () => {
  it("tests scroll", async () => {

   // mock the search call (which should be the 1st call
    esmock.add(
      {
        method: ["POST"],
        path: "/_search",
      },
      () => {
        return {
          _scroll_id: "<id>",
          hits: {
            total: { value: 1, relation: "eq" },
            max_score: 1.0,
            hits: [
              {
                _index: "test",
                _type: "doc",
                _id: "component/f745b673-25fe-5b68-9e8a-361b4e13185a",
                _score: 1.0,
                _source: {
                  id: "component/f745b673-25fe-5b68-9e8a-361b4e13185a",
                },
              },
            ],
          },
        };
      }
    );


   // mock the subsequent "scroll" calls... we'll return a result the 1st time, and then the second return nothing.
    var calls = 0;
    esmock.add(
      {
        method: ["POST"],
        path: "/_search/scroll",
      },
      () => {
        if (calls > 0) {
          return {
            hits: { hits: [] },
            _scroll_id: "<id>",
            total: { value: 0, relation: "eq" },
          };
        }
        calls++;
        return {
          _scroll_id: "<id>",
          hits: {
            total: { value: 1, relation: "eq" },
            hits: [
              {
                _index: "test",
                _type: "doc",
                _id: "component/aaaaaaa-25fe-5b68-9e8a-361b4e13185a",
                _score: 1.0,
                _source: {
                  id: "component/aaaaaaa-25fe-5b68-9e8a-361b4e13185a",
                },
              },
            ],
          },
        };
      }
    );

//  this is the initial search
    let params = {
      scroll: "30s",
      size: 1,
      body: {
        query: {
          term: {
            rel_ids: "123",
          },
        },
      },
    };
    let count = 0;
    for await (const hit of scrollSearch(params)) {
      count++;
    }
    expect(count).toEqual(2);
  });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants