Add test coverage
This commit is contained in:
parent
f3a59f6eae
commit
4907f8d895
3 changed files with 174 additions and 16 deletions
19
deno.lock
generated
19
deno.lock
generated
|
@ -4,8 +4,10 @@
|
|||
"npm:@modelcontextprotocol/sdk@*": "1.10.2_express@5.1.0_zod@3.24.3",
|
||||
"npm:@modelcontextprotocol/sdk@^1.9.0": "1.10.2_express@5.1.0_zod@3.24.3",
|
||||
"npm:@types/express@5": "5.0.1",
|
||||
"npm:@types/node@*": "22.12.0",
|
||||
"npm:@types/node@^22.13.10": "22.15.2",
|
||||
"npm:@types/react@^19.0.12": "19.1.2",
|
||||
"npm:express@*": "4.21.2",
|
||||
"npm:express@^4.21.2": "4.21.2",
|
||||
"npm:open@^10.1.0": "10.1.1",
|
||||
"npm:prettier@^3.5.3": "3.5.3",
|
||||
|
@ -1407,21 +1409,6 @@
|
|||
"workspace": {
|
||||
"dependencies": [
|
||||
"npm:@modelcontextprotocol/sdk@*"
|
||||
],
|
||||
"packageJson": {
|
||||
"dependencies": [
|
||||
"npm:@modelcontextprotocol/sdk@^1.9.0",
|
||||
"npm:@types/express@5",
|
||||
"npm:@types/node@^22.13.10",
|
||||
"npm:@types/react@^19.0.12",
|
||||
"npm:express@^4.21.2",
|
||||
"npm:open@^10.1.0",
|
||||
"npm:prettier@^3.5.3",
|
||||
"npm:react@19",
|
||||
"npm:tsup@^8.4.0",
|
||||
"npm:tsx@^4.19.3",
|
||||
"npm:typescript@^5.8.2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
134
tests/coordination_test.ts
Normal file
134
tests/coordination_test.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
import {
|
||||
assertEquals,
|
||||
assertRejects,
|
||||
} from "std/assert/mod.ts";
|
||||
import { describe, it, afterEach, beforeEach } from "std/testing/bdd.ts";
|
||||
import { assertSpyCalls, spy, stub } from "std/testing/mock.ts";
|
||||
import {
|
||||
isPidRunning,
|
||||
isLockValid,
|
||||
waitForAuthentication,
|
||||
} from "../src/lib/coordination.ts";
|
||||
|
||||
/**
|
||||
* Basic tests for the coordination module
|
||||
*/
|
||||
describe("coordination", () => {
|
||||
describe("isPidRunning", () => {
|
||||
it("returns false when Deno.Command throws an error", async () => {
|
||||
const originalCommand = Deno.Command;
|
||||
try {
|
||||
// @ts-ignore - Replace Deno.Command with a function that throws
|
||||
Deno.Command = () => {
|
||||
throw new Error("Test error");
|
||||
};
|
||||
|
||||
const result = await isPidRunning(1234);
|
||||
assertEquals(result, false);
|
||||
} finally {
|
||||
// Restore original Command
|
||||
Deno.Command = originalCommand;
|
||||
}
|
||||
});
|
||||
|
||||
if (Deno.build.os === "linux" || Deno.build.os === "darwin") {
|
||||
it("checks if a process is running on non-Windows by using kill command", async () => {
|
||||
// Just verify it runs without error - actual functionality depends on the OS
|
||||
const result = await isPidRunning(Deno.pid);
|
||||
// Our own process should be running
|
||||
assertEquals(typeof result, "boolean");
|
||||
});
|
||||
} else if (Deno.build.os === "windows") {
|
||||
it("checks if a process is running on Windows by using tasklist command", async () => {
|
||||
// Just verify it runs without error - actual functionality depends on the OS
|
||||
const result = await isPidRunning(Deno.pid);
|
||||
// Our own process should be running
|
||||
assertEquals(typeof result, "boolean");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("isLockValid", () => {
|
||||
const mockLockData = {
|
||||
pid: 1234, // A likely non-existent PID
|
||||
port: 8000,
|
||||
timestamp: Date.now() - (31 * 60 * 1000), // Expired (31 minutes old)
|
||||
};
|
||||
|
||||
it("returns false for expired lockfile", async () => {
|
||||
const result = await isLockValid(mockLockData);
|
||||
assertEquals(result, false);
|
||||
});
|
||||
|
||||
it("returns false for non-existent process", async () => {
|
||||
// Find a PID that's unlikely to exist
|
||||
let testPid = 999999;
|
||||
while (await isPidRunning(testPid)) {
|
||||
testPid += 1000;
|
||||
}
|
||||
|
||||
const validTimestampData = {
|
||||
...mockLockData,
|
||||
pid: testPid,
|
||||
timestamp: Date.now(), // Not expired
|
||||
};
|
||||
|
||||
const result = await isLockValid(validTimestampData);
|
||||
assertEquals(result, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("waitForAuthentication", () => {
|
||||
let fetchStub: ReturnType<typeof stub>;
|
||||
let setTimeoutStub: ReturnType<typeof stub>;
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-ignore - Required for testing
|
||||
setTimeoutStub = stub(globalThis, "setTimeout", (callback: TimerHandler) => {
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
return 1 as unknown as ReturnType<typeof setTimeout>;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Clean up
|
||||
if (fetchStub) fetchStub.restore();
|
||||
setTimeoutStub.restore();
|
||||
});
|
||||
|
||||
it("returns true when authentication completes", async () => {
|
||||
// Mock fetch to simulate a successful authentication
|
||||
// @ts-ignore - Required for testing
|
||||
fetchStub = stub(globalThis, "fetch", () =>
|
||||
Promise.resolve(new Response("Auth completed", { status: 200 }))
|
||||
);
|
||||
|
||||
const result = await waitForAuthentication(8000);
|
||||
assertEquals(result, true);
|
||||
});
|
||||
|
||||
it("returns false for unexpected status", async () => {
|
||||
// Mock fetch to simulate an error response
|
||||
// @ts-ignore - Required for testing
|
||||
fetchStub = stub(globalThis, "fetch", () =>
|
||||
Promise.resolve(new Response("Error", { status: 500 }))
|
||||
);
|
||||
|
||||
const result = await waitForAuthentication(8000);
|
||||
assertEquals(result, false);
|
||||
});
|
||||
|
||||
it("returns false when fetch fails", async () => {
|
||||
// Mock fetch to simulate a network error
|
||||
// @ts-ignore - Required for testing
|
||||
fetchStub = stub(globalThis, "fetch", () =>
|
||||
Promise.reject(new Error("Network error"))
|
||||
);
|
||||
|
||||
const result = await waitForAuthentication(8000);
|
||||
assertEquals(result, false);
|
||||
});
|
||||
});
|
||||
});
|
37
tests/test-utils.ts
Normal file
37
tests/test-utils.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Test utilities for mcp-remote-deno tests
|
||||
*/
|
||||
|
||||
import type { Server } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
|
||||
/**
|
||||
* A mock server for testing
|
||||
*/
|
||||
export class MockServer {
|
||||
/**
|
||||
* The HTTP server instance
|
||||
*/
|
||||
server: Partial<Server>;
|
||||
|
||||
/**
|
||||
* A function that returns the auth code
|
||||
*/
|
||||
waitForAuthCode: () => Promise<string>;
|
||||
|
||||
/**
|
||||
* Creates a new MockServer
|
||||
* @param port The port the server is listening on
|
||||
*/
|
||||
constructor(port = 8000) {
|
||||
this.server = {
|
||||
address: () => ({
|
||||
port,
|
||||
address: "127.0.0.1",
|
||||
family: "IPv4"
|
||||
} as AddressInfo),
|
||||
// Add other server properties as needed
|
||||
};
|
||||
this.waitForAuthCode = () => Promise.resolve("mock-auth-code");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue