feat: add tests for /api/chat (#950)

This commit is contained in:
Jeremy 2025-04-22 18:55:17 -07:00 committed by GitHub
parent 4ca93aaf89
commit a159b77fcf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 197 additions and 50 deletions

34
tests/fixtures.ts Normal file
View file

@ -0,0 +1,34 @@
import { expect as baseExpect, test as baseTest } from '@playwright/test';
import { createAuthenticatedContext, type UserContext } from './auth-helper';
interface Fixtures {
adaContext: UserContext;
babbageContext: UserContext;
}
export const test = baseTest.extend<any, Fixtures>({
adaContext: [
async ({ browser }, use) => {
const ada = await createAuthenticatedContext({
browser,
name: 'ada',
});
await use(ada);
await ada.context.close();
},
{ scope: 'worker' },
],
babbageContext: [
async ({ browser }, use) => {
const babbage = await createAuthenticatedContext({
browser,
name: 'babbage',
});
await use(babbage);
await babbage.context.close();
},
{ scope: 'worker' },
],
});
export const expect = baseExpect;

View file

@ -1,4 +1,4 @@
import { CoreMessage } from 'ai';
import type { CoreMessage } from 'ai';
export const TEST_PROMPTS: Record<string, CoreMessage> = {
USER_SKY: {

36
tests/prompts/routes.ts Normal file
View file

@ -0,0 +1,36 @@
export const TEST_PROMPTS = {
SKY: {
MESSAGES: [
{
role: 'user',
content: 'Why is the sky blue?',
parts: [{ type: 'text', text: 'Why is the sky blue?' }],
},
],
OUTPUT_STREAM: [
'0:"It\'s "',
'0:"just "',
'0:"blue "',
'0:"duh! "',
'e:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10},"isContinued":false}',
'd:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10}}',
],
},
GRASS: {
MESSAGES: [
{
role: 'user',
content: 'Why is grass green?',
parts: [{ type: 'text', text: 'Why is grass green?' }],
},
],
OUTPUT_STREAM: [
'0:"It\'s "',
'0:"just "',
'0:"green "',
'0:"duh! "',
'e:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10},"isContinued":false}',
'd:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10}}',
],
},
};

81
tests/routes/chat.test.ts Normal file
View file

@ -0,0 +1,81 @@
import { generateUUID } from '@/lib/utils';
import { expect, test } from '../fixtures';
import { TEST_PROMPTS } from '../prompts/routes';
const chatIdsCreatedByAda: Array<string> = [];
test.describe
.serial('/api/chat', () => {
test('Ada cannot invoke a chat generation with empty request body', async ({
adaContext,
}) => {
const response = await adaContext.request.post('/api/chat', {
data: {},
});
expect(response.status()).toBe(500);
const text = await response.text();
expect(text).toEqual('An error occurred while processing your request!');
});
test('Ada can invoke chat generation', async ({ adaContext }) => {
const chatId = generateUUID();
const response = await adaContext.request.post('api/chat', {
data: {
id: chatId,
messages: TEST_PROMPTS.SKY.MESSAGES,
selectedChatModel: 'chat-model',
},
});
expect(response.status()).toBe(200);
const text = await response.text();
const lines = text.split('\n');
const [_, ...rest] = lines;
expect(rest.filter(Boolean)).toEqual(TEST_PROMPTS.SKY.OUTPUT_STREAM);
chatIdsCreatedByAda.push(chatId);
});
test("Babbage cannot append message to Ada's chat", async ({
babbageContext,
}) => {
const [chatId] = chatIdsCreatedByAda;
const response = await babbageContext.request.post('api/chat', {
data: {
id: chatId,
messages: TEST_PROMPTS.GRASS.MESSAGES,
selectedChatModel: 'chat-model',
},
});
expect(response.status()).toBe(403);
const text = await response.text();
expect(text).toEqual('Forbidden');
});
test("Babbage cannot delete Ada's chat", async ({ babbageContext }) => {
const [chatId] = chatIdsCreatedByAda;
const response = await babbageContext.request.delete(
`api/chat?id=${chatId}`,
);
expect(response.status()).toBe(403);
const text = await response.text();
expect(text).toEqual('Forbidden');
});
test('Ada can delete her own chat', async ({ adaContext }) => {
const [chatId] = chatIdsCreatedByAda;
const response = await adaContext.request.delete(`api/chat?id=${chatId}`);
expect(response.status()).toBe(200);
const deletedChat = await response.json();
expect(deletedChat).toMatchObject({ id: chatId });
});
});

View file

@ -1,36 +1,14 @@
import type { Document } from '@/lib/db/schema';
import { generateUUID } from '@/lib/utils';
import {
createAuthenticatedContext,
type UserContext,
} from '@/tests/auth-helper';
import { expect, test } from '@playwright/test';
let adaContext: UserContext;
let babbageContext: UserContext;
import { expect, test } from '../fixtures';
const documentsCreatedByAda: Array<Document> = [];
test.beforeAll(async ({ browser }) => {
adaContext = await createAuthenticatedContext({
browser,
name: 'ada',
});
babbageContext = await createAuthenticatedContext({
browser,
name: 'babbage',
});
});
test.afterAll(async () => {
await adaContext.context.close();
await babbageContext.context.close();
});
test.describe
.serial('/api/document', () => {
test('Ada cannot retrieve a document without specifying an id', async () => {
test('Ada cannot retrieve a document without specifying an id', async ({
adaContext,
}) => {
const response = await adaContext.request.get('/api/document');
expect(response.status()).toBe(400);
@ -38,7 +16,9 @@ test.describe
expect(text).toEqual('Missing id');
});
test('Ada cannot retrieve a document that does not exist', async () => {
test('Ada cannot retrieve a document that does not exist', async ({
adaContext,
}) => {
const documentId = generateUUID();
const response = await adaContext.request.get(
@ -50,7 +30,7 @@ test.describe
expect(text).toEqual('Not found');
});
test('Ada can create a document', async () => {
test('Ada can create a document', async ({ adaContext }) => {
const documentId = generateUUID();
const draftDocument = {
@ -73,7 +53,7 @@ test.describe
documentsCreatedByAda.push(createdDocument);
});
test('Ada can retrieve a created document', async () => {
test('Ada can retrieve a created document', async ({ adaContext }) => {
const [document] = documentsCreatedByAda;
const response = await adaContext.request.get(
@ -88,7 +68,9 @@ test.describe
expect(retrievedDocument).toMatchObject(document);
});
test('Ada can save a new version of the document', async () => {
test('Ada can save a new version of the document', async ({
adaContext,
}) => {
const [firstDocument] = documentsCreatedByAda;
const draftDocument = {
@ -111,7 +93,9 @@ test.describe
documentsCreatedByAda.push(createdDocument);
});
test('Ada can retrieve all versions of her documents', async () => {
test('Ada can retrieve all versions of her documents', async ({
adaContext,
}) => {
const [firstDocument, secondDocument] = documentsCreatedByAda;
const response = await adaContext.request.get(
@ -128,7 +112,9 @@ test.describe
expect(secondRetrievedDocument).toMatchObject(secondDocument);
});
test('Ada cannot delete a document without specifying an id', async () => {
test('Ada cannot delete a document without specifying an id', async ({
adaContext,
}) => {
const response = await adaContext.request.delete(`/api/document`);
expect(response.status()).toBe(400);
@ -136,7 +122,9 @@ test.describe
expect(text).toEqual('Missing id');
});
test('Ada cannot delete a document without specifying a timestamp', async () => {
test('Ada cannot delete a document without specifying a timestamp', async ({
adaContext,
}) => {
const [firstDocument] = documentsCreatedByAda;
const response = await adaContext.request.delete(
@ -148,7 +136,9 @@ test.describe
expect(text).toEqual('Missing timestamp');
});
test('Ada can delete a document by specifying id and timestamp', async () => {
test('Ada can delete a document by specifying id and timestamp', async ({
adaContext,
}) => {
const [firstDocument, secondDocument] = documentsCreatedByAda;
const response = await adaContext.request.delete(
@ -163,7 +153,9 @@ test.describe
expect(deletedDocument).toMatchObject(secondDocument);
});
test('Ada can retrieve documents without deleted versions', async () => {
test('Ada can retrieve documents without deleted versions', async ({
adaContext,
}) => {
const [firstDocument] = documentsCreatedByAda;
const response = await adaContext.request.get(
@ -178,7 +170,7 @@ test.describe
expect(firstRetrievedDocument).toMatchObject(firstDocument);
});
test("Babbage cannot update Ada's document", async () => {
test("Babbage cannot update Ada's document", async ({ babbageContext }) => {
const [firstDocument] = documentsCreatedByAda;
const draftDocument = {
@ -199,7 +191,7 @@ test.describe
expect(text).toEqual('Forbidden');
});
test("Ada's documents did not get updated", async () => {
test("Ada's documents did not get updated", async ({ adaContext }) => {
const [firstDocument] = documentsCreatedByAda;
const response = await adaContext.request.get(