Thin MongoDB ODM built for Standard Schema
mongodb
zod
deno
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});