@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// What a loader does with a refusal on TanStack Start (F10): a real 302, 404 or 403, set before anything
|
||||
// streams. Start has no `forbidden()`, so the 403 is a not-found with `data.forbidden` plus the header the
|
||||
// server entry turns into the status.
|
||||
|
||||
import { AppError } from "@conusai-kit/core"
|
||||
import { isNotFound, isRedirect } from "@tanstack/react-router"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
const start = vi.hoisted(() => ({
|
||||
setResponseStatus: vi.fn<(status: number) => void>(),
|
||||
setResponseHeader: vi.fn<(name: string, value: string) => void>(),
|
||||
}))
|
||||
vi.mock("@tanstack/react-start/server", () => start)
|
||||
|
||||
const { orFallback, PAGE_STATUS_HEADER, pageForbidden, pageNotFound, signInHref, toSignIn } = await import(
|
||||
"../apps/web/src/server/guard"
|
||||
)
|
||||
|
||||
const refusal = (status: number): AppError =>
|
||||
new AppError({ type: "/problems/refused", code: "refused", title: "Refused", status })
|
||||
const thrownBy = async (run: () => unknown): Promise<unknown> => {
|
||||
try {
|
||||
await run()
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
throw new Error("it did not throw")
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
start.setResponseStatus.mockClear()
|
||||
start.setResponseHeader.mockClear()
|
||||
})
|
||||
|
||||
describe("signInHref", () => {
|
||||
it("names where to come back to, and nothing for the home page", () => {
|
||||
expect(signInHref("/acme/general/demo/notes?x=1")).toBe(
|
||||
"/auth/sign-in?return_to=%2Facme%2Fgeneral%2Fdemo%2Fnotes%3Fx%3D1",
|
||||
)
|
||||
expect(signInHref("/")).toBe("/auth/sign-in")
|
||||
expect(signInHref("/", "denied")).toBe("/auth/sign-in?error=denied")
|
||||
})
|
||||
})
|
||||
|
||||
describe("the status pages", () => {
|
||||
it("answers a signed-out visitor with a 302 to sign in and back", async () => {
|
||||
const error = (await thrownBy(() => toSignIn("/a/b/c/notes"))) as { options: { href: string; statusCode: number } }
|
||||
expect(isRedirect(error)).toBe(true)
|
||||
expect(error.options.href).toBe("/auth/sign-in?return_to=%2Fa%2Fb%2Fc%2Fnotes")
|
||||
expect(error.options.statusCode).toBe(302)
|
||||
})
|
||||
|
||||
it("answers not found with 404, in the status and in the header the server entry applies", async () => {
|
||||
expect(isNotFound(await thrownBy(() => pageNotFound()))).toBe(true)
|
||||
expect(start.setResponseStatus).toHaveBeenCalledWith(404)
|
||||
expect(start.setResponseHeader).toHaveBeenCalledWith(PAGE_STATUS_HEADER, "404")
|
||||
})
|
||||
|
||||
it("answers forbidden with 403 and marks the not-found as the forbidden page", async () => {
|
||||
const error = (await thrownBy(() => pageForbidden())) as { data: unknown }
|
||||
expect(isNotFound(error)).toBe(true)
|
||||
expect(error.data).toEqual({ forbidden: true })
|
||||
expect(start.setResponseHeader).toHaveBeenCalledWith(PAGE_STATUS_HEADER, "403")
|
||||
})
|
||||
})
|
||||
|
||||
describe("orFallback", () => {
|
||||
it("passes an answer through", async () => {
|
||||
await expect(orFallback("/here", async () => "rows")).resolves.toBe("rows")
|
||||
expect(start.setResponseStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it.each([
|
||||
[404, "404"],
|
||||
[403, "403"],
|
||||
])("turns a procedure's %i into that page", async (status, header) => {
|
||||
const error = await thrownBy(() => orFallback("/here", () => Promise.reject(refusal(status))))
|
||||
expect(isNotFound(error)).toBe(true)
|
||||
expect(start.setResponseHeader).toHaveBeenCalledWith(PAGE_STATUS_HEADER, header)
|
||||
})
|
||||
|
||||
it("turns a procedure's 401 into the way to sign in", async () => {
|
||||
expect(isRedirect(await thrownBy(() => orFallback("/here", () => Promise.reject(refusal(401)))))).toBe(true)
|
||||
})
|
||||
|
||||
it("leaves anything else to the error boundary", async () => {
|
||||
const fault = new Error("database down")
|
||||
expect(await thrownBy(() => orFallback("/here", () => Promise.reject(fault)))).toBe(fault)
|
||||
expect(await thrownBy(() => orFallback("/here", () => Promise.reject(refusal(500))))).toBeInstanceOf(AppError)
|
||||
expect(start.setResponseStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user