Files
supabase/apps/docs/app/api/graphql/route.test.ts
Charis c740f5ade0 feat(docs): content api allow get requests for caching (#41443)
Content API requests were all accepted via POST (as detailed in the
GraphQL spec) and not amenable to caching.

Now, requests are also accepted via GET (as optionally allowed for query
requests only), and cached.

Also sent the request method to Logflare so we can track the ratio of
GET/POST requests.
2025-12-17 11:46:02 -05:00

172 lines
5.0 KiB
TypeScript

import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('~/lib/logger', async () => {
const actual = await vi.importActual<typeof import('~/lib/logger')>('~/lib/logger')
return {
...actual,
sendToLogflare: vi.fn(),
}
})
import { GET, POST } from './route'
describe('/api/graphql POST basic error statuses', () => {
beforeAll(() => {
vi.spyOn(console, 'error').mockImplementation(() => {})
})
afterAll(() => {
vi.restoreAllMocks()
})
beforeEach(() => {
vi.clearAllMocks()
vi.spyOn(console, 'error').mockImplementation(() => {})
})
it('should return error if request body is not valid JSON', async () => {
const request = new Request('http://localhost/api/graphql', {
method: 'POST',
body: 'not json',
})
const response = await POST(request)
const json = await response.json()
expect(json.errors[0].message).toBe('Invalid request: Request body must be valid JSON')
})
it('should return error if request body is missing required fields', async () => {
const request = new Request('http://localhost/api/graphql', {
method: 'POST',
body: JSON.stringify({ variables: {} }),
})
const response = await POST(request)
const json = await response.json()
expect(json.errors[0].message).toContain(
'Invalid request: GraphQL request payload must be valid GraphQL request object'
)
})
it('should return error if query is not a string', async () => {
const request = new Request('http://localhost/api/graphql', {
method: 'POST',
body: JSON.stringify({ query: 123 }),
})
const response = await POST(request)
const json = await response.json()
expect(json.errors[0].message).toContain(
'Invalid request: GraphQL request payload must be valid GraphQL request object'
)
})
it('should return error for internal server errors', async () => {
const request = new Request('http://localhost/api/graphql', {
method: 'POST',
body: JSON.stringify({ query: 'query { hello }' }),
})
vi.spyOn(request, 'json').mockImplementation(() => {
throw new Error('Unexpected error')
})
const response = await POST(request)
const json = await response.json()
expect(json.errors[0].message).toBe('Internal Server Error')
})
})
describe('/api/graphql schema snapshot', () => {
it('should match snapshot', async () => {
const schemaQuery = `
query {
schema
}
`
const request = new Request('http://localhost/api/graphql', {
method: 'POST',
body: JSON.stringify({ query: schemaQuery }),
})
const response = await POST(request)
const json = await response.json()
expect(json.errors).toBeUndefined()
const {
data: { schema },
} = json
expect(schema).toMatchSnapshot()
})
})
describe('/api/graphql GET support', () => {
beforeAll(() => {
vi.spyOn(console, 'error').mockImplementation(() => {})
})
afterAll(() => {
vi.restoreAllMocks()
})
it('should return error if query parameter is missing', async () => {
const request = new Request('http://localhost/api/graphql', {
method: 'GET',
})
const response = await GET(request)
const json = await response.json()
expect(json.errors[0].message).toContain(
'Invalid request: GraphQL request payload must be valid GraphQL request object'
)
})
it('should return error if variables parameter is not valid JSON', async () => {
const url = new URL('http://localhost/api/graphql')
url.searchParams.set('query', '{ schema }')
url.searchParams.set('variables', '{not json}')
const request = new Request(url, {
method: 'GET',
})
const response = await GET(request)
const json = await response.json()
expect(json.errors[0].message).toBe(
'Invalid request: Variables query parameter must be valid JSON'
)
})
it('should not allow mutations on GET requests', async () => {
const url = new URL('http://localhost/api/graphql')
url.searchParams.set('query', 'mutation { __typename }')
const request = new Request(url, {
method: 'GET',
})
const response = await GET(request)
const json = await response.json()
expect(
json.errors.some((error: { message: string }) =>
error.message.includes('GET requests may only execute query operations')
)
).toBe(true)
})
it('should return schema via GET and set cache headers', async () => {
const schemaQuery = 'query { schema }'
const url = new URL('http://localhost/api/graphql')
url.searchParams.set('query', schemaQuery)
const request = new Request(url, {
method: 'GET',
})
const response = await GET(request)
const json = await response.json()
expect(json.errors).toBeUndefined()
const cacheControl = response.headers.get('Cache-Control')
expect(cacheControl).toBe('public, s-maxage=3600, stale-while-revalidate=300')
expect(json.data.schema).toBeDefined()
})
})