test: align in-memory jobs limit handling

This commit is contained in:
Benjamin Lu
2026-04-15 20:58:29 -07:00
parent f257b7136e
commit ea35401536
2 changed files with 71 additions and 13 deletions

View File

@@ -19,6 +19,11 @@ type RegisteredRoute = {
type PageStub = Pick<Page, 'route' | 'unroute'>
type FulfillOptions = NonNullable<Parameters<Route['fulfill']>[0]>
type JobsListFixtureResponse = Omit<JobsListResponse, 'pagination'> & {
pagination: Omit<JobsListResponse['pagination'], 'limit'> & {
limit: number | null
}
}
function createPageStub(): {
page: PageStub
@@ -185,10 +190,14 @@ describe('InMemoryJobsBackend', () => {
routes,
'http://localhost/api/jobs'
)
const response = await invokeJsonRoute<JobsListResponse>(listRouteHandler, {
url: 'http://localhost/api/jobs?offset=-1&limit=0'
})
const response = await invokeJsonRoute<JobsListFixtureResponse>(
listRouteHandler,
{
url: 'http://localhost/api/jobs?offset=-1'
}
)
expect(response.status).toBe(200)
expect(response.body.jobs.map((job) => job.id)).toEqual([
'job-newest',
'job-middle',
@@ -196,12 +205,35 @@ describe('InMemoryJobsBackend', () => {
])
expect(response.body.pagination).toEqual({
offset: 0,
limit: 3,
limit: null,
total: 3,
has_more: false
})
})
it('returns 400 when limit is not a positive integer', async () => {
const { page, routes } = createPageStub()
const backend = new InMemoryJobsBackend(page as unknown as Page)
await backend.seed([createSeededJob({ id: 'job-1', createTime: 1_000 })])
const listRouteHandler = getRouteHandler(
routes,
'http://localhost/api/jobs'
)
const response = await invokeJsonRoute<{ error: string }>(
listRouteHandler,
{
url: 'http://localhost/api/jobs?limit=0'
}
)
expect(response.status).toBe(400)
expect(response.body).toEqual({
error: 'limit must be a positive integer'
})
})
it('filters by status and workflow_id, then sorts and paginates by execution_duration', async () => {
const { page, routes } = createPageStub()
const backend = new InMemoryJobsBackend(page as unknown as Page)

View File

@@ -14,13 +14,27 @@ export type SeededJob = {
detail: JobDetailResponse
}
function parseLimit(url: URL, total: number): number {
const value = Number(url.searchParams.get('limit'))
if (!Number.isInteger(value) || value <= 0) {
return total
type JobsListFixtureResponse = Omit<JobsListResponse, 'pagination'> & {
pagination: Omit<JobsListResponse['pagination'], 'limit'> & {
limit: number | null
}
}
function parseLimit(url: URL): { error?: string; limit?: number } {
if (!url.searchParams.has('limit')) {
return {}
}
return value
const value = Number(url.searchParams.get('limit'))
if (!Number.isInteger(value)) {
return { error: 'limit must be an integer' }
}
if (value <= 0) {
return { error: 'limit must be a positive integer' }
}
return { limit: value }
}
function parseOffset(url: URL): number {
@@ -124,19 +138,31 @@ export class InMemoryJobsBackend {
})
const offset = parseOffset(url)
const { error: limitError, limit } = parseLimit(url)
if (limitError) {
await route.fulfill({
status: 400,
contentType: 'application/json',
body: JSON.stringify({ error: limitError })
})
return
}
const total = filteredJobs.length
const limit = parseLimit(url, total)
const visibleJobs = filteredJobs.slice(offset, offset + limit)
const visibleJobs =
limit === undefined
? filteredJobs.slice(offset)
: filteredJobs.slice(offset, offset + limit)
const response = {
jobs: visibleJobs,
pagination: {
offset,
limit,
limit: limit ?? null,
total,
has_more: offset + visibleJobs.length < total
}
} satisfies JobsListResponse
} satisfies JobsListFixtureResponse
await route.fulfill({
status: 200,