diff --git a/apps/docs/app/api/graphql/route.ts b/apps/docs/app/api/graphql/route.ts index c786b1c92f..36d0cadebf 100644 --- a/apps/docs/app/api/graphql/route.ts +++ b/apps/docs/app/api/graphql/route.ts @@ -26,7 +26,7 @@ function isDevGraphiQL(request: Request) { const referrer = request.headers.get('Referer') return ( IS_DEV && - origin.startsWith('http://localhost') && + origin?.startsWith('http://localhost') && referrer === `${origin}${BASE_PATH ?? ''}/graphiql` ) } diff --git a/apps/docs/app/api/graphql/tests/schema.smoke.test.ts b/apps/docs/app/api/graphql/tests/schema.smoke.test.ts new file mode 100644 index 0000000000..d5553d038a --- /dev/null +++ b/apps/docs/app/api/graphql/tests/schema.smoke.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest' + +const GRAPHQL_URL = 'https://supabase.com/docs/api/graphql' +// For dev testing: comment out above and uncomment below +// const GRAPHQL_URL = 'http://localhost:3001/docs/api/graphql' + +describe('prod smoke test: graphql: schema', () => { + it('schema query returns non-empty string', async () => { + const query = ` + query SchemaQuery { + schema + } + ` + const result = await fetch(GRAPHQL_URL, { + method: 'POST', + body: JSON.stringify({ + query, + }), + }) + + expect(result.status).toBe(200) + const { data, errors } = await result.json() + expect(errors).toBeUndefined() + + const { schema } = data + expect(schema).toBeDefined() + expect(typeof schema).toBe('string') + expect(schema.length).toBeGreaterThan(0) + }) +}) diff --git a/apps/docs/app/api/graphql/tests/searchDocs.smoke.test.ts b/apps/docs/app/api/graphql/tests/searchDocs.smoke.test.ts new file mode 100644 index 0000000000..15f1d8b5da --- /dev/null +++ b/apps/docs/app/api/graphql/tests/searchDocs.smoke.test.ts @@ -0,0 +1,194 @@ +import { describe, expect, it } from 'vitest' + +const GRAPHQL_URL = 'https://supabase.com/docs/api/graphql' +// For dev testing: comment out above and uncomment below +// const GRAPHQL_URL = 'http://localhost:3001/docs/api/graphql' + +describe('prod smoke test: graphql: searchDocs', () => { + it('searchDocs query returns results', async () => { + const query = ` + query SearchDocsQuery($query: String!) { + searchDocs(query: $query) { + nodes { + title + href + content + } + } + } + ` + const result = await fetch(GRAPHQL_URL, { + method: 'POST', + body: JSON.stringify({ query, variables: { query: 'typescript type generation' } }), + }) + + expect(result.status).toBe(200) + const { data, errors } = await result.json() + expect(errors).toBeUndefined() + + const { + searchDocs: { nodes }, + } = data + expect(Array.isArray(nodes)).toBe(true) + expect(nodes.length).toBeGreaterThan(0) + expect(nodes[0]).toHaveProperty('title') + expect(nodes[0]).toHaveProperty('href') + expect(nodes[0]).toHaveProperty('content') + }) + + it('searchDocs query includes guides', async () => { + const query = ` + query SearchDocsQuery($query: String!) { + searchDocs(query: $query) { + nodes { + ...on Guide { + title + href + content + subsections { + nodes { + title + href + content + } + } + } + } + } + } + ` + const result = await fetch(GRAPHQL_URL, { + method: 'POST', + body: JSON.stringify({ query, variables: { query: 'typescript type generation' } }), + }) + + expect(result.status).toBe(200) + const { data, errors } = await result.json() + expect(errors).toBeUndefined() + + const { + searchDocs: { nodes }, + } = data + expect(Array.isArray(nodes)).toBe(true) + expect(nodes.length).toBeGreaterThan(0) + + const guideNode = nodes.find((node: any) => !!node.title) + expect(guideNode).toBeDefined() + expect(guideNode).toHaveProperty('title') + expect(guideNode).toHaveProperty('href') + expect(guideNode).toHaveProperty('content') + expect(guideNode).toHaveProperty('subsections') + }) + + it('searchDocs query includes SDK references', async () => { + const query = ` + query SearchDocsQuery($query: String!) { + searchDocs(query: $query) { + nodes { + ...on ClientLibraryFunctionReference { + title + href + content + language + methodName + } + } + } + } + ` + const result = await fetch(GRAPHQL_URL, { + method: 'POST', + body: JSON.stringify({ query, variables: { query: 'signInWithOAuth' } }), + }) + + expect(result.status).toBe(200) + const { data, errors } = await result.json() + expect(errors).toBeUndefined() + + const { + searchDocs: { nodes }, + } = data + expect(Array.isArray(nodes)).toBe(true) + expect(nodes.length).toBeGreaterThan(0) + + const guideNode = nodes.find((node: any) => !!node.title) + expect(guideNode).toBeDefined() + expect(guideNode).toHaveProperty('title') + expect(guideNode).toHaveProperty('href') + expect(guideNode).toHaveProperty('content') + expect(guideNode).toHaveProperty('language') + expect(guideNode).toHaveProperty('methodName') + }) + + it('searchDocs query includes troubleshooting articles', async () => { + const query = ` + query SearchDocsQuery($query: String!) { + searchDocs(query: $query) { + nodes { + ...on TroubleshootingGuide { + title + href + content + } + } + } + } + ` + const result = await fetch(GRAPHQL_URL, { + method: 'POST', + body: JSON.stringify({ query, variables: { query: 'exhaust I/O' } }), + }) + + expect(result.status).toBe(200) + const { data, errors } = await result.json() + expect(errors).toBeUndefined() + + const { + searchDocs: { nodes }, + } = data + expect(Array.isArray(nodes)).toBe(true) + expect(nodes.length).toBeGreaterThan(0) + + const guideNode = nodes.find((node: any) => !!node.title) + expect(guideNode).toBeDefined() + expect(guideNode).toHaveProperty('title') + expect(guideNode).toHaveProperty('href') + expect(guideNode).toHaveProperty('content') + }) + + it('searchDocs query includes CLI references', async () => { + const query = ` + query SearchDocsQuery($query: String!) { + searchDocs(query: $query) { + nodes { + ...on CLICommandReference { + title + href + content + } + } + } + } + ` + const result = await fetch(GRAPHQL_URL, { + method: 'POST', + body: JSON.stringify({ query, variables: { query: 'supabase db reset' } }), + }) + + expect(result.status).toBe(200) + const { data, errors } = await result.json() + expect(errors).toBeUndefined() + + const { + searchDocs: { nodes }, + } = data + expect(Array.isArray(nodes)).toBe(true) + expect(nodes.length).toBeGreaterThan(0) + + const guideNode = nodes.find((node: any) => !!node.title) + expect(guideNode).toBeDefined() + expect(guideNode).toHaveProperty('title') + expect(guideNode).toHaveProperty('href') + expect(guideNode).toHaveProperty('content') + }) +}) diff --git a/apps/docs/app/api/graphql/tests/searchDocs.test.ts b/apps/docs/app/api/graphql/tests/searchDocs.test.ts index f280009e8a..1d9d3c3d03 100644 --- a/apps/docs/app/api/graphql/tests/searchDocs.test.ts +++ b/apps/docs/app/api/graphql/tests/searchDocs.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from 'vitest' +import { afterAll, describe, expect, it, vi } from 'vitest' import { Result } from '~/features/helpers.fn' import { type OpenAIClientInterface } from '~/lib/openAi' import { ApiError } from '../../utils' @@ -45,6 +45,11 @@ vi.mock('~/lib/supabase', () => ({ })) describe('/api/graphql searchDocs', () => { + afterAll(() => { + vi.doUnmock('~/lib/openAi') + vi.doUnmock('~/lib/supabase') + }) + it('should return search results when given a valid query', async () => { const searchQuery = ` query {