UNPKG

@sidequest/backend-test

Version:

@sidequest/backend-test is a test suite for backend implementations of Sidequest, a Node.js library for managing background jobs and distributed queues.

250 lines (245 loc) 13.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var vitest = require('vitest'); var backend = require('./backend.cjs'); function defineCountJobsOverTimeTestSuite() { vitest.describe("countJobsOverTime", () => { vitest.it("should throw error for invalid time range format", async () => { await vitest.expect(backend.backend.countJobsOverTime("invalid")).rejects.toThrow(); await vitest.expect(backend.backend.countJobsOverTime("12x")).rejects.toThrow(); await vitest.expect(backend.backend.countJobsOverTime("")).rejects.toThrow(); }); vitest.it("should return correct number of time buckets with zero counts when no jobs exist", async () => { const result = await backend.backend.countJobsOverTime("5m"); vitest.expect(result).toHaveLength(5); result.forEach((bucket) => { vitest.expect(bucket).toHaveProperty("timestamp"); vitest.expect(bucket.timestamp).toBeInstanceOf(Date); vitest.expect(bucket.total).toBe(0); vitest.expect(bucket.waiting).toBe(0); vitest.expect(bucket.claimed).toBe(0); vitest.expect(bucket.running).toBe(0); vitest.expect(bucket.completed).toBe(0); vitest.expect(bucket.failed).toBe(0); vitest.expect(bucket.canceled).toBe(0); }); // Check that timestamps are in chronological order and properly spaced for (let i = 1; i < result.length; i++) { const timeDiff = result[i].timestamp.getTime() - result[i - 1].timestamp.getTime(); vitest.expect(timeDiff).toBe(60 * 1000); // 1 minute in milliseconds } }); vitest.it("should return correct number of time buckets for different time ranges", async () => { const resultMinutes = await backend.backend.countJobsOverTime("3m"); vitest.expect(resultMinutes).toHaveLength(3); const resultHours = await backend.backend.countJobsOverTime("2h"); vitest.expect(resultHours).toHaveLength(2); const resultDays = await backend.backend.countJobsOverTime("4d"); vitest.expect(resultDays).toHaveLength(4); }); vitest.it("should count jobs correctly by state and time bucket", async () => { const now = new Date(); const twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000); const oneMinuteAgo = new Date(now.getTime() - 1 * 60 * 1000); // Create jobs with different timestamps await backend.backend.createNewJob({ queue: "test-queue", script: "test-script", class: "TestJob", args: [], constructor_args: [], state: "waiting", attempt: 0, }); const job2 = await backend.backend.createNewJob({ queue: "test-queue", script: "test-script", class: "TestJob", args: [], constructor_args: [], state: "waiting", attempt: 0, }); const job3 = await backend.backend.createNewJob({ queue: "test-queue", script: "test-script", class: "TestJob", args: [], constructor_args: [], state: "waiting", attempt: 0, }); // Update jobs to different states and times await backend.backend.updateJob({ id: job2.id, state: "completed", completed_at: twoMinutesAgo, }); await backend.backend.updateJob({ id: job3.id, state: "failed", failed_at: oneMinuteAgo, }); const result = await backend.backend.countJobsOverTime("5m"); vitest.expect(result).toHaveLength(5); // Find the most recent bucket (should have the waiting job) const mostRecentBucket = result[result.length - 1]; vitest.expect(mostRecentBucket.waiting).toBeGreaterThanOrEqual(1); vitest.expect(mostRecentBucket.total).toBeGreaterThanOrEqual(1); // Check that we have some completed and failed jobs in the time range const totalCompleted = result.reduce((sum, bucket) => sum + bucket.completed, 0); const totalFailed = result.reduce((sum, bucket) => sum + bucket.failed, 0); const totalWaiting = result.reduce((sum, bucket) => sum + bucket.waiting, 0); vitest.expect(totalCompleted).toBeGreaterThanOrEqual(1); vitest.expect(totalFailed).toBeGreaterThanOrEqual(1); vitest.expect(totalWaiting).toBeGreaterThanOrEqual(1); // Verify total counts add up correctly for each bucket result.forEach((bucket) => { const calculatedTotal = bucket.waiting + bucket.claimed + bucket.running + bucket.completed + bucket.failed + bucket.canceled; vitest.expect(bucket.total).toBe(calculatedTotal); }); }); vitest.it("should handle hour-based time ranges correctly", async () => { const result = await backend.backend.countJobsOverTime("24h"); vitest.expect(result).toHaveLength(24); // Check that timestamps are spaced 1 hour apart for (let i = 1; i < result.length; i++) { const timeDiff = result[i].timestamp.getTime() - result[i - 1].timestamp.getTime(); vitest.expect(timeDiff).toBe(60 * 60 * 1000); // 1 hour in milliseconds } // Verify each bucket has proper structure result.forEach((bucket) => { vitest.expect(bucket).toHaveProperty("timestamp"); vitest.expect(bucket).toHaveProperty("total"); vitest.expect(bucket).toHaveProperty("waiting"); vitest.expect(bucket).toHaveProperty("claimed"); vitest.expect(bucket).toHaveProperty("running"); vitest.expect(bucket).toHaveProperty("completed"); vitest.expect(bucket).toHaveProperty("failed"); vitest.expect(bucket).toHaveProperty("canceled"); vitest.expect(typeof bucket.total).toBe("number"); vitest.expect(typeof bucket.waiting).toBe("number"); vitest.expect(typeof bucket.claimed).toBe("number"); vitest.expect(typeof bucket.running).toBe("number"); vitest.expect(typeof bucket.completed).toBe("number"); vitest.expect(typeof bucket.failed).toBe("number"); vitest.expect(typeof bucket.canceled).toBe("number"); }); }); vitest.it("should handle day-based time ranges correctly", async () => { const result = await backend.backend.countJobsOverTime("7d"); vitest.expect(result).toHaveLength(7); // Check that timestamps are spaced 1 day apart for (let i = 1; i < result.length; i++) { const timeDiff = result[i].timestamp.getTime() - result[i - 1].timestamp.getTime(); vitest.expect(timeDiff).toBe(24 * 60 * 60 * 1000); // 1 day in milliseconds } }); vitest.it("should use appropriate timestamp fields based on job state", async () => { const now = new Date(); const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000); // Create a job and update it through different states const job = await backend.backend.createNewJob({ queue: "test-queue", script: "test-script", class: "TestJob", args: [], constructor_args: [], state: "waiting", attempt: 0, }); // Update to completed state with specific completed_at timestamp await backend.backend.updateJob({ id: job.id, state: "completed", completed_at: oneHourAgo, }); const result = await backend.backend.countJobsOverTime("2h"); vitest.expect(result).toHaveLength(2); // The job should appear in the time bucket corresponding to when it was completed const totalCompleted = result.reduce((sum, bucket) => sum + bucket.completed, 0); vitest.expect(totalCompleted).toBe(1); }); vitest.it("should return timestamps in chronological order", async () => { const result = await backend.backend.countJobsOverTime("10m"); vitest.expect(result).toHaveLength(10); for (let i = 1; i < result.length; i++) { vitest.expect(result[i].timestamp.getTime()).toBeGreaterThan(result[i - 1].timestamp.getTime()); } // The last timestamp should be the most recent const lastTimestamp = result[result.length - 1].timestamp; const now = new Date(); // Allow for some tolerance (within the last minute) const timeDifference = now.getTime() - lastTimestamp.getTime(); vitest.expect(timeDifference).toBeLessThan(60 * 1000); // Less than 1 minute difference }); vitest.it("should include all job states in each time bucket", async () => { const result = await backend.backend.countJobsOverTime("3m"); result.forEach((bucket) => { // Verify structure and types vitest.expect(bucket.timestamp).toBeInstanceOf(Date); vitest.expect(typeof bucket.total).toBe("number"); vitest.expect(typeof bucket.waiting).toBe("number"); vitest.expect(typeof bucket.claimed).toBe("number"); vitest.expect(typeof bucket.running).toBe("number"); vitest.expect(typeof bucket.completed).toBe("number"); vitest.expect(typeof bucket.failed).toBe("number"); vitest.expect(typeof bucket.canceled).toBe("number"); // Verify all counts are non-negative vitest.expect(bucket.total).toBeGreaterThanOrEqual(0); vitest.expect(bucket.waiting).toBeGreaterThanOrEqual(0); vitest.expect(bucket.claimed).toBeGreaterThanOrEqual(0); vitest.expect(bucket.running).toBeGreaterThanOrEqual(0); vitest.expect(bucket.completed).toBeGreaterThanOrEqual(0); vitest.expect(bucket.failed).toBeGreaterThanOrEqual(0); vitest.expect(bucket.canceled).toBeGreaterThanOrEqual(0); }); }); vitest.it("should handle edge case with single time unit", async () => { const result = await backend.backend.countJobsOverTime("1m"); vitest.expect(result).toHaveLength(1); const resultHour = await backend.backend.countJobsOverTime("1h"); vitest.expect(resultHour).toHaveLength(1); const resultDay = await backend.backend.countJobsOverTime("1d"); vitest.expect(resultDay).toHaveLength(1); }); vitest.it("should filter jobs correctly within the time range", async () => { const now = new Date(); const veryOldTime = new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000); // 10 days ago // Create a very old job const oldJob = await backend.backend.createNewJob({ queue: "test-queue", script: "test-script", class: "TestJob", args: [], constructor_args: [], state: "waiting", attempt: 0, }); await backend.backend.updateJob({ id: oldJob.id, state: "completed", completed_at: veryOldTime, }); // Create a recent job await backend.backend.createNewJob({ queue: "test-queue", script: "test-script", class: "TestJob", args: [], constructor_args: [], state: "waiting", attempt: 0, }); const result = await backend.backend.countJobsOverTime("5m"); // The old job should not appear in the 5-minute window const totalCompleted = result.reduce((sum, bucket) => sum + bucket.completed, 0); vitest.expect(totalCompleted).toBe(0); // But the recent waiting job should appear const totalWaiting = result.reduce((sum, bucket) => sum + bucket.waiting, 0); vitest.expect(totalWaiting).toBeGreaterThanOrEqual(1); }); }); } exports.default = defineCountJobsOverTimeTestSuite; //# sourceMappingURL=countJobsOverTime.cjs.map