// test/routes.test.js
const fetch = require("node-fetch");
const { expect } = require("chai");
const http = require("http");
let port = process.env.TEST_PORT;
require("dotenv").config();
const domain = process.env.SERVER_DOMAIN;
port = port || process.env.SERVER_PORT;
const server_address = process.env.SERVER_ADDRESS;
const schema = process.env.TEST_SCHEMA || process.env.SERVER_SCHEMA;
const baseUrl = `${schema}://${server_address}:${port}`;
// Create a proper HTTP agent
const httpAgent = new http.Agent({
keepAlive: true,
maxSockets: 10,
timeout: 10000,
});
describe(`API route status tests with dependencies at ${baseUrl}`, () => {
let serverOnline = false;
let routes = [];
// Clean up the agent after all tests
after(() => {
if (httpAgent.destroy) {
httpAgent.destroy();
}
});
it("should confirm server is online via /health", async () => {
const res = await fetch(baseUrl + "/health", {
agent: httpAgent,
timeout: 5000,
method: "HEAD",
redirect: "manual",
});
console.log("Healthcheck body: ", res.text());
expect(res.ok).to.be.true;
serverOnline = true;
});
it("should fetch routes from /sitemap.json", async function () {
if (!serverOnline) {
this.skip();
}
try {
const res = await fetch(baseUrl + "/sitemap.json", {
agent: httpAgent,
timeout: 10000,
});
// console.log("Sitemap response status:", res.status);
// console.log("Sitemap response headers:", res.headers.raw());
expect(res.ok).to.be.true;
const responseText = await res.text();
let sitemapData;
try {
sitemapData = JSON.parse(responseText);
} catch (parseError) {
console.error("JSON parse error:", parseError.message);
throw new Error(
`Invalid JSON response: ${responseText.substring(0, 200)}...`
);
}
// Extract routes from the sitemap structure
let extractedRoutes = sitemapData.sitemap || [];
// Also flatten any nested children routes
const flattenRoutes = (items) => {
let flatRoutes = [];
if (!Array.isArray(items)) {
console.log("Items is not an array:", typeof items, items);
return flatRoutes;
}
for (const item of items) {
if (item && item.loc && item.loc !== "#") {
// Skip placeholder routes
flatRoutes.push(item);
}
if (item && item.children && Array.isArray(item.children)) {
flatRoutes = flatRoutes.concat(flattenRoutes(item.children));
}
}
return flatRoutes;
};
routes = flattenRoutes(extractedRoutes);
// console.log(
// "Extracted routes:",
// routes.map((r) => r.loc || "NO_LOC")
// );
console.log("Total routes found:", routes.length);
expect(routes).to.be.an("array").that.is.not.empty;
} catch (error) {
console.error("Error in sitemap fetch test:", error.message);
throw error;
}
});
it("should return 200 for all routes, except / route which should return 301", async function () {
this.timeout(30000); // 30 second timeout for entire test
if (!serverOnline || routes.length === 0) {
this.skip();
}
for (const route of routes) {
// Skip the root route and any routes without a proper loc
if (route.loc && route.loc !== "/" && route.loc !== "#") {
const url = baseUrl + route.loc;
console.log(`Testing route: ${route.loc}`);
try {
const res = await fetch(url, {
method: "GET",
agent: httpAgent,
timeout: 10000,
});
expect(
res.status,
`Route GET ${route.loc} should return 200`
).to.equal(200);
} catch (error) {
console.error(`Error testing route ${route.loc}:`, error.message);
throw error;
}
}
}
});
// Optional: Test the root route separately if you expect it to return 301
it("should return 301 for / route", async function () {
if (!serverOnline) {
this.skip();
}
try {
const res = await fetch(baseUrl + "/", {
agent: httpAgent,
timeout: 10000,
redirect: "manual", // Don't follow redirects automatically
});
// console.log("Root route response status:", res.status);
// console.log("Root route response headers:", res.headers.raw());
expect(res.status, "Root route should return 301").to.equal(301);
} catch (error) {
console.error("Error testing root route:", error.message);
// If we get a socket hang up, the server might be closing connections
if (error.message.includes("socket hang up")) {
console.log(
"Server appears to be closing connections. Skipping this test."
);
this.skip();
} else {
throw error;
}
}
});
});