Thin MongoDB ODM built for Standard Schema
mongodb zod deno
at main 12 kB view raw
1import { assertEquals, assertExists } from "@std/assert"; 2import { z } from "@zod/zod"; 3import { Model } from "../mod.ts"; 4import { applyDefaultsForUpsert } from "../model/validation.ts"; 5import { setupTestDb, teardownTestDb } from "./utils.ts"; 6 7/** 8 * Test suite for default value handling in different operation types 9 * 10 * This tests the three main cases: 11 * 1. Plain inserts - defaults applied directly 12 * 2. Updates without upsert - defaults NOT applied 13 * 3. Upserts that create - defaults applied via $setOnInsert 14 * 4. Upserts that match - $setOnInsert ignored (correct behavior) 15 * 5. Replace with upsert - defaults applied on creation 16 */ 17 18// Schema with defaults for testing 19const productSchema = z.object({ 20 name: z.string(), 21 price: z.number().min(0), 22 category: z.string().default("general"), 23 inStock: z.boolean().default(true), 24 createdAt: z.date().default(() => new Date("2024-01-01T00:00:00Z")), 25 tags: z.array(z.string()).default([]), 26}); 27 28let ProductModel: Model<typeof productSchema>; 29 30Deno.test.beforeAll(async () => { 31 await setupTestDb(); 32 ProductModel = new Model("test_products_defaults", productSchema); 33}); 34 35Deno.test.beforeEach(async () => { 36 await ProductModel.delete({}); 37}); 38 39Deno.test.afterAll(async () => { 40 await ProductModel.delete({}); 41 await teardownTestDb(); 42}); 43 44Deno.test({ 45 name: "Defaults: Case 1 - Plain insert applies defaults", 46 async fn() { 47 // Insert without providing optional fields with defaults 48 const result = await ProductModel.insertOne({ 49 name: "Widget", 50 price: 29.99, 51 // category, inStock, createdAt, tags not provided - should use defaults 52 }); 53 54 assertExists(result.insertedId); 55 56 // Verify defaults were applied 57 const product = await ProductModel.findById(result.insertedId); 58 assertExists(product); 59 60 assertEquals(product.name, "Widget"); 61 assertEquals(product.price, 29.99); 62 assertEquals(product.category, "general"); // default 63 assertEquals(product.inStock, true); // default 64 assertExists(product.createdAt); // default function called 65 assertEquals(product.tags, []); // default empty array 66 }, 67 sanitizeResources: false, 68 sanitizeOps: false, 69}); 70 71Deno.test({ 72 name: "Defaults: Case 2 - Update without upsert does NOT apply defaults", 73 async fn() { 74 // First create a document without defaults (simulate old data) 75 const insertResult = await ProductModel.insertOne({ 76 name: "Gadget", 77 price: 19.99, 78 category: "electronics", 79 inStock: false, 80 createdAt: new Date("2023-01-01"), 81 tags: ["test"], 82 }); 83 84 assertExists(insertResult.insertedId); 85 86 // Now update it - defaults should NOT be applied 87 await ProductModel.updateOne( 88 { _id: insertResult.insertedId }, 89 { price: 24.99 }, 90 // No upsert flag 91 ); 92 93 const updated = await ProductModel.findById(insertResult.insertedId); 94 assertExists(updated); 95 96 assertEquals(updated.price, 24.99); // updated 97 assertEquals(updated.category, "electronics"); // unchanged 98 assertEquals(updated.inStock, false); // unchanged 99 assertEquals(updated.tags, ["test"]); // unchanged 100 }, 101 sanitizeResources: false, 102 sanitizeOps: false, 103}); 104 105Deno.test({ 106 name: 107 "Defaults: Case 3 - Upsert that creates applies defaults via $setOnInsert", 108 async fn() { 109 // Upsert with a query that won't match - will create new document 110 const result = await ProductModel.updateOne( 111 { name: "NonExistent" }, 112 { price: 39.99 }, 113 { upsert: true }, 114 ); 115 116 assertEquals(result.upsertedCount, 1); 117 assertExists(result.upsertedId); 118 119 // Verify the created document has defaults applied 120 const product = await ProductModel.findOne({ name: "NonExistent" }); 121 assertExists(product); 122 123 assertEquals(product.price, 39.99); // from $set 124 assertEquals(product.name, "NonExistent"); // from query 125 assertEquals(product.category, "general"); // default via $setOnInsert 126 assertEquals(product.inStock, true); // default via $setOnInsert 127 assertExists(product.createdAt); // default via $setOnInsert 128 assertEquals(product.tags, []); // default via $setOnInsert 129 }, 130 sanitizeResources: false, 131 sanitizeOps: false, 132}); 133 134Deno.test({ 135 name: "Defaults: Case 4 - Upsert that matches does NOT apply defaults", 136 async fn() { 137 // Create a document first with explicit non-default values 138 const insertResult = await ProductModel.insertOne({ 139 name: "ExistingProduct", 140 price: 49.99, 141 category: "premium", 142 inStock: false, 143 createdAt: new Date("2023-06-01"), 144 tags: ["premium", "featured"], 145 }); 146 147 assertExists(insertResult.insertedId); 148 149 // Upsert with matching query - should update, not insert 150 const result = await ProductModel.updateOne( 151 { name: "ExistingProduct" }, 152 { price: 44.99 }, 153 { upsert: true }, 154 ); 155 156 assertEquals(result.matchedCount, 1); 157 assertEquals(result.modifiedCount, 1); 158 assertEquals(result.upsertedCount, 0); // No insert happened 159 160 // Verify defaults were NOT applied (existing values preserved) 161 const product = await ProductModel.findOne({ name: "ExistingProduct" }); 162 assertExists(product); 163 164 assertEquals(product.price, 44.99); // updated via $set 165 assertEquals(product.category, "premium"); // preserved (not overwritten with default) 166 assertEquals(product.inStock, false); // preserved 167 assertEquals(product.tags, ["premium", "featured"]); // preserved 168 }, 169 sanitizeResources: false, 170 sanitizeOps: false, 171}); 172 173Deno.test({ 174 name: "Defaults: Case 5 - Replace without upsert uses defaults from parse", 175 async fn() { 176 // Create initial document 177 const insertResult = await ProductModel.insertOne({ 178 name: "ReplaceMe", 179 price: 10.0, 180 category: "old", 181 inStock: true, 182 createdAt: new Date("2020-01-01"), 183 tags: ["old"], 184 }); 185 186 assertExists(insertResult.insertedId); 187 188 // Replace with partial data - defaults should fill in missing fields 189 await ProductModel.replaceOne( 190 { _id: insertResult.insertedId }, 191 { 192 name: "Replaced", 193 price: 15.0, 194 // category, inStock, createdAt, tags not provided - defaults should apply 195 }, 196 ); 197 198 const product = await ProductModel.findById(insertResult.insertedId); 199 assertExists(product); 200 201 assertEquals(product.name, "Replaced"); 202 assertEquals(product.price, 15.0); 203 assertEquals(product.category, "general"); // default applied 204 assertEquals(product.inStock, true); // default applied 205 assertExists(product.createdAt); // default applied 206 assertEquals(product.tags, []); // default applied 207 }, 208 sanitizeResources: false, 209 sanitizeOps: false, 210}); 211 212Deno.test({ 213 name: "Defaults: Case 6 - Replace with upsert (creates) applies defaults", 214 async fn() { 215 // Replace with upsert on non-existent document 216 const result = await ProductModel.replaceOne( 217 { name: "NewViaReplace" }, 218 { 219 name: "NewViaReplace", 220 price: 99.99, 221 // Missing optional fields - defaults should apply 222 }, 223 { upsert: true }, 224 ); 225 226 assertEquals(result.upsertedCount, 1); 227 assertExists(result.upsertedId); 228 229 const product = await ProductModel.findOne({ name: "NewViaReplace" }); 230 assertExists(product); 231 232 assertEquals(product.name, "NewViaReplace"); 233 assertEquals(product.price, 99.99); 234 assertEquals(product.category, "general"); // default 235 assertEquals(product.inStock, true); // default 236 assertExists(product.createdAt); // default 237 assertEquals(product.tags, []); // default 238 }, 239 sanitizeResources: false, 240 sanitizeOps: false, 241}); 242 243Deno.test({ 244 name: "Defaults: Upsert only applies defaults to unmodified fields", 245 async fn() { 246 // Upsert where we explicitly set some fields that have defaults 247 const result = await ProductModel.updateOne( 248 { name: "CustomDefaults" }, 249 { 250 price: 25.0, 251 category: "custom", // Explicitly setting a field that has a default 252 // inStock not set - should get default 253 }, 254 { upsert: true }, 255 ); 256 257 assertEquals(result.upsertedCount, 1); 258 259 const product = await ProductModel.findOne({ name: "CustomDefaults" }); 260 assertExists(product); 261 262 assertEquals(product.name, "CustomDefaults"); // from query 263 assertEquals(product.price, 25.0); // from $set 264 assertEquals(product.category, "custom"); // from $set (NOT default) 265 assertEquals(product.inStock, true); // default via $setOnInsert 266 assertExists(product.createdAt); // default via $setOnInsert 267 assertEquals(product.tags, []); // default via $setOnInsert 268 }, 269 sanitizeResources: false, 270 sanitizeOps: false, 271}); 272 273Deno.test({ 274 name: "Defaults: insertMany applies defaults to all documents", 275 async fn() { 276 const result = await ProductModel.insertMany([ 277 { name: "Bulk1", price: 10 }, 278 { name: "Bulk2", price: 20, category: "special" }, 279 { name: "Bulk3", price: 30 }, 280 ]); 281 282 assertEquals(Object.keys(result.insertedIds).length, 3); 283 284 const products = await ProductModel.find({}); 285 assertEquals(products.length, 3); 286 287 // All should have defaults where not provided 288 for (const product of products) { 289 assertExists(product.createdAt); 290 assertEquals(product.inStock, true); 291 assertEquals(product.tags, []); 292 293 if (product.name === "Bulk2") { 294 assertEquals(product.category, "special"); 295 } else { 296 assertEquals(product.category, "general"); 297 } 298 } 299 }, 300 sanitizeResources: false, 301 sanitizeOps: false, 302}); 303 304Deno.test({ 305 name: 306 "Defaults: applyDefaultsForUpsert preserves existing $setOnInsert values", 307 fn() { 308 const schema = z.object({ 309 name: z.string(), 310 flag: z.boolean().default(true), 311 count: z.number().default(0), 312 }); 313 314 const update = { 315 $set: { name: "test" }, 316 $setOnInsert: { flag: false }, 317 }; 318 319 const result = applyDefaultsForUpsert(schema, {}, update); 320 321 assertEquals(result.$setOnInsert?.flag, false); 322 assertEquals(result.$setOnInsert?.count, 0); 323 }, 324 sanitizeResources: false, 325 sanitizeOps: false, 326}); 327 328Deno.test({ 329 name: 330 "Defaults: applyDefaultsForUpsert keeps query equality fields untouched", 331 fn() { 332 const schema = z.object({ 333 status: z.string().default("pending"), 334 flag: z.boolean().default(true), 335 name: z.string(), 336 }); 337 338 const query = { status: "queued" }; 339 const update = { $set: { name: "upsert-test" } }; 340 341 const result = applyDefaultsForUpsert(schema, query, update); 342 343 assertEquals(result.$setOnInsert?.status, undefined); 344 assertEquals(result.$setOnInsert?.flag, true); 345 }, 346 sanitizeResources: false, 347 sanitizeOps: false, 348}); 349 350Deno.test({ 351 name: 352 "Defaults: findOneAndUpdate with upsert preserves query equality fields", 353 async fn() { 354 await ProductModel.findOneAndUpdate( 355 { name: "FindOneUpsert", category: "special" }, 356 { price: 12.5 }, 357 { upsert: true }, 358 ); 359 360 const product = await ProductModel.findOne({ name: "FindOneUpsert" }); 361 assertExists(product); 362 363 assertEquals(product.category, "special"); // from query, not default 364 assertEquals(product.price, 12.5); // from update 365 assertEquals(product.inStock, true); // default applied 366 assertExists(product.createdAt); // default applied 367 assertEquals(product.tags, []); // default applied 368 }, 369 sanitizeResources: false, 370 sanitizeOps: false, 371}); 372 373Deno.test({ 374 name: "Defaults: findOneAndReplace with upsert applies defaults on creation", 375 async fn() { 376 const result = await ProductModel.findOneAndReplace( 377 { name: "FindOneReplaceUpsert" }, 378 { 379 name: "FindOneReplaceUpsert", 380 price: 77.0, 381 }, 382 { upsert: true }, 383 ); 384 385 assertExists(result.lastErrorObject?.upserted); 386 387 const product = await ProductModel.findOne({ 388 name: "FindOneReplaceUpsert", 389 }); 390 assertExists(product); 391 392 assertEquals(product.name, "FindOneReplaceUpsert"); 393 assertEquals(product.price, 77.0); 394 assertEquals(product.category, "general"); // default applied 395 assertEquals(product.inStock, true); // default applied 396 assertExists(product.createdAt); // default applied 397 assertEquals(product.tags, []); // default applied 398 }, 399 sanitizeResources: false, 400 sanitizeOps: false, 401});