import { assert } from "chai"; import { describe, it, beforeEach, afterEach } from "node:test"; import * as m from "../index"; import * as config from "./config"; import mockModule from "./mock"; describe("WatchDutyEvent", function() { let models: m.BackendModels, mongoose: m.MongooseModule; let testItem: ReturnType["watchDutyEvent"]; beforeEach(async function() { const c = await m.connect(config.url); models = c.models; mongoose = c.mongoose; testItem = mockModule({ mongoose }).watchDutyEvent; await models.WatchDutyEvent.syncIndexes(); }); afterEach(async function() { await models.WatchDutyEvent.deleteMany({}); await mongoose.disconnect(); }); it("is saved and reads back every field", async function() { const sut = await new models.WatchDutyEvent(testItem).save(); assert.isNotNull(sut._id); assert.equal(sut.watchDutyId, testItem.watchDutyId); assert.equal(sut.name, testItem.name); assert.equal(sut.description, testItem.description); assert.equal(sut.geoEventType, testItem.geoEventType); assert.equal(sut.locationGeoJSON?.type, "Point"); assert.equal(sut.locationGeoJSON?.coordinates[0], testItem.locationGeoJSON.coordinates[0]); assert.equal(sut.locationGeoJSON?.coordinates[1], testItem.locationGeoJSON.coordinates[1]); assert.equal(sut.address, testItem.address); assert.isFalse(sut.isPrescribed); assert.equal(sut.areaSquareMeters, testItem.areaSquareMeters); assert.equal(sut.containment, testItem.containment); assert.equal(sut.evacuationOrders, testItem.evacuationOrders); assert.equal(sut.evacuationWarnings, testItem.evacuationWarnings); assert.equal(sut.evacuationAdvisories, testItem.evacuationAdvisories); assert.equal(sut.evacuationShelterInPlace, testItem.evacuationShelterInPlace); assert.equal(sut.regions.length, 1); assert.equal(sut.regions[0]?.name, testItem.regions[0]?.name); assert.equal(sut.regions[0]?.state, testItem.regions[0]?.state); assert.isTrue(sut.active); assert.equal(sut.watchDutyCreatedAt.toISOString(), testItem.watchDutyCreatedAt.toISOString()); assert.equal(sut.watchDutyModifiedAt.toISOString(), testItem.watchDutyModifiedAt.toISOString()); assert.instanceOf(sut.created, Date); assert.instanceOf(sut.modified, Date); }); it("defaults optional fields", async function() { const sut = await new models.WatchDutyEvent({ watchDutyId: 654321, watchDutyCreatedAt: new Date(), watchDutyModifiedAt: new Date(), }).save(); assert.equal(sut.name, ""); assert.equal(sut.description, ""); assert.equal(sut.geoEventType, ""); assert.isNull(sut.locationGeoJSON); assert.equal(sut.address, ""); assert.isFalse(sut.isPrescribed); assert.isNull(sut.areaSquareMeters); assert.isNull(sut.containment); assert.equal(sut.evacuationOrders, ""); assert.equal(sut.evacuationWarnings, ""); assert.equal(sut.evacuationAdvisories, ""); assert.equal(sut.evacuationShelterInPlace, ""); assert.deepEqual(sut.regions, []); assert.isTrue(sut.active); assert.instanceOf(sut.created, Date); assert.instanceOf(sut.modified, Date); }); // timestamps defaults createdAt to true, which would store an undeclared createdAt field. it("stores no timestamp field outside the schema", async function() { await new models.WatchDutyEvent({ watchDutyId: 333, watchDutyCreatedAt: new Date(), watchDutyModifiedAt: new Date(), }).save(); const raw = await models.WatchDutyEvent.collection.findOne({ watchDutyId: 333 }); assert.isNotNull(raw); assert.notProperty(raw, "createdAt"); assert.notProperty(raw, "updatedAt"); assert.property(raw, "created"); assert.property(raw, "modified"); }); // The retention sweep deletes inactive events by `modified`, and the cron upserts through // findOneAndUpdate, which skips the schema default. Only the timestamps option keeps the // field current. it("advances modified on findOneAndUpdate", async function() { const staleDate = new Date("2020-01-02T03:04:05.000Z"); await new models.WatchDutyEvent({ watchDutyId: 222, watchDutyCreatedAt: new Date(), watchDutyModifiedAt: new Date(), }).save(); await models.WatchDutyEvent.collection.updateOne({ watchDutyId: 222, }, { $set: { modified: staleDate, }, }); const updated = await models.WatchDutyEvent.findOneAndUpdate({ watchDutyId: 222, }, { $set: { active: false, }, }, { new: true, }); assert.isNotNull(updated); assert.isFalse(updated?.active); assert.isAbove(Number(updated?.modified), Number(staleDate)); }); it("enforces a unique watchDutyId index", async function() { await new models.WatchDutyEvent({ watchDutyId: 111, watchDutyCreatedAt: new Date(), watchDutyModifiedAt: new Date(), }).save(); let thrownError: unknown = null; try { await new models.WatchDutyEvent({ watchDutyId: 111, watchDutyCreatedAt: new Date(), watchDutyModifiedAt: new Date(), }).save(); } catch (e) { thrownError = e; } assert.isNotNull(thrownError, "expected duplicate watchDutyId to be rejected"); assert.equal((thrownError as { code?: number }).code, 11000); assert.include(String((thrownError as Error).message), "E11000"); }); it("requires watchDutyId", async function() { let thrownError: unknown = null; try { await new models.WatchDutyEvent({ watchDutyCreatedAt: new Date(), watchDutyModifiedAt: new Date(), }).save(); } catch (e) { thrownError = e; } assert.isNotNull(thrownError, "expected missing watchDutyId to be rejected"); assert.include(String((thrownError as Error).message), "watchDutyId"); }); });