0

I want to test my logic and ensure that I don't exceed a limit of X requests every X seconds. Using mocha@^9.1.3, chai@^4.3.4 and sinon@^12.0.1, latest versions as of this comment.

I've read answers to very similar problems to mine, tried applying the knowledge gained but to no avail:

  1. https://stackoverflow.com/a/52196951 — great explanation as to the why, however I must've misunderstood something
  2. https://stackoverflow.com/a/55448441 — similar to 1.
  3. https://stackoverflow.com/a/43048888, https://stackoverflow.com/a/60629795 — adding clock.tickAsync inside the wait utility function would not let me step through the code

I have a wait utility function looking like this:

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

and my code to reproduce my issue in my test file looks like (please do read comments in code):

import sinon from 'sinon';
import got from 'got';

describe('sinon usefaketimers promise-timeouts', () => {
  let clock: sinon.SinonFakeTimers;

  beforeEach(() => {
    clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
    // i've also tried this
    // clock = sinon.useFakeTimers();
  });

  afterEach(() => {
    clock.restore();
  });

  it('steps through all promise-timeouts manually', async () => {
    const main = async () => {
      console.log('starting');
      await wait(1000);
      console.log('waited 1s');
      await wait(1000);
      console.log('waited 2s');
      // switching out `got()` below to `await Promise.resolve()`
      // finishes the test successfully
      await got('https://jsonplaceholder.typicode.com/todos/1').json(); // <-- switch to `await Promise.resolve()` makes it work
      console.log('request done');
      await wait(1000);
      console.log('waited 3s');
    };

    const promise = main();

    await clock.tickAsync(1000); // I have tried adding different combinations
    await clock.tickAsync(1000); // of `await Promise.resolve()` after the tick(s)
    await clock.tickAsync(1000);

    return promise;
  });
});

and the resulting output is, (it hangs after "request done", missing the last line "waited 3s"):

starting
waited 1s
waited 2s
request done

Note, I've tried switching out got to superagent and node-fetch without any success. I'm not entirely sure why they would be any different from Promise.resolve as they are all promises returned and awaited on.

Really hope to clear up whatever I didn't get from reading above questions (and so many other threads) from yesterday and todays debugging.

  • Well (ignoring the first two seconds) the code does call `got()`, then calls `clock.tickAsync(1000);`, and then only after `got()` fulfills it does `wait(1000);`. You could mock `got` and make it fulfill immediately, or have it call `tickAsync` *after* the http request finishes? Not sure what the goal of your test is here. – Bergi Jan 09 '22 at 08:50
  • It might help the understanding if you would intersperse those `await clock.tickAsync(1000);` with some more logs – Bergi Jan 09 '22 at 08:52
  • Thanks for reading my post @Bergi. This is a contrived example of my code, but the problem remains the same. I make two kinds of requests and once all is done I recursively call `main` from inside itself to go through all pagination data. First request is fetching data, second is fetching more data based on data from the first response, this is at least 1 more request to be made but could potentially be several. I have the `wait`'s to throttle requests done to stay within "X requests every X seconds". The 2x `wait` above at beginning could be reduced to 1 (as explained above) but issue remains – Alejandro Andersson Jan 09 '22 at 10:52
  • @Bergi, I did try to `nock` it, assuming it should give the same result as mocking `got`, with no success. You're right it does call `got()` and resolves the promise after 2nd `tickAsync` but does not continue past 3rd `wait` with a third `tickAsync`. I have tried to put `await Promise.resolve()` in between the 2nd and 3rd `tickAsync` wanting to flush what's in PromiseJobs queue (`got` call) but didn't get that to work either. The two initial `wait` in above code snippet is just to convey that `tickAsync` does work with my `wait` func, however only 1 in the beginning is needed – Alejandro Andersson Jan 09 '22 at 11:03

0 Answers0