fastify-allow
Version:
Fastify plugin that adds an Allow header with all registered methods to GET and HEAD responses. See https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1
167 lines (150 loc) • 5.07 kB
text/typescript
import test from "tape"
import Fastify, {FastifyReply, FastifyRequest, LightMyRequestResponse} from "fastify"
import allowPlugin, {AllowOptions} from "../lib"
test("request tests, default fastify options", async (testGroup) => {
const app = Fastify()
const opts: AllowOptions = {send405: true}
// need to await as the plugin is registered on a queue
await app.register(allowPlugin, opts)
const routePath = "/:param1/things/:p2"
app.get(routePath, (req: FastifyRequest, rep: FastifyReply) => {
rep.send("\"Testing\"")
})
app.post(routePath, (req: FastifyRequest, rep: FastifyReply) => {
rep.status(201)
})
app.options("*", (req: FastifyRequest, rep: FastifyReply) => {
rep.send("\"Testing wildcards\"")
})
testGroup.test("ALLOW / 405 tests", (t) => {
const url = "/abcde/things/123?q=1"
t.plan(9)
app.inject({method: "GET", url}, (err, res) => {
t.equal(res.statusCode, 200)
t.equal(res.headers.allow, "GET, HEAD, POST, OPTIONS")
t.equal(res.body, "\"Testing\"")
})
app.inject({method: "OPTIONS", url: "/wildcard/things/anything/here"}, (err, res) => {
t.equal(res.statusCode, 200)
t.equal(res.headers.allow, "OPTIONS")
t.equal(res.body, "\"Testing wildcards\"")
})
app.inject({method: "DELETE", url}, (err, res) => {
t.equal(res.statusCode, 405)
t.equal(res.headers.allow, "GET, HEAD, POST, OPTIONS")
const data = parseBody(res)
t.deepEqual(data, {
message: `DELETE ${url} not allowed. Examine 'Allow' header for supported methods.`,
error: "Method Not Allowed",
statusCode: 405
})
})
})
testGroup.test("404 for case sensitive URL", (t) => {
t.plan(2)
const url = "/ttgg/THINGS/123"
app.inject({method: "GET", url}, (err, res) => {
t.equal(res.statusCode, 404)
const data = parseBody(res)
t.deepEqual(data, {
message: `Route GET:${url} not found`,
error: "Not Found",
statusCode: 404
})
})
})
testGroup.test("404 for trailing slash", (t) => {
t.plan(2)
const url = "/ttgg/things/123/"
app.inject({method: "GET", url}, (err, res) => {
t.equal(res.statusCode, 404)
const data = parseBody(res)
t.deepEqual(data, {
message: `Route GET:${url} not found`,
error: "Not Found",
statusCode: 404
})
})
})
testGroup.test("404 for non-handled routes", (t) => {
t.plan(2)
const url = "/no-handler/test"
app.inject({method: "GET", url}, (err, res) => {
t.equal(res.statusCode, 404)
const data = parseBody(res)
t.deepEqual(data, {
message: `Route GET:${url} not found`,
error: "Not Found",
statusCode: 404
})
})
})
})
test("send405ForWildcard, caseSensitive, ignoreTrailingSlash options set to opposite of default", async (testGroup) => {
const app = Fastify({
caseSensitive: false,
ignoreTrailingSlash: true
})
const opts: AllowOptions = {
send405: true,
send405ForWildcard: true
}
await app.register(allowPlugin, opts)
const routePath = "/:param/stuff"
app.get(routePath, (req: FastifyRequest, rep: FastifyReply) => {
rep.send("\"Testing 2\"")
})
app.options("/:param/*", (req: FastifyRequest, rep: FastifyReply) => {
rep.send()
})
testGroup.test("request tests prefix-different matches", (t) => {
const url = "/not/p1/things"
t.plan(2)
app.inject({method: "GET", url}, (err, res) => {
t.equal(res.statusCode, 405)
t.equal(res.headers.allow, "OPTIONS")
})
})
testGroup.test("request tests case insensitive matches", (t) => {
const url = "/p1/STUFF"
t.plan(3)
app.inject({method: "GET", url}, (err, res) => {
t.equal(res.statusCode, 200)
t.equal(res.headers.allow, "GET, HEAD, OPTIONS")
t.equal(res.body, "\"Testing 2\"")
})
})
testGroup.test("request tests trailing slash matches", (t) => {
const url = "/p1/stuff/"
t.plan(3)
app.inject({method: "DELETE", url}, (err, res) => {
t.equal(res.statusCode, 405)
t.equal(res.headers.allow, "GET, HEAD, OPTIONS")
const data = parseBody(res)
t.deepEqual(data, {
message: `DELETE ${url} not allowed. Examine 'Allow' header for supported methods.`,
error: "Method Not Allowed",
statusCode: 405
})
})
})
testGroup.test("request tests wildcard returns 405", (t) => {
const url = "/p1/other-stuff"
t.plan(3)
app.inject({method: "DELETE", url}, (err, res) => {
t.equal(res.statusCode, 405)
t.equal(res.headers.allow, "OPTIONS")
const data = parseBody(res)
t.deepEqual(data, {
message: `DELETE ${url} not allowed. Examine 'Allow' header for supported methods.`,
error: "Method Not Allowed",
statusCode: 405
})
})
})
})
function parseBody(res: LightMyRequestResponse): any {
return res.body.length
? JSON.parse(res.body)
: ""
}