Files
adam-acs/tests/property/configuration_pagination.property.spec.ts
T
Kevin L'HereecandCopilot d1c65dbf1e chore: initial commit — AdonisJS v7 TR-069 ACS
- Docker infrastructure (Dockerfile, docker-compose.yml, genieacs-sim)
- AdonisJS v7 app with Inertia.js + Vue 3
- MongoDB integration
- CWMP/TR-069 state machine engine
- Configuration engine with compliance tracking
- Branding: ADAM ACS (favicon, navbar, locales)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 16:46:37 +02:00

207 lines
6.6 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import fc from 'fast-check'
/**
* Property 18: Pagination invariants
* Validates: Requirements 11.3
*
* Verifies that:
* 1. Page number is always >= 1 (defaults to 1 if not provided)
* 2. PerPage is always >= 1 (defaults to 25 if not provided)
* 3. The correct page and perPage values are passed to the model's paginateConfigurations method
*/
// Mock the ConfigurationModel
const mockPaginateConfigurations = vi.fn()
vi.mock('#models/configuration', () => ({
default: {
paginateConfigurations: (...args: any[]) => mockPaginateConfigurations(...args),
findAllEnabled: vi.fn().mockResolvedValue([]),
},
}))
vi.mock('#models/configuration_version', () => ({
default: {
getLatestVersionNumber: vi.fn().mockResolvedValue(0),
create: vi.fn().mockResolvedValue({}),
deleteByConfigurationIdentifier: vi.fn().mockResolvedValue(0),
paginateVersions: vi.fn().mockResolvedValue({ data: [], total: 0, page: 1, perPage: 25, lastPage: 1 }),
},
}))
// Import the service once after mocks are set up
import ConfigurationService from '../../app/services/configuration_service.js'
describe('Property 18: Pagination invariants', () => {
beforeEach(() => {
mockPaginateConfigurations.mockReset()
mockPaginateConfigurations.mockResolvedValue({
data: [],
total: 0,
page: 1,
perPage: 25,
lastPage: 1,
})
})
it('page defaults to 1 when not provided', async () => {
const service = new ConfigurationService()
await service.findAll({})
expect(mockPaginateConfigurations).toHaveBeenCalledWith(1, 25, {})
})
it('perPage defaults to 25 when not provided', async () => {
const service = new ConfigurationService()
await service.findAll({ page: 3 })
expect(mockPaginateConfigurations).toHaveBeenCalledWith(3, 25, {})
})
it('page passed to paginateConfigurations is always >= 1 for any positive integer input', async () => {
await fc.assert(
fc.asyncProperty(
fc.integer({ min: 1, max: 10000 }),
fc.integer({ min: 1, max: 1000 }),
async (page, perPage) => {
mockPaginateConfigurations.mockClear()
mockPaginateConfigurations.mockResolvedValue({
data: [],
total: 0,
page,
perPage,
lastPage: 1,
})
const service = new ConfigurationService()
await service.findAll({ page, perPage })
expect(mockPaginateConfigurations).toHaveBeenCalledTimes(1)
const [calledPage] = mockPaginateConfigurations.mock.calls[0]
expect(calledPage).toBeGreaterThanOrEqual(1)
}
)
)
})
it('perPage passed to paginateConfigurations is always >= 1 for any positive integer input', async () => {
await fc.assert(
fc.asyncProperty(
fc.integer({ min: 1, max: 10000 }),
fc.integer({ min: 1, max: 1000 }),
async (page, perPage) => {
mockPaginateConfigurations.mockClear()
mockPaginateConfigurations.mockResolvedValue({
data: [],
total: 0,
page,
perPage,
lastPage: 1,
})
const service = new ConfigurationService()
await service.findAll({ page, perPage })
expect(mockPaginateConfigurations).toHaveBeenCalledTimes(1)
const [, calledPerPage] = mockPaginateConfigurations.mock.calls[0]
expect(calledPerPage).toBeGreaterThanOrEqual(1)
}
)
)
})
it('correct page and perPage values are passed to paginateConfigurations for random inputs', async () => {
await fc.assert(
fc.asyncProperty(
fc.integer({ min: 1, max: 10000 }),
fc.integer({ min: 1, max: 1000 }),
async (page, perPage) => {
mockPaginateConfigurations.mockClear()
mockPaginateConfigurations.mockResolvedValue({
data: [],
total: 0,
page,
perPage,
lastPage: 1,
})
const service = new ConfigurationService()
await service.findAll({ page, perPage })
expect(mockPaginateConfigurations).toHaveBeenCalledTimes(1)
const [calledPage, calledPerPage, calledFilter] =
mockPaginateConfigurations.mock.calls[0]
expect(calledPage).toBe(page)
expect(calledPerPage).toBe(perPage)
expect(calledFilter).toEqual({})
}
)
)
})
it('page defaults to 1 and perPage defaults to 25 when both are undefined', async () => {
const service = new ConfigurationService()
await service.findAll({})
expect(mockPaginateConfigurations).toHaveBeenCalledTimes(1)
const [calledPage, calledPerPage] = mockPaginateConfigurations.mock.calls[0]
expect(calledPage).toBe(1)
expect(calledPerPage).toBe(25)
})
it('filters are correctly passed alongside pagination parameters', async () => {
await fc.assert(
fc.asyncProperty(
fc.integer({ min: 1, max: 100 }),
fc.integer({ min: 1, max: 100 }),
fc.option(fc.string({ minLength: 1, maxLength: 20 }).map((s) => s.replace(/\s/g, 'x')), { nil: undefined }),
fc.option(fc.boolean(), { nil: undefined }),
fc.option(fc.string({ minLength: 1, maxLength: 20 }).map((s) => s.replace(/\s/g, 'x')), { nil: undefined }),
async (page, perPage, tag, enabled, productClass) => {
mockPaginateConfigurations.mockClear()
mockPaginateConfigurations.mockResolvedValue({
data: [],
total: 0,
page,
perPage,
lastPage: 1,
})
const service = new ConfigurationService()
await service.findAll({ page, perPage, tag, enabled, productClass })
expect(mockPaginateConfigurations).toHaveBeenCalledTimes(1)
const [calledPage, calledPerPage, calledFilter] =
mockPaginateConfigurations.mock.calls[0]
// Pagination params are passed correctly
expect(calledPage).toBe(page)
expect(calledPerPage).toBe(perPage)
// Filters are built correctly
if (tag) {
expect(calledFilter.tags).toBe(tag)
} else {
expect(calledFilter.tags).toBeUndefined()
}
if (enabled !== undefined) {
expect(calledFilter.enabled).toBe(enabled)
} else {
expect(calledFilter.enabled).toBeUndefined()
}
if (productClass) {
expect(calledFilter['criteria.productClass']).toBe(productClass)
} else {
expect(calledFilter['criteria.productClass']).toBeUndefined()
}
}
)
)
})
})