Files
adam-acs/tests/integration/ttl_expiration.spec.ts
T
Kevin L'HereecandContinue ef30c2159c fix: isolate integration test databases to prevent parallel teardown conflicts
Each integration test file now uses its own MongoDB database:
- ttl_expiration → adam-acs-ttl-test
- device_model   → adam-acs-devices-test
- base_model     → adam-acs-basemodel-test

Prevents dropDatabase() in one afterAll from destroying data
used by another test file running concurrently.

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 13:27:16 +00:00

141 lines
5.2 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'
import { MongoClient, Db } from 'mongodb'
const MONGO_URI = 'mongodb://adam:adam@localhost:27017/adam-acs-ttl-test?authSource=admin'
const COLLECTION_NAME = 'cwmp_exchanges'
let client: MongoClient
let db: Db
let skip = false
describe('TTL expiration integration test', () => {
beforeAll(async () => {
try {
client = new MongoClient(MONGO_URI, { serverSelectionTimeoutMS: 2000 })
await client.connect()
db = client.db('adam-acs-ttl-test')
// Ensure the collection exists before creating the index
const collections = await db.listCollections({ name: COLLECTION_NAME }).toArray()
if (collections.length === 0) {
await db.createCollection(COLLECTION_NAME)
}
// Create the TTL index (as the migration would)
const collection = db.collection(COLLECTION_NAME)
await collection.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 86400, name: 'idx_createdAt_ttl', background: true }
)
} catch {
skip = true
}
})
afterAll(async () => {
if (skip) return
await db.dropDatabase()
await client.close()
})
beforeEach(async () => {
if (skip) return
await db.collection(COLLECTION_NAME).deleteMany({})
})
/**
* Validates: Requirements 7.3
*
* Verify the TTL index is configured with expireAfterSeconds = 86400 (24 hours).
* In a real MongoDB deployment, the TTL monitor thread runs every 60s and removes
* documents whose createdAt field is older than 86400 seconds. For this test, we
* verify the index configuration is correct rather than waiting for the TTL monitor.
*/
it('should have a TTL index on createdAt with expireAfterSeconds = 86400', async () => {
if (skip) return
const collection = db.collection(COLLECTION_NAME)
const indexes = await collection.indexes()
const ttlIndex = indexes.find((idx) => idx.name === 'idx_createdAt_ttl')
expect(ttlIndex).toBeDefined()
expect(ttlIndex!.key).toEqual({ createdAt: 1 })
expect(ttlIndex!.expireAfterSeconds).toBe(86400)
})
it('should store exchanges with createdAt set to 25 hours ago as expired candidates', async () => {
if (skip) return
const collection = db.collection(COLLECTION_NAME)
const twentyFiveHoursAgo = new Date(Date.now() - 25 * 60 * 60 * 1000)
// Insert exchanges with createdAt set 25 hours in the past
const expiredExchanges = Array.from({ length: 5 }, (_, i) => ({
deviceId: 'device-ttl-test',
direction: 'CPE→ACS' as const,
timestamp: new Date(twentyFiveHoursAgo.getTime() + i * 1000).toISOString(),
sessionId: '10.0.0.1:54321->192.168.1.1:8080',
cwmpMethod: 'Inform',
rawXml: `<soap-env:Envelope><soap-env:Body><cwmp:Inform/></soap-env:Body></soap-env:Envelope>`,
sequenceNumber: i + 1,
createdAt: twentyFiveHoursAgo,
}))
await collection.insertMany(expiredExchanges)
// Verify the documents were inserted
const count = await collection.countDocuments({ deviceId: 'device-ttl-test' })
expect(count).toBe(5)
// Verify all documents have a createdAt older than 24 hours
const docs = await collection.find({ deviceId: 'device-ttl-test' }).toArray()
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000)
for (const doc of docs) {
expect(doc.createdAt).toBeInstanceOf(Date)
expect(doc.createdAt.getTime()).toBeLessThan(twentyFourHoursAgo.getTime())
}
})
it('should NOT mark recent exchanges as expired candidates', async () => {
if (skip) return
const collection = db.collection(COLLECTION_NAME)
const now = new Date()
// Insert exchanges with current createdAt (should NOT be expired)
const recentExchanges = Array.from({ length: 3 }, (_, i) => ({
deviceId: 'device-ttl-recent',
direction: 'ACS→CPE' as const,
timestamp: new Date(now.getTime() + i * 1000).toISOString(),
sessionId: '10.0.0.1:54321->192.168.1.1:8080',
cwmpMethod: 'GetParameterValuesResponse',
rawXml: `<soap-env:Envelope><soap-env:Body><cwmp:GetParameterValuesResponse/></soap-env:Body></soap-env:Envelope>`,
sequenceNumber: i + 1,
createdAt: now,
}))
await collection.insertMany(recentExchanges)
// Verify documents exist and their createdAt is within 24 hours
const docs = await collection.find({ deviceId: 'device-ttl-recent' }).toArray()
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000)
expect(docs).toHaveLength(3)
for (const doc of docs) {
expect(doc.createdAt).toBeInstanceOf(Date)
expect(doc.createdAt.getTime()).toBeGreaterThan(twentyFourHoursAgo.getTime())
}
})
it('TTL index expireAfterSeconds should equal exactly 24 hours in seconds', async () => {
if (skip) return
const collection = db.collection(COLLECTION_NAME)
const indexes = await collection.indexes()
const ttlIndex = indexes.find((idx) => idx.name === 'idx_createdAt_ttl')
expect(ttlIndex).toBeDefined()
// 24 hours = 24 * 60 * 60 = 86400 seconds
const twentyFourHoursInSeconds = 24 * 60 * 60
expect(ttlIndex!.expireAfterSeconds).toBe(twentyFourHoursInSeconds)
})
})