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>
141 lines
5.2 KiB
TypeScript
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)
|
|
})
|
|
})
|