/** * Tests for schedule GET API route * * @vitest-environment node */ import { authMockFns, databaseMock, workflowAuthzMockFns, workflowsUtilsMock } from '@sim/testing' import { NextRequest } from 'next/server' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/workflows/utils', () => workflowsUtilsMock) vi.mock('drizzle-orm', () => ({ eq: vi.fn(), and: vi.fn(), or: vi.fn(), isNull: vi.fn(), })) import { GET } from '@/app/api/schedules/route' function createRequest(url: string): NextRequest { return new NextRequest(new URL(url), { method: 'GET' }) } const mockDbSelect = databaseMock.db.select as ReturnType function mockDbChain(results: any[]) { let callIndex = 0 mockDbSelect.mockImplementation(() => ({ from: () => ({ where: () => ({ limit: () => results[callIndex++] || [], }), leftJoin: () => ({ where: () => ({ limit: () => results[callIndex++] || [], }), }), }), })) } describe('Schedule GET API', () => { beforeEach(() => { vi.clearAllMocks() authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-1' } }) workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({ allowed: true, status: 200, workflow: { id: 'wf-1', workspaceId: 'ws-1' }, workspacePermission: 'read', }) }) afterEach(() => { vi.clearAllMocks() }) it('returns schedule data for authorized user', async () => { mockDbChain([ [ { schedule: { id: 'sched-1', cronExpression: '0 9 * * *', status: 'active', failedCount: 0, }, }, ], ]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) const data = await res.json() expect(res.status).toBe(200) expect(data.schedule.cronExpression).toBe('0 9 * * *') expect(data.isDisabled).toBe(false) }) it('returns null when no schedule exists', async () => { mockDbChain([[]]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) const data = await res.json() expect(res.status).toBe(200) expect(data.schedule).toBeNull() }) it('requires authentication', async () => { authMockFns.mockGetSession.mockResolvedValue(null) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) expect(res.status).toBe(401) }) it('requires workflowId parameter', async () => { const res = await GET(createRequest('http://test/api/schedules')) expect(res.status).toBe(400) }) it('returns 404 for non-existent workflow', async () => { workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({ allowed: false, status: 404, message: 'Workflow not found', workflow: null, workspacePermission: null, }) mockDbChain([[]]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) expect(res.status).toBe(404) }) it('denies access for unauthorized user', async () => { workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({ allowed: false, status: 403, message: 'Unauthorized: Access denied to read this workflow', workflow: { id: 'wf-1', workspaceId: 'ws-1' }, workspacePermission: null, }) mockDbChain([[{ userId: 'other-user', workspaceId: null }]]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) expect(res.status).toBe(403) }) it('allows workspace members to view', async () => { mockDbChain([[{ schedule: { id: 'sched-1', status: 'active', failedCount: 0 } }]]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) expect(res.status).toBe(200) }) it('indicates disabled schedule with failures', async () => { mockDbChain([[{ schedule: { id: 'sched-1', status: 'disabled', failedCount: 100 } }]]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) const data = await res.json() expect(res.status).toBe(200) expect(data.isDisabled).toBe(true) expect(data.hasFailures).toBe(true) }) })