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