Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ test.js
dist/
.env
.env.*
.context/
2 changes: 1 addition & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
"sortPackageJson": {
"sortScripts": true
},
"ignorePatterns": ["dist/**"]
"ignorePatterns": ["dist/**", ".context/**"]
}
2 changes: 1 addition & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"options": {
"reportUnusedDisableDirectives": "error"
},
"ignorePatterns": ["**/.gitignore", "**/node_modules", "dist/**", "test.ts"],
"ignorePatterns": ["**/.gitignore", "**/node_modules", "dist/**", ".context/**", "test.ts"],
"rules": {
"no-unused-vars": [
"warn",
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true,
"typescript.tsdk": "node_modules/typescript/lib"
"js/ts.tsdk.path": "node_modules/typescript/lib"
}
63 changes: 21 additions & 42 deletions test/batcher.test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import { describe, it, expect, afterEach } from "vitest";
import { useFakeClock, sleep } from "./helpers/clock.js";
import { test, describe, expect } from "./helpers/test-api.js";
const Bottleneck = require("./bottleneck");

const wait = function (ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
};

describe("Batcher", function () {
let limiter;

afterEach(function () {
if (limiter) return limiter.disconnect(false);
});
// Batcher is datastore-independent, so this file only runs in the `local`
// project (excluded from the redis projects in vitest.config.ts) and always
// gets the fake clock — timing assertions below are exact virtual times.
useFakeClock();

it("Should batch by time and size", async function () {
limiter = new Bottleneck();
describe("Batcher", () => {
test("Should batch by time and size", async function () {
const batcher = new Bottleneck.Batcher({ maxTime: 100, maxSize: 3 });
const batches = [];
const batchTimes = [];
Expand All @@ -32,12 +25,11 @@ describe("Batcher", function () {
[1, 2, 3],
[4, 5],
]);
expect(batchTimes[0] - t0).toBeLessThan(20);
expect(batchTimes[1] - batchTimes[0]).toBeGreaterThanOrEqual(95);
expect(batchTimes[0] - t0).toBe(0);
expect(batchTimes[1] - batchTimes[0]).toBe(100);
});

it("Should batch by time", async function () {
limiter = new Bottleneck();
test("Should batch by time", async function () {
const batcher = new Bottleneck.Batcher({ maxTime: 100 });
const batches = [];
const batchTimes = [];
Expand All @@ -51,7 +43,7 @@ describe("Batcher", function () {
await Promise.all([batcher.add(1), batcher.add(2)]);

expect(batches).toStrictEqual([[1, 2]]);
expect(batchTimes[0] - t0).toBeGreaterThanOrEqual(95);
expect(batchTimes[0] - t0).toBe(100);

const t1 = Date.now();
await Promise.all([batcher.add(3), batcher.add(4)]);
Expand All @@ -60,11 +52,10 @@ describe("Batcher", function () {
[1, 2],
[3, 4],
]);
expect(batchTimes[1] - t1).toBeGreaterThanOrEqual(95);
expect(batchTimes[1] - t1).toBe(100);
});

it("Should batch by size", async function () {
limiter = new Bottleneck();
test("Should batch by size", async function () {
const batcher = new Bottleneck.Batcher({ maxSize: 2 });
const batches = [];

Expand All @@ -82,8 +73,7 @@ describe("Batcher", function () {
]);
});

it("Should stagger flushes", async function () {
limiter = new Bottleneck();
test("Should stagger flushes", async function () {
const batcher = new Bottleneck.Batcher({ maxTime: 100, maxSize: 3 });
const batches = [];
const batchTimes = [];
Expand All @@ -95,24 +85,15 @@ describe("Batcher", function () {

const t0 = Date.now();
const p1 = batcher.add(1);
await wait(50);
await sleep(50);
const p2 = batcher.add(2);
await Promise.all([p1, p2]);

expect(batches).toStrictEqual([[1, 2]]);
const elapsed = batchTimes[0] - t0;
// Lower bound is the contract: the flush MUST wait for maxTime=100ms
// since adding p2 mid-window must not reset (or shorten) the flush
// timer. The upper bound is just a sanity check — under sustained
// event-loop pressure (parallel test files, redis containers booting,
// GC) setTimeout can drift well past maxTime+40ms; the original 140ms
// upper bound was flaky for that reason.
expect(elapsed).toBeGreaterThanOrEqual(95);
expect(elapsed).toBeLessThan(1000);
expect(batchTimes[0] - t0).toBe(100);
});

it("Should force then stagger flushes", async function () {
limiter = new Bottleneck();
test("Should force then stagger flushes", async function () {
const batcher = new Bottleneck.Batcher({ maxTime: 100, maxSize: 3 });
const batches = [];
const batchTimes = [];
Expand All @@ -125,20 +106,18 @@ describe("Batcher", function () {
const t0 = Date.now();
await Promise.all([batcher.add(1), batcher.add(2), batcher.add(3)]);
expect(batches).toStrictEqual([[1, 2, 3]]);
expect(batchTimes[0] - t0).toBeLessThan(20);
expect(batchTimes[0] - t0).toBe(0);

const t1 = Date.now();
const p4 = batcher.add(4);
await wait(50);
await sleep(50);
const p5 = batcher.add(5);
await Promise.all([p4, p5]);

expect(batches).toStrictEqual([
[1, 2, 3],
[4, 5],
]);
const elapsed = batchTimes[1] - t1;
expect(elapsed).toBeGreaterThanOrEqual(95);
expect(elapsed).toBeLessThan(140);
expect(batchTimes[1] - t1).toBe(100);
});
});
Loading