Thin MongoDB ODM built for Standard Schema
mongodb zod deno
1import { assert, assertEquals, assertExists, assertRejects } from "@std/assert"; 2import { 3 connect, 4 disconnect, 5 Model, 6 ValidationError, 7 ConnectionError, 8} from "../mod.ts"; 9import { z } from "@zod/zod"; 10import { MongoMemoryServer } from "mongodb-memory-server-core"; 11 12let mongoServer: MongoMemoryServer | null = null; 13 14async function setupTestServer() { 15 if (!mongoServer) { 16 mongoServer = await MongoMemoryServer.create(); 17 } 18 return mongoServer.getUri(); 19} 20 21Deno.test.afterEach(async () => { 22 await disconnect(); 23}); 24 25Deno.test.afterAll(async () => { 26 if (mongoServer) { 27 await mongoServer.stop(); 28 mongoServer = null; 29 } 30}); 31 32// Test schemas 33const userSchema = z.object({ 34 name: z.string().min(1), 35 email: z.string().email(), 36 age: z.number().int().positive().optional(), 37}); 38 39Deno.test({ 40 name: "Errors: ValidationError - should throw on invalid insert", 41 async fn() { 42 const uri = await setupTestServer(); 43 await connect(uri, "test_db"); 44 45 const UserModel = new Model("users", userSchema); 46 47 await assertRejects( 48 async () => { 49 await UserModel.insertOne({ name: "", email: "invalid" }); 50 }, 51 ValidationError, 52 "Validation failed on insert" 53 ); 54 }, 55 sanitizeResources: false, 56 sanitizeOps: false, 57}); 58 59Deno.test({ 60 name: "Errors: ValidationError - should have structured issues", 61 async fn() { 62 const uri = await setupTestServer(); 63 await connect(uri, "test_db"); 64 65 const UserModel = new Model("users", userSchema); 66 67 try { 68 await UserModel.insertOne({ name: "", email: "invalid" }); 69 throw new Error("Should have thrown ValidationError"); 70 } catch (error) { 71 assert(error instanceof ValidationError); 72 assertEquals(error.operation, "insert"); 73 assertExists(error.issues); 74 assert(error.issues.length > 0); 75 76 // Check field errors 77 const fieldErrors = error.getFieldErrors(); 78 assertExists(fieldErrors.name); 79 assertExists(fieldErrors.email); 80 } 81 }, 82 sanitizeResources: false, 83 sanitizeOps: false, 84}); 85 86Deno.test({ 87 name: "Errors: ValidationError - should throw on invalid update", 88 async fn() { 89 const uri = await setupTestServer(); 90 await connect(uri, "test_db"); 91 92 const UserModel = new Model("users", userSchema); 93 94 await assertRejects( 95 async () => { 96 await UserModel.updateOne({ name: "test" }, { email: "invalid-email" }); 97 }, 98 ValidationError, 99 "Validation failed on update" 100 ); 101 }, 102 sanitizeResources: false, 103 sanitizeOps: false, 104}); 105 106Deno.test({ 107 name: "Errors: ValidationError - should throw on invalid replace", 108 async fn() { 109 const uri = await setupTestServer(); 110 await connect(uri, "test_db"); 111 112 const UserModel = new Model("users", userSchema); 113 114 // First insert a valid document 115 await UserModel.insertOne({ name: "Test", email: "test@example.com" }); 116 117 await assertRejects( 118 async () => { 119 await UserModel.replaceOne({ name: "Test" }, { name: "", email: "invalid" }); 120 }, 121 ValidationError, 122 "Validation failed on replace" 123 ); 124 }, 125 sanitizeResources: false, 126 sanitizeOps: false, 127}); 128 129Deno.test({ 130 name: "Errors: ValidationError - update operation should be in error", 131 async fn() { 132 const uri = await setupTestServer(); 133 await connect(uri, "test_db"); 134 135 const UserModel = new Model("users", userSchema); 136 137 try { 138 await UserModel.updateOne({ name: "test" }, { age: -5 }); 139 throw new Error("Should have thrown ValidationError"); 140 } catch (error) { 141 assert(error instanceof ValidationError); 142 assertEquals(error.operation, "update"); 143 144 const fieldErrors = error.getFieldErrors(); 145 assertExists(fieldErrors.age); 146 } 147 }, 148 sanitizeResources: false, 149 sanitizeOps: false, 150}); 151 152Deno.test({ 153 name: "Errors: ConnectionError - should throw on connection failure", 154 async fn() { 155 await assertRejects( 156 async () => { 157 await connect("mongodb://invalid-host-that-does-not-exist:27017", "test_db", { 158 serverSelectionTimeoutMS: 1000, // 1 second timeout 159 connectTimeoutMS: 1000, 160 }); 161 }, 162 ConnectionError, 163 "Failed to connect to MongoDB" 164 ); 165 }, 166 sanitizeResources: false, 167 sanitizeOps: false, 168}); 169 170Deno.test({ 171 name: "Errors: ConnectionError - should include URI in error", 172 async fn() { 173 try { 174 await connect("mongodb://invalid-host-that-does-not-exist:27017", "test_db", { 175 serverSelectionTimeoutMS: 1000, // 1 second timeout 176 connectTimeoutMS: 1000, 177 }); 178 throw new Error("Should have thrown ConnectionError"); 179 } catch (error) { 180 assert(error instanceof ConnectionError); 181 assertEquals(error.uri, "mongodb://invalid-host-that-does-not-exist:27017"); 182 } 183 }, 184 sanitizeResources: false, 185 sanitizeOps: false, 186}); 187 188Deno.test({ 189 name: "Errors: ConnectionError - should throw when getDb called without connection", 190 async fn() { 191 // Make sure not connected 192 await disconnect(); 193 194 const { getDb } = await import("../client/connection.ts"); 195 196 try { 197 getDb(); 198 throw new Error("Should have thrown ConnectionError"); 199 } catch (error) { 200 assert(error instanceof ConnectionError); 201 assert(error.message.includes("not connected")); 202 } 203 }, 204 sanitizeResources: false, 205 sanitizeOps: false, 206}); 207 208Deno.test({ 209 name: "Errors: ValidationError - field errors should be grouped correctly", 210 async fn() { 211 const uri = await setupTestServer(); 212 await connect(uri, "test_db"); 213 214 const UserModel = new Model("users", userSchema); 215 216 try { 217 await UserModel.insertOne({ 218 name: "", 219 email: "not-an-email", 220 age: -10, 221 }); 222 throw new Error("Should have thrown ValidationError"); 223 } catch (error) { 224 assert(error instanceof ValidationError); 225 226 const fieldErrors = error.getFieldErrors(); 227 228 // Each field should have its own errors 229 assert(Array.isArray(fieldErrors.name)); 230 assert(Array.isArray(fieldErrors.email)); 231 assert(Array.isArray(fieldErrors.age)); 232 233 // Verify error messages are present 234 assert(fieldErrors.name.length > 0); 235 assert(fieldErrors.email.length > 0); 236 assert(fieldErrors.age.length > 0); 237 } 238 }, 239 sanitizeResources: false, 240 sanitizeOps: false, 241}); 242 243Deno.test({ 244 name: "Errors: Error name should be set correctly", 245 async fn() { 246 const uri = await setupTestServer(); 247 await connect(uri, "test_db"); 248 249 const UserModel = new Model("users", userSchema); 250 251 try { 252 await UserModel.insertOne({ name: "", email: "invalid" }); 253 } catch (error) { 254 assert(error instanceof ValidationError); 255 assertEquals(error.name, "ValidationError"); 256 } 257 }, 258 sanitizeResources: false, 259 sanitizeOps: false, 260});