652 lines
20 KiB
JavaScript
652 lines
20 KiB
JavaScript
/**
|
|
* Unit tests for ScheduleService functionality
|
|
* Tests Requirements 1.1, 1.4, 3.1, 4.1, 4.2, 4.3 from the scheduled-price-updates spec
|
|
*/
|
|
|
|
const ScheduleService = require("../../src/services/schedule");
|
|
const Logger = require("../../src/utils/logger");
|
|
|
|
// Mock logger to avoid file operations during tests
|
|
jest.mock("../../src/utils/logger");
|
|
|
|
describe("ScheduleService", () => {
|
|
let scheduleService;
|
|
let mockLogger;
|
|
|
|
beforeEach(() => {
|
|
// Mock logger
|
|
mockLogger = {
|
|
info: jest.fn().mockResolvedValue(),
|
|
warning: jest.fn().mockResolvedValue(),
|
|
error: jest.fn().mockResolvedValue(),
|
|
};
|
|
Logger.mockImplementation(() => mockLogger);
|
|
|
|
scheduleService = new ScheduleService(mockLogger);
|
|
|
|
// Clear any existing timers
|
|
jest.clearAllTimers();
|
|
jest.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Clean up schedule service
|
|
scheduleService.cleanup();
|
|
jest.useRealTimers();
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe("parseScheduledTime - Requirement 1.1, 4.1, 4.2", () => {
|
|
describe("Valid datetime formats", () => {
|
|
test("should parse basic ISO 8601 format", () => {
|
|
const futureTime = new Date(Date.now() + 60000)
|
|
.toISOString()
|
|
.slice(0, 19);
|
|
const result = scheduleService.parseScheduledTime(futureTime);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
|
|
test("should parse ISO 8601 with UTC timezone", () => {
|
|
const futureTime = new Date(Date.now() + 60000).toISOString();
|
|
const result = scheduleService.parseScheduledTime(futureTime);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
|
|
test("should parse ISO 8601 with positive timezone offset", () => {
|
|
// Create a future time that accounts for timezone offset
|
|
const futureTime =
|
|
new Date(Date.now() + 60000 + 5 * 60 * 60 * 1000)
|
|
.toISOString()
|
|
.slice(0, 19) + "+05:00";
|
|
const result = scheduleService.parseScheduledTime(futureTime);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
|
|
test("should parse ISO 8601 with negative timezone offset", () => {
|
|
const futureTime =
|
|
new Date(Date.now() + 60000).toISOString().slice(0, 19) + "-08:00";
|
|
const result = scheduleService.parseScheduledTime(futureTime);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
|
|
test("should parse ISO 8601 with milliseconds", () => {
|
|
const futureTime = new Date(Date.now() + 60000).toISOString();
|
|
const result = scheduleService.parseScheduledTime(futureTime);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
|
|
test("should handle whitespace around valid datetime", () => {
|
|
const futureTime =
|
|
" " + new Date(Date.now() + 60000).toISOString().slice(0, 19) + " ";
|
|
const result = scheduleService.parseScheduledTime(futureTime);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
});
|
|
|
|
describe("Invalid datetime formats - Requirement 1.4", () => {
|
|
test("should throw error for null input", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(null);
|
|
}).toThrow(/❌ Scheduled time is required but not provided/);
|
|
});
|
|
|
|
test("should throw error for undefined input", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(undefined);
|
|
}).toThrow(/❌ Scheduled time is required but not provided/);
|
|
});
|
|
|
|
test("should throw error for empty string", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("");
|
|
}).toThrow(/❌ Scheduled time is required but not provided/);
|
|
});
|
|
|
|
test("should throw error for whitespace-only string", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(" ");
|
|
}).toThrow(/❌ Scheduled time cannot be empty/);
|
|
});
|
|
|
|
test("should throw error for non-string input", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(123);
|
|
}).toThrow(/❌ Scheduled time must be provided as a string/);
|
|
});
|
|
|
|
test("should throw error for invalid format - space instead of T", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-12-25 10:30:00");
|
|
}).toThrow(/❌ Invalid datetime format/);
|
|
});
|
|
|
|
test("should throw error for invalid format - missing time", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-12-25");
|
|
}).toThrow(/❌ Invalid datetime format/);
|
|
});
|
|
|
|
test("should throw error for invalid format - wrong separator", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024/12/25T10:30:00");
|
|
}).toThrow(/❌ Invalid datetime format/);
|
|
});
|
|
|
|
test("should throw error for invalid month value", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-13-25T10:30:00");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
|
|
test("should throw error for invalid day value", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-12-32T10:30:00");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
|
|
test("should throw error for invalid hour value", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-12-25T25:30:00");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
|
|
test("should throw error for invalid minute value", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-12-25T10:60:00");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
|
|
test("should throw error for invalid second value", () => {
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2024-12-25T10:30:60");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
|
|
test("should throw error for impossible date", () => {
|
|
// Test with invalid month value instead since JS Date auto-corrects impossible dates
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2026-13-15T10:30:00");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
});
|
|
|
|
describe("Past datetime validation - Requirement 4.3", () => {
|
|
test("should throw error for past datetime", () => {
|
|
// Use a clearly past date
|
|
const pastTime = "2020-01-01T10:30:00";
|
|
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(pastTime);
|
|
}).toThrow(/❌ Scheduled time is in the past/);
|
|
});
|
|
|
|
test("should throw error for current time", () => {
|
|
// Use a clearly past date
|
|
const pastTime = "2020-01-01T10:30:00";
|
|
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(pastTime);
|
|
}).toThrow(/❌ Scheduled time is in the past/);
|
|
});
|
|
|
|
test("should include helpful context in past time error", () => {
|
|
// Use a clearly past date
|
|
const pastTime = "2020-01-01T10:30:00";
|
|
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(pastTime);
|
|
}).toThrow(/days ago/);
|
|
});
|
|
});
|
|
|
|
describe("Distant future validation - Requirement 4.4", () => {
|
|
test("should warn for dates more than 7 days in future", () => {
|
|
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
const distantFuture = new Date(Date.now() + 8 * 24 * 60 * 60 * 1000)
|
|
.toISOString()
|
|
.slice(0, 19);
|
|
|
|
const result = scheduleService.parseScheduledTime(distantFuture);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining("⚠️ WARNING: Distant Future Scheduling")
|
|
);
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
|
|
test("should not warn for dates within 7 days", () => {
|
|
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
const nearFuture = new Date(Date.now() + 6 * 24 * 60 * 60 * 1000)
|
|
.toISOString()
|
|
.slice(0, 19);
|
|
|
|
const result = scheduleService.parseScheduledTime(nearFuture);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(consoleSpy).not.toHaveBeenCalled();
|
|
|
|
consoleSpy.mockRestore();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("calculateDelay - Delay calculation accuracy", () => {
|
|
test("should calculate correct delay for future time", () => {
|
|
const futureTime = new Date(Date.now() + 5000); // 5 seconds from now
|
|
const delay = scheduleService.calculateDelay(futureTime);
|
|
|
|
expect(delay).toBeGreaterThan(4900);
|
|
expect(delay).toBeLessThan(5100);
|
|
});
|
|
|
|
test("should return 0 for past time", () => {
|
|
const pastTime = new Date(Date.now() - 1000);
|
|
const delay = scheduleService.calculateDelay(pastTime);
|
|
|
|
expect(delay).toBe(0);
|
|
});
|
|
|
|
test("should return 0 for current time", () => {
|
|
const currentTime = new Date();
|
|
const delay = scheduleService.calculateDelay(currentTime);
|
|
|
|
expect(delay).toBe(0);
|
|
});
|
|
|
|
test("should handle large delays correctly", () => {
|
|
const distantFuture = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
|
|
const delay = scheduleService.calculateDelay(distantFuture);
|
|
|
|
expect(delay).toBeGreaterThan(24 * 60 * 60 * 1000 - 1000);
|
|
expect(delay).toBeLessThan(24 * 60 * 60 * 1000 + 1000);
|
|
});
|
|
|
|
test("should handle edge case of exactly current time", () => {
|
|
const exactTime = new Date(Date.now());
|
|
const delay = scheduleService.calculateDelay(exactTime);
|
|
|
|
expect(delay).toBeGreaterThanOrEqual(0);
|
|
expect(delay).toBeLessThan(100); // Should be very small
|
|
});
|
|
});
|
|
|
|
describe("formatTimeRemaining", () => {
|
|
test("should format seconds correctly", () => {
|
|
expect(scheduleService.formatTimeRemaining(30000)).toBe("30s");
|
|
});
|
|
|
|
test("should format minutes and seconds", () => {
|
|
expect(scheduleService.formatTimeRemaining(90000)).toBe("1m 30s");
|
|
});
|
|
|
|
test("should format hours, minutes, and seconds", () => {
|
|
expect(scheduleService.formatTimeRemaining(3690000)).toBe("1h 1m 30s");
|
|
});
|
|
|
|
test("should format days, hours, minutes, and seconds", () => {
|
|
expect(scheduleService.formatTimeRemaining(90090000)).toBe(
|
|
"1d 1h 1m 30s"
|
|
);
|
|
});
|
|
|
|
test("should handle zero time", () => {
|
|
expect(scheduleService.formatTimeRemaining(0)).toBe("0s");
|
|
});
|
|
|
|
test("should handle negative time", () => {
|
|
expect(scheduleService.formatTimeRemaining(-1000)).toBe("0s");
|
|
});
|
|
|
|
test("should format only relevant units", () => {
|
|
expect(scheduleService.formatTimeRemaining(3600000)).toBe("1h");
|
|
expect(scheduleService.formatTimeRemaining(60000)).toBe("1m");
|
|
});
|
|
});
|
|
|
|
describe("waitUntilScheduledTime - Cancellation handling - Requirement 3.1", () => {
|
|
test("should resolve immediately for past time", async () => {
|
|
const pastTime = new Date(Date.now() - 1000);
|
|
const result = await scheduleService.waitUntilScheduledTime(
|
|
pastTime,
|
|
() => {}
|
|
);
|
|
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
test("should resolve true when timeout completes", async () => {
|
|
const futureTime = new Date(Date.now() + 1000);
|
|
|
|
const waitPromise = scheduleService.waitUntilScheduledTime(
|
|
futureTime,
|
|
() => {}
|
|
);
|
|
|
|
jest.advanceTimersByTime(1000);
|
|
|
|
const result = await waitPromise;
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
test("should resolve false when cancelled", async () => {
|
|
const futureTime = new Date(Date.now() + 5000);
|
|
let cancelCallbackCalled = false;
|
|
|
|
const onCancel = () => {
|
|
cancelCallbackCalled = true;
|
|
};
|
|
|
|
const waitPromise = scheduleService.waitUntilScheduledTime(
|
|
futureTime,
|
|
onCancel
|
|
);
|
|
|
|
// Advance time slightly to let cancellation check start
|
|
jest.advanceTimersByTime(150);
|
|
|
|
// Cancel the operation
|
|
scheduleService.cleanup();
|
|
|
|
// Advance time to trigger cancellation check
|
|
jest.advanceTimersByTime(150);
|
|
|
|
const result = await waitPromise;
|
|
|
|
expect(result).toBe(false);
|
|
expect(cancelCallbackCalled).toBe(true);
|
|
});
|
|
|
|
test("should clean up timeout on cancellation", async () => {
|
|
const futureTime = new Date(Date.now() + 5000);
|
|
|
|
const waitPromise = scheduleService.waitUntilScheduledTime(
|
|
futureTime,
|
|
() => {}
|
|
);
|
|
|
|
// Advance time slightly
|
|
jest.advanceTimersByTime(150);
|
|
|
|
// Cancel and verify cleanup
|
|
scheduleService.cleanup();
|
|
|
|
expect(scheduleService.cancelRequested).toBe(true);
|
|
expect(scheduleService.currentTimeoutId).toBe(null);
|
|
|
|
jest.advanceTimersByTime(150);
|
|
|
|
const result = await waitPromise;
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
test("should handle cancellation without callback", async () => {
|
|
const futureTime = new Date(Date.now() + 2000);
|
|
|
|
const waitPromise = scheduleService.waitUntilScheduledTime(futureTime);
|
|
|
|
jest.advanceTimersByTime(150);
|
|
scheduleService.cleanup();
|
|
jest.advanceTimersByTime(150);
|
|
|
|
const result = await waitPromise;
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
test("should not execute callback if timeout completes first", async () => {
|
|
const futureTime = new Date(Date.now() + 1000);
|
|
let cancelCallbackCalled = false;
|
|
|
|
const onCancel = () => {
|
|
cancelCallbackCalled = true;
|
|
};
|
|
|
|
const waitPromise = scheduleService.waitUntilScheduledTime(
|
|
futureTime,
|
|
onCancel
|
|
);
|
|
|
|
// Let timeout complete first
|
|
jest.advanceTimersByTime(1000);
|
|
|
|
// Then try to cancel (should not affect result)
|
|
scheduleService.cleanup();
|
|
|
|
const result = await waitPromise;
|
|
|
|
expect(result).toBe(true);
|
|
expect(cancelCallbackCalled).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("displayScheduleInfo", () => {
|
|
test("should display scheduling information", async () => {
|
|
const futureTime = new Date(Date.now() + 60000);
|
|
|
|
await scheduleService.displayScheduleInfo(futureTime);
|
|
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
expect.stringContaining("Operation scheduled for:")
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
expect.stringContaining("Time remaining:")
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
"Press Ctrl+C to cancel the scheduled operation"
|
|
);
|
|
});
|
|
|
|
test("should start countdown display", async () => {
|
|
const futureTime = new Date(Date.now() + 60000);
|
|
const startCountdownSpy = jest.spyOn(
|
|
scheduleService,
|
|
"startCountdownDisplay"
|
|
);
|
|
|
|
await scheduleService.displayScheduleInfo(futureTime);
|
|
|
|
expect(startCountdownSpy).toHaveBeenCalledWith(futureTime);
|
|
});
|
|
});
|
|
|
|
describe("executeScheduledOperation", () => {
|
|
test("should execute operation callback successfully", async () => {
|
|
const mockOperation = jest.fn().mockResolvedValue(0);
|
|
|
|
const result = await scheduleService.executeScheduledOperation(
|
|
mockOperation
|
|
);
|
|
|
|
expect(mockOperation).toHaveBeenCalled();
|
|
expect(result).toBe(0);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
"Executing scheduled operation..."
|
|
);
|
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
"Scheduled operation completed successfully"
|
|
);
|
|
});
|
|
|
|
test("should handle operation callback errors", async () => {
|
|
const mockOperation = jest
|
|
.fn()
|
|
.mockRejectedValue(new Error("Operation failed"));
|
|
|
|
await expect(
|
|
scheduleService.executeScheduledOperation(mockOperation)
|
|
).rejects.toThrow("Operation failed");
|
|
|
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
"Scheduled operation failed: Operation failed"
|
|
);
|
|
});
|
|
|
|
test("should return default exit code when operation returns undefined", async () => {
|
|
const mockOperation = jest.fn().mockResolvedValue(undefined);
|
|
|
|
const result = await scheduleService.executeScheduledOperation(
|
|
mockOperation
|
|
);
|
|
|
|
expect(result).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("validateSchedulingConfiguration", () => {
|
|
test("should return valid result for correct datetime", () => {
|
|
const futureTime = new Date(Date.now() + 60000)
|
|
.toISOString()
|
|
.slice(0, 19);
|
|
|
|
const result =
|
|
scheduleService.validateSchedulingConfiguration(futureTime);
|
|
|
|
expect(result.isValid).toBe(true);
|
|
expect(result.scheduledTime).toBeInstanceOf(Date);
|
|
expect(result.originalInput).toBe(futureTime);
|
|
expect(result.validationError).toBe(null);
|
|
});
|
|
|
|
test("should return invalid result with error details for bad input", () => {
|
|
const result =
|
|
scheduleService.validateSchedulingConfiguration("invalid-date");
|
|
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.scheduledTime).toBe(null);
|
|
expect(result.validationError).toContain("❌ Invalid datetime format");
|
|
expect(result.errorCategory).toBe("format");
|
|
expect(result.suggestions).toContain(
|
|
"Use ISO 8601 format: YYYY-MM-DDTHH:MM:SS"
|
|
);
|
|
});
|
|
|
|
test("should categorize missing input error correctly", () => {
|
|
const result = scheduleService.validateSchedulingConfiguration("");
|
|
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errorCategory).toBe("missing_input");
|
|
expect(result.suggestions).toContain(
|
|
"Set the SCHEDULED_EXECUTION_TIME environment variable"
|
|
);
|
|
});
|
|
|
|
test("should categorize past time error correctly", () => {
|
|
const pastTime = "2020-01-01T10:30:00";
|
|
|
|
const result = scheduleService.validateSchedulingConfiguration(pastTime);
|
|
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errorCategory).toBe("past_time");
|
|
expect(result.suggestions).toContain(
|
|
"Set a future datetime for the scheduled operation"
|
|
);
|
|
});
|
|
|
|
test("should categorize invalid values error correctly", () => {
|
|
const result = scheduleService.validateSchedulingConfiguration(
|
|
"2024-13-25T10:30:00"
|
|
);
|
|
|
|
expect(result.isValid).toBe(false);
|
|
expect(result.errorCategory).toBe("invalid_values");
|
|
expect(result.suggestions).toContain(
|
|
"Verify month is 01-12, day is valid for the month"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Resource management and cleanup", () => {
|
|
test("should initialize with correct default state", () => {
|
|
expect(scheduleService.cancelRequested).toBe(false);
|
|
expect(scheduleService.countdownInterval).toBe(null);
|
|
expect(scheduleService.currentTimeoutId).toBe(null);
|
|
});
|
|
|
|
test("should reset state correctly", () => {
|
|
// Set some state
|
|
scheduleService.cancelRequested = true;
|
|
scheduleService.countdownInterval = setInterval(() => {}, 1000);
|
|
scheduleService.currentTimeoutId = setTimeout(() => {}, 1000);
|
|
|
|
// Reset
|
|
scheduleService.reset();
|
|
|
|
expect(scheduleService.cancelRequested).toBe(false);
|
|
expect(scheduleService.countdownInterval).toBe(null);
|
|
expect(scheduleService.currentTimeoutId).toBe(null);
|
|
});
|
|
|
|
test("should handle multiple cleanup calls safely", () => {
|
|
expect(() => {
|
|
scheduleService.cleanup();
|
|
scheduleService.cleanup();
|
|
scheduleService.cleanup();
|
|
}).not.toThrow();
|
|
|
|
expect(scheduleService.cancelRequested).toBe(true);
|
|
});
|
|
|
|
test("should stop countdown display on cleanup", () => {
|
|
const stopCountdownSpy = jest.spyOn(
|
|
scheduleService,
|
|
"stopCountdownDisplay"
|
|
);
|
|
|
|
scheduleService.cleanup();
|
|
|
|
expect(stopCountdownSpy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("Edge cases and error handling", () => {
|
|
test("should handle timezone edge cases", () => {
|
|
const timeWithTimezone =
|
|
new Date(Date.now() + 60000).toISOString().slice(0, 19) + "+00:00";
|
|
|
|
const result = scheduleService.parseScheduledTime(timeWithTimezone);
|
|
|
|
expect(result).toBeInstanceOf(Date);
|
|
expect(result.getTime()).toBeGreaterThan(Date.now());
|
|
});
|
|
|
|
test("should handle leap year dates", () => {
|
|
// Test February 29th in a future leap year (2028)
|
|
const leapYearDate = "2028-02-29T10:30:00";
|
|
|
|
// This should not throw an error for a valid leap year date
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime(leapYearDate);
|
|
}).not.toThrow();
|
|
});
|
|
|
|
test("should reject February 29th in non-leap year", () => {
|
|
// JavaScript Date constructor auto-corrects Feb 29 in non-leap years to March 1
|
|
// So we test with an invalid day value instead
|
|
expect(() => {
|
|
scheduleService.parseScheduledTime("2027-02-32T10:30:00");
|
|
}).toThrow(/❌ Invalid datetime values/);
|
|
});
|
|
|
|
test("should handle very small delays correctly", () => {
|
|
const nearFuture = new Date(Date.now() + 10); // 10ms from now
|
|
const delay = scheduleService.calculateDelay(nearFuture);
|
|
|
|
expect(delay).toBeGreaterThanOrEqual(0);
|
|
expect(delay).toBeLessThan(100);
|
|
});
|
|
});
|
|
});
|