Thin MongoDB ODM built for Standard Schema
mongodb zod deno
at main 6.9 kB view raw
1import { assert, assertEquals, assertExists, assertRejects } from "@std/assert"; 2import { 3 connect, 4 ConnectionError, 5 disconnect, 6 Model, 7 ValidationError, 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.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" }, { 120 name: "", 121 email: "invalid", 122 }); 123 }, 124 ValidationError, 125 "Validation failed on replace", 126 ); 127 }, 128 sanitizeResources: false, 129 sanitizeOps: false, 130}); 131 132Deno.test({ 133 name: "Errors: ValidationError - update operation should be in error", 134 async fn() { 135 const uri = await setupTestServer(); 136 await connect(uri, "test_db"); 137 138 const UserModel = new Model("users", userSchema); 139 140 try { 141 await UserModel.updateOne({ name: "test" }, { age: -5 }); 142 throw new Error("Should have thrown ValidationError"); 143 } catch (error) { 144 assert(error instanceof ValidationError); 145 assertEquals(error.operation, "update"); 146 147 const fieldErrors = error.getFieldErrors(); 148 assertExists(fieldErrors.age); 149 } 150 }, 151 sanitizeResources: false, 152 sanitizeOps: false, 153}); 154 155Deno.test({ 156 name: "Errors: ConnectionError - should throw on connection failure", 157 async fn() { 158 await assertRejects( 159 async () => { 160 await connect( 161 "mongodb://invalid-host-that-does-not-exist:27017", 162 "test_db", 163 { 164 serverSelectionTimeoutMS: 1000, // 1 second timeout 165 connectTimeoutMS: 1000, 166 }, 167 ); 168 }, 169 ConnectionError, 170 "Failed to connect to MongoDB", 171 ); 172 }, 173 sanitizeResources: false, 174 sanitizeOps: false, 175}); 176 177Deno.test({ 178 name: "Errors: ConnectionError - should include URI in error", 179 async fn() { 180 try { 181 await connect( 182 "mongodb://invalid-host-that-does-not-exist:27017", 183 "test_db", 184 { 185 serverSelectionTimeoutMS: 1000, // 1 second timeout 186 connectTimeoutMS: 1000, 187 }, 188 ); 189 throw new Error("Should have thrown ConnectionError"); 190 } catch (error) { 191 assert(error instanceof ConnectionError); 192 assertEquals( 193 error.uri, 194 "mongodb://invalid-host-that-does-not-exist:27017", 195 ); 196 } 197 }, 198 sanitizeResources: false, 199 sanitizeOps: false, 200}); 201 202Deno.test({ 203 name: 204 "Errors: ConnectionError - should throw when getDb called without connection", 205 async fn() { 206 // Make sure not connected 207 await disconnect(); 208 209 const { getDb } = await import("../client/connection.ts"); 210 211 try { 212 getDb(); 213 throw new Error("Should have thrown ConnectionError"); 214 } catch (error) { 215 assert(error instanceof ConnectionError); 216 assert(error.message.includes("not connected")); 217 } 218 }, 219 sanitizeResources: false, 220 sanitizeOps: false, 221}); 222 223Deno.test({ 224 name: "Errors: ValidationError - field errors should be grouped correctly", 225 async fn() { 226 const uri = await setupTestServer(); 227 await connect(uri, "test_db"); 228 229 const UserModel = new Model("users", userSchema); 230 231 try { 232 await UserModel.insertOne({ 233 name: "", 234 email: "not-an-email", 235 age: -10, 236 }); 237 throw new Error("Should have thrown ValidationError"); 238 } catch (error) { 239 assert(error instanceof ValidationError); 240 241 const fieldErrors = error.getFieldErrors(); 242 243 // Each field should have its own errors 244 assert(Array.isArray(fieldErrors.name)); 245 assert(Array.isArray(fieldErrors.email)); 246 assert(Array.isArray(fieldErrors.age)); 247 248 // Verify error messages are present 249 assert(fieldErrors.name.length > 0); 250 assert(fieldErrors.email.length > 0); 251 assert(fieldErrors.age.length > 0); 252 } 253 }, 254 sanitizeResources: false, 255 sanitizeOps: false, 256}); 257 258Deno.test({ 259 name: "Errors: Error name should be set correctly", 260 async fn() { 261 const uri = await setupTestServer(); 262 await connect(uri, "test_db"); 263 264 const UserModel = new Model("users", userSchema); 265 266 try { 267 await UserModel.insertOne({ name: "", email: "invalid" }); 268 } catch (error) { 269 assert(error instanceof ValidationError); 270 assertEquals(error.name, "ValidationError"); 271 } 272 }, 273 sanitizeResources: false, 274 sanitizeOps: false, 275});