- 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>
207 lines
6.6 KiB
TypeScript
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()
|
|
}
|
|
}
|
|
)
|
|
)
|
|
})
|
|
})
|