import { getSchedulerStatus, startScheduler, stopScheduler } from '$lib/server/scheduler'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // Mock environment variables const { mockEnv } = vi.hoisted(() => { return { mockEnv: { AUTH_SCHEDULER_ENABLED: 'false', AUTH_SCHEDULER_INTERVAL_MINUTES: '720' } }; }); vi.mock('$env/dynamic/private', () => ({ env: mockEnv })); // Mock the browser module vi.mock('$lib/server/browser', () => ({ getBrowser: vi.fn(), initializeBrowser: vi.fn(), closeBrowser: vi.fn() })); // Mock fs operations const mockFs = { existsSync: vi.fn(), mkdirSync: vi.fn(), writeFileSync: vi.fn(), readFileSync: vi.fn() }; describe('Scheduler Service', () => { beforeEach(() => { // Reset environment variables mockEnv.AUTH_SCHEDULER_ENABLED = 'false'; mockEnv.AUTH_SCHEDULER_INTERVAL_MINUTES = '720'; // Clear all mocks vi.clearAllMocks(); // Reset scheduler state by stopping if running try { stopScheduler(); } catch { // Ignore if not running } }); afterEach(async () => { // Ensure scheduler is stopped after each test await stopScheduler(); }); describe('Configuration', () => { it('should use default interval when AUTH_SCHEDULER_INTERVAL_MINUTES is not set', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockEnv.AUTH_SCHEDULER_INTERVAL_MINUTES = ''; const status = getSchedulerStatus(); expect(status.config.intervalMinutes).toBe(720); }); it('should parse custom interval minutes from environment', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockEnv.AUTH_SCHEDULER_INTERVAL_MINUTES = '30'; const status = getSchedulerStatus(); expect(status.config.intervalMinutes).toBe(30); }); it('should disable scheduler when AUTH_SCHEDULER_ENABLED is not true', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'false'; const status = getSchedulerStatus(); expect(status.config.enabled).toBe(false); expect(status.running).toBe(false); }); it('should parse AUTH_SCHEDULER_ENABLED as true when set to "true"', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; const status = getSchedulerStatus(); expect(status.config.enabled).toBe(true); }); }); describe('Scheduler Lifecycle', () => { it('should not start when disabled', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'false'; await startScheduler(); const status = getSchedulerStatus(); expect(status.running).toBe(false); }); it('should start when enabled', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockFs.existsSync.mockReturnValue(true); await startScheduler(); const status = getSchedulerStatus(); expect(status.running).toBe(true); }); it('should not start twice', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockFs.existsSync.mockReturnValue(true); await startScheduler(); const consoleSpy = vi.spyOn(console, 'warn'); await startScheduler(); expect(consoleSpy).toHaveBeenCalledWith('[Scheduler] Scheduler is already running'); }); it('should stop the scheduler', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockFs.existsSync.mockReturnValue(true); await startScheduler(); expect(getSchedulerStatus().running).toBe(true); await stopScheduler(); expect(getSchedulerStatus().running).toBe(false); }); it('should handle stopping when not running', async () => { const consoleSpy = vi.spyOn(console, 'log'); await stopScheduler(); expect(consoleSpy).toHaveBeenCalledWith('[Scheduler] Scheduler is not running'); }); }); describe('Status Reporting', () => { it('should return scheduler status with default values', () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'false'; const status = getSchedulerStatus(); expect(status).toEqual({ running: false, lastRenewalTime: null, isRenewing: false, config: { enabled: false, intervalMinutes: 720 } }); }); it('should report running state correctly', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockFs.existsSync.mockReturnValue(true); await startScheduler(); const status = getSchedulerStatus(); expect(status.running).toBe(true); expect(status.isRenewing).toBe(false); }); it('should track configuration', async () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockEnv.AUTH_SCHEDULER_INTERVAL_MINUTES = '1440'; const status = getSchedulerStatus(); expect(status.config.enabled).toBe(true); expect(status.config.intervalMinutes).toBe(1440); }); }); describe('Auth Renewal', () => { it('should skip renewal if no auth.json exists', async () => { mockFs.existsSync.mockReturnValue(false); // Note: In a real test, you'd import and call the renewal function directly // This test verifies the behavior when auth file is missing expect(mockFs.existsSync.mock.calls.length).toBeGreaterThanOrEqual(0); }); it('should prevent concurrent renewal attempts', async () => { // This would be tested through integration tests with actual browser context // The scheduler maintains state.isRenewing flag to prevent concurrent calls const status = getSchedulerStatus(); expect(status.isRenewing).toBe(false); }); }); describe('Environment Variables', () => { it('should handle empty AUTH_SCHEDULER_INTERVAL_MINUTES with default', () => { mockEnv.AUTH_SCHEDULER_ENABLED = 'true'; mockEnv.AUTH_SCHEDULER_INTERVAL_MINUTES = ''; const status = getSchedulerStatus(); // Empty string should fall back to default due to parseInt('', 10) returning NaN // and the || 720 fallback expect(status.config.intervalMinutes).toBeDefined(); }); }); });