import { GET } from "@/app/api/rss/route" import { generateRssFeed } from "@/lib/rss" import { describe, it, expect, vi, beforeEach } from "vitest" // Mock the RSS library vi.mock("@/lib/rss", () => ({ generateRssFeed: vi.fn(), })) describe("/api/rss", () => { beforeEach(() => { vi.clearAllMocks() }) const mockRssFeed = ` Test RSS Feed Test Description https://example.com Test Article Test article description https://example.com/articles/test Mon, 01 Jan 2024 00:00:00 GMT ` describe("GET /api/rss", () => { it("successfully generates and returns RSS feed", async () => { vi.mocked(generateRssFeed).mockResolvedValue(mockRssFeed) const consoleLogSpy = vi .spyOn(console, "log") .mockImplementation(() => {}) const response = await GET(new Request("http://localhost:3000/api/rss")) expect(response.status).toBe(200) expect(response.headers.get("Content-Type")).toBe("application/xml") expect(response.headers.get("Cache-Control")).toBe( "public, s-maxage=3600, stale-while-revalidate=1800" ) const responseText = await response.text() expect(responseText).toBe(mockRssFeed) expect(generateRssFeed).toHaveBeenCalledTimes(1) expect(consoleLogSpy).toHaveBeenCalledWith( "RSS feed generated successfully" ) consoleLogSpy.mockRestore() }) it("handles RSS generation errors gracefully", async () => { const error = new Error("Failed to read content files") vi.mocked(generateRssFeed).mockRejectedValue(error) const consoleErrorSpy = vi .spyOn(console, "error") .mockImplementation(() => {}) const response = await GET(new Request("http://localhost:3000/api/rss")) expect(response.status).toBe(500) const responseText = await response.text() expect(responseText).toBe( "Error generating RSS feed: Failed to read content files" ) expect(consoleErrorSpy).toHaveBeenCalledWith( "Error generating RSS feed:", error ) expect(consoleErrorSpy).toHaveBeenCalledWith("Error details:", { message: "Failed to read content files", stack: error.stack, name: "Error", }) consoleErrorSpy.mockRestore() }) it("handles non-Error objects thrown during generation", async () => { const stringError = "String error message" vi.mocked(generateRssFeed).mockRejectedValue(stringError) const consoleErrorSpy = vi .spyOn(console, "error") .mockImplementation(() => {}) const response = await GET(new Request("http://localhost:3000/api/rss")) expect(response.status).toBe(500) const responseText = await response.text() expect(responseText).toBe("Error generating RSS feed: Unknown error") expect(consoleErrorSpy).toHaveBeenCalledWith( "Error generating RSS feed:", stringError ) consoleErrorSpy.mockRestore() }) it("returns proper content type header for XML", async () => { vi.mocked(generateRssFeed).mockResolvedValue(mockRssFeed) const response = await GET(new Request("http://localhost:3000/api/rss")) expect(response.headers.get("Content-Type")).toBe("application/xml") }) it("sets appropriate cache control headers", async () => { vi.mocked(generateRssFeed).mockResolvedValue(mockRssFeed) const response = await GET(new Request("http://localhost:3000/api/rss")) const cacheControl = response.headers.get("Cache-Control") expect(cacheControl).toBe( "public, s-maxage=3600, stale-while-revalidate=1800" ) }) it("handles empty RSS feed content", async () => { vi.mocked(generateRssFeed).mockResolvedValue("") const response = await GET(new Request("http://localhost:3000/api/rss")) expect(response.status).toBe(200) const responseText = await response.text() expect(responseText).toBe("") }) it("logs successful RSS generation", async () => { vi.mocked(generateRssFeed).mockResolvedValue(mockRssFeed) const consoleLogSpy = vi .spyOn(console, "log") .mockImplementation(() => {}) await GET(new Request("http://localhost:3000/api/rss")) expect(consoleLogSpy).toHaveBeenCalledWith( "RSS feed generated successfully" ) consoleLogSpy.mockRestore() }) it("provides detailed error logging for Error objects", async () => { const error = new Error("Detailed error message") error.name = "CustomError" error.stack = "Error stack trace" vi.mocked(generateRssFeed).mockRejectedValue(error) const consoleErrorSpy = vi .spyOn(console, "error") .mockImplementation(() => {}) await GET(new Request("http://localhost:3000/api/rss")) expect(consoleErrorSpy).toHaveBeenCalledWith( "Error generating RSS feed:", error ) expect(consoleErrorSpy).toHaveBeenCalledWith("Error details:", { message: "Detailed error message", stack: "Error stack trace", name: "CustomError", }) consoleErrorSpy.mockRestore() }) it("handles timeout errors from RSS generation", async () => { const timeoutError = new Error("Operation timed out") timeoutError.name = "TimeoutError" vi.mocked(generateRssFeed).mockRejectedValue(timeoutError) const consoleErrorSpy = vi .spyOn(console, "error") .mockImplementation(() => {}) const response = await GET(new Request("http://localhost:3000/api/rss")) expect(response.status).toBe(500) const responseText = await response.text() expect(responseText).toBe( "Error generating RSS feed: Operation timed out" ) consoleErrorSpy.mockRestore() }) it("returns valid XML structure", async () => { vi.mocked(generateRssFeed).mockResolvedValue(mockRssFeed) const response = await GET(new Request("http://localhost:3000/api/rss")) const responseText = await response.text() // Basic XML structure validation expect(responseText).toContain("") expect(responseText).toContain("") expect(responseText).toContain("") expect(responseText).toContain("") }) it("preserves RSS feed content exactly as generated", async () => { const customRssFeed = ` Custom` vi.mocked(generateRssFeed).mockResolvedValue(customRssFeed) const response = await GET(new Request("http://localhost:3000/api/rss")) const responseText = await response.text() expect(responseText).toBe(customRssFeed) }) }) })