Skip to content

Commit d843b35

Browse files
chore: fixes test setup for getRandomUUID (#1151)
1 parent 7aa455b commit d843b35

5 files changed

Lines changed: 76 additions & 125 deletions

File tree

api-extractor/reports/web.public.api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,9 @@ export type ExportsManagerEvents = {
581581
"export-available": [string];
582582
};
583583

584+
// @public
585+
export function getRandomUUID(): string;
586+
584587
// @public (undocumented)
585588
export interface InProgressExport extends CommonExportData {
586589
// (undocumented)

src/web.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export type { TelemetryEvents, TelemetryConfig } from "./telemetry/telemetry.js"
8484
export type { TelemetryEvent, BaseEvent } from "./telemetry/types.js";
8585
export { EventCache } from "./telemetry/eventCache.js";
8686
export { ErrorCodes, MongoDBError } from "./common/errors.js";
87+
export { getRandomUUID } from "./helpers/getRandomUUID.js";
8788
export type { AuthProvider, Credentials } from "./common/atlas/auth/authProvider.js";
8889
export type {
8990
ConnectionStringAuthType,
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { describe, it, expect, afterEach } from "vitest";
2+
import { getRandomUUID } from "mongodb-mcp-server/web";
3+
4+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5+
6+
describe("getRandomUUID() in browser environment", () => {
7+
let originalCrypto: Crypto | undefined;
8+
9+
afterEach(() => {
10+
// Restore globalThis.crypto if it was modified
11+
if (originalCrypto !== undefined) {
12+
Object.defineProperty(globalThis, "crypto", {
13+
value: originalCrypto,
14+
configurable: true,
15+
writable: true,
16+
});
17+
originalCrypto = undefined;
18+
}
19+
});
20+
21+
it("should use Web Crypto API and return a valid UUID", () => {
22+
// In a real browser (Chromium), require("crypto") resolves to crypto-browserify
23+
// which lacks randomUUID(), so getRandomUUID naturally falls back to
24+
// globalThis.crypto.randomUUID() — the Web Crypto API path.
25+
const uuid = getRandomUUID();
26+
expect(uuid).toMatch(UUID_REGEX);
27+
});
28+
29+
it("should fall back to BSON UUID when globalThis.crypto is unavailable", () => {
30+
// Save and remove globalThis.crypto entirely so both the Node.js crypto
31+
// path (crypto-browserify) and Web Crypto path fail, forcing BSON UUID.
32+
originalCrypto = globalThis.crypto;
33+
34+
Object.defineProperty(globalThis, "crypto", {
35+
value: undefined,
36+
configurable: true,
37+
writable: true,
38+
});
39+
40+
const uuid = getRandomUUID();
41+
expect(uuid).toMatch(UUID_REGEX);
42+
});
43+
44+
it("should fall back to BSON UUID when crypto.randomUUID is not a function", () => {
45+
// Keep globalThis.crypto but remove randomUUID so the Web Crypto
46+
// branch is skipped and BSON UUID is used instead.
47+
originalCrypto = globalThis.crypto;
48+
49+
const cryptoWithoutRandomUUID = Object.create(globalThis.crypto) as Crypto;
50+
Object.defineProperty(cryptoWithoutRandomUUID, "randomUUID", {
51+
value: undefined,
52+
configurable: true,
53+
writable: true,
54+
});
55+
56+
Object.defineProperty(globalThis, "crypto", {
57+
value: cryptoWithoutRandomUUID,
58+
configurable: true,
59+
writable: true,
60+
});
61+
62+
const uuid = getRandomUUID();
63+
expect(uuid).toMatch(UUID_REGEX);
64+
});
65+
});

tests/e2e/getRandomUUID.test.ts

Lines changed: 0 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,6 @@ import { execSync } from "child_process";
33
import { writeFileSync, mkdirSync, rmSync } from "fs";
44
import { join } from "path";
55

6-
const simulateBrowserEnvironment = `
7-
// Simulate browser environment by breaking require
8-
const originalRequire = global.require;
9-
global.require = function(module) {
10-
if (module === 'crypto') {
11-
throw new Error('Cannot find module crypto');
12-
}
13-
return originalRequire(module);
14-
};
15-
`;
16-
176
describe("getRandomUUID()", () => {
187
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
198
const tmpDir = join(__dirname, "..", "tmp", "uuid-e2e");
@@ -43,117 +32,4 @@ console.log(uuid);
4332

4433
expect(result).toMatch(UUID_REGEX);
4534
});
46-
47-
it("should fall back to Web Crypto API when Node.js crypto is unavailable", () => {
48-
const script = `
49-
${simulateBrowserEnvironment}
50-
51-
// Remove globalThis.crypto
52-
const originalCrypto = globalThis.crypto;
53-
delete globalThis.crypto;
54-
55-
// Now import and test
56-
import("../../../dist/esm/helpers/getRandomUUID.js").then(({ getRandomUUID }) => {
57-
const uuid = getRandomUUID();
58-
console.log(uuid);
59-
}).catch(err => {
60-
console.error('Error:', err);
61-
process.exit(1);
62-
});
63-
`;
64-
65-
const scriptPath = join(tmpDir, "test-web-crypto.mjs");
66-
writeFileSync(scriptPath, script);
67-
68-
try {
69-
const result = execSync(`node ${scriptPath}`, {
70-
encoding: "utf-8",
71-
cwd: join(__dirname, "..", ".."),
72-
}).trim();
73-
74-
if (result === "SKIP: globalThis.crypto.randomUUID not available") {
75-
// Skip this test if Web Crypto API is not available
76-
expect(true).toBe(true);
77-
} else {
78-
expect(result).toMatch(UUID_REGEX);
79-
}
80-
} catch (error) {
81-
// If the test fails, it might be because we can't properly mock require
82-
// This is acceptable for e2e tests
83-
console.warn("Could not test Web Crypto fallback:", error);
84-
}
85-
});
86-
87-
it("should fall back to BSON UUID when both crypto methods are unavailable", () => {
88-
const script = `
89-
${simulateBrowserEnvironment}
90-
91-
// Remove globalThis.crypto
92-
const originalCrypto = globalThis.crypto;
93-
delete globalThis.crypto;
94-
95-
// Now import and test
96-
import("../../../dist/esm/helpers/getRandomUUID.js").then(({ getRandomUUID }) => {
97-
const uuid = getRandomUUID();
98-
console.log(uuid);
99-
}).catch(err => {
100-
console.error('Error:', err);
101-
process.exit(1);
102-
});
103-
`;
104-
105-
const scriptPath = join(tmpDir, "test-bson-fallback.mjs");
106-
writeFileSync(scriptPath, script);
107-
108-
try {
109-
const result = execSync(`node ${scriptPath}`, {
110-
encoding: "utf-8",
111-
cwd: join(__dirname, "..", ".."),
112-
}).trim();
113-
114-
expect(result).toMatch(UUID_REGEX);
115-
} catch (error) {
116-
// If the test fails, it might be because we can't properly mock require
117-
console.warn("Could not test BSON fallback:", error);
118-
}
119-
});
120-
121-
it("should fall back to BSON UUID when crypto.randomUUID is not a function", () => {
122-
const script = `
123-
${simulateBrowserEnvironment}
124-
125-
// Mock globalThis.crypto without randomUUID using Object.defineProperty
126-
Object.defineProperty(globalThis, 'crypto', {
127-
value: {
128-
getRandomValues: function() {}
129-
},
130-
writable: true,
131-
configurable: true
132-
});
133-
134-
// Now import and test
135-
import("../../../dist/esm/helpers/getRandomUUID.js").then(({ getRandomUUID }) => {
136-
const uuid = getRandomUUID();
137-
console.log(uuid);
138-
}).catch(err => {
139-
console.error('Error:', err);
140-
process.exit(1);
141-
});
142-
`;
143-
144-
const scriptPath = join(tmpDir, "test-no-randomuuid.mjs");
145-
writeFileSync(scriptPath, script);
146-
147-
try {
148-
const result = execSync(`node ${scriptPath}`, {
149-
encoding: "utf-8",
150-
cwd: join(__dirname, "..", ".."),
151-
}).trim();
152-
153-
expect(result).toMatch(UUID_REGEX);
154-
} catch (error) {
155-
// If the test fails, it might be because we can't properly mock the environment
156-
console.warn("Could not test BSON fallback with partial crypto:", error);
157-
}
158-
});
15935
});

vitest.config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ export default defineConfig({
4747
test: {
4848
name: "unit-and-integration",
4949
include: ["**/*.test.ts"],
50-
exclude: [...vitestDefaultExcludes, "scripts/**", "tests/accuracy/**", ...longRunningTests],
50+
exclude: [
51+
...vitestDefaultExcludes,
52+
"scripts/**",
53+
"tests/accuracy/**",
54+
"tests/browser/**",
55+
...longRunningTests,
56+
],
5157
},
5258
},
5359
{

0 commit comments

Comments
 (0)