132 lines
3.0 KiB
JavaScript
132 lines
3.0 KiB
JavaScript
const { handleSearch } = require("../src/search");
|
|
|
|
describe("handleSearch", () => {
|
|
let req;
|
|
let res;
|
|
|
|
beforeEach(() => {
|
|
req = {
|
|
query: {}
|
|
};
|
|
|
|
// using .mockReturnThis() because of chainable methods
|
|
res = {
|
|
status: jest.fn().mockReturnThis(),
|
|
send: jest.fn().mockReturnThis(),
|
|
setHeader: jest.fn()
|
|
};
|
|
});
|
|
|
|
// clean up the observed data after each test
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
test("returns 400 when query is missing", async () => {
|
|
global.fetch = jest.fn().mockResolvedValue({
|
|
json: jest.fn().mockResolvedValue({
|
|
results: [
|
|
{
|
|
url: "https://example.com",
|
|
title: "Example",
|
|
content: "Content",
|
|
irrelevant: "removed"
|
|
}
|
|
]
|
|
})
|
|
});
|
|
|
|
await handleSearch(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.send).toHaveBeenCalledWith("Missing search query");
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("fetches search results and sends filtered JSON", async () => {
|
|
req.query.query = "nodejs testing";
|
|
|
|
global.fetch = jest.fn().mockResolvedValue({
|
|
json: jest.fn().mockResolvedValue({
|
|
results: [
|
|
{
|
|
url: "https://example.com",
|
|
title: "Example",
|
|
content: "Content",
|
|
score: 0.8
|
|
}
|
|
]
|
|
})
|
|
});
|
|
|
|
await handleSearch(req, res);
|
|
|
|
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
|
|
const [url, options] = global.fetch.mock.calls[0];
|
|
|
|
expect(url).toContain("/search?");
|
|
expect(url).toContain("q=nodejs+testing");
|
|
expect(url).toContain("engines=startpage");
|
|
expect(url).toContain("format=json");
|
|
expect(url).not.toContain("score=0.8");
|
|
|
|
expect(options).toEqual({
|
|
headers: {
|
|
Accept: "application/json"
|
|
}
|
|
});
|
|
|
|
expect(res.setHeader).toHaveBeenCalledWith(
|
|
"Content-Type",
|
|
"application/json; charset=utf-8"
|
|
);
|
|
|
|
expect(res.setHeader).toHaveBeenCalledWith(
|
|
"Content-Disposition",
|
|
'attachment; filename="output.json"'
|
|
);
|
|
|
|
expect(res.send).toHaveBeenCalledWith(
|
|
JSON.stringify(
|
|
[
|
|
{
|
|
url: "https://example.com",
|
|
title: "Example",
|
|
content: "Content"
|
|
}
|
|
],
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
});
|
|
|
|
test("uses an empty array when the API omits results", async () => {
|
|
req.query.query = "empty";
|
|
|
|
global.fetch = jest.fn().mockResolvedValue({
|
|
json: jest.fn().mockResolvedValue({})
|
|
});
|
|
|
|
await handleSearch(req, res);
|
|
|
|
expect(res.send).toHaveBeenCalledWith("[]");
|
|
});
|
|
|
|
test("returns 500 when fetch fails", async () => {
|
|
req.query.query = "error";
|
|
|
|
global.fetch = jest
|
|
.fn()
|
|
.mockRejectedValue(new Error("SearxNG unavailable"));
|
|
|
|
await handleSearch(req, res);
|
|
|
|
expect(res.status).toHaveBeenCalledWith(500);
|
|
expect(res.send).toHaveBeenCalledWith(
|
|
"<p><b>Search failed!<br>See the console for the error.</b></p>"
|
|
);
|
|
});
|
|
});
|