Skip to content
Merged

Test #168

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions lib/chat/__tests__/handleChatCompletion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ vi.mock("@/lib/supabase/rooms/selectRoom", () => ({
default: vi.fn(),
}));

vi.mock("@/lib/supabase/rooms/insertRoom", () => ({
insertRoom: vi.fn(),
vi.mock("@/lib/supabase/rooms/upsertRoom", () => ({
upsertRoom: vi.fn(),
}));

vi.mock("@/lib/supabase/memories/upsertMemory", () => ({
Expand All @@ -36,7 +36,7 @@ vi.mock("@/lib/telegram/sendErrorNotification", () => ({

import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails";
import selectRoom from "@/lib/supabase/rooms/selectRoom";
import { insertRoom } from "@/lib/supabase/rooms/insertRoom";
import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom";
import upsertMemory from "@/lib/supabase/memories/upsertMemory";
import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification";
import { generateChatTitle } from "@/lib/chat/generateChatTitle";
Expand All @@ -47,7 +47,7 @@ import type { ChatRequestBody } from "../validateChatRequest";

const mockSelectAccountEmails = vi.mocked(selectAccountEmails);
const mockSelectRoom = vi.mocked(selectRoom);
const mockInsertRoom = vi.mocked(insertRoom);
const mockUpsertRoom = vi.mocked(upsertRoom);
const mockUpsertMemory = vi.mocked(upsertMemory);
const mockSendNewConversationNotification = vi.mocked(sendNewConversationNotification);
const mockGenerateChatTitle = vi.mocked(generateChatTitle);
Expand Down Expand Up @@ -143,7 +143,7 @@ describe("handleChatCompletion", () => {

await handleChatCompletion(body, responseMessages);

expect(mockInsertRoom).toHaveBeenCalledWith(
expect(mockUpsertRoom).toHaveBeenCalledWith(
expect.objectContaining({
id: "new-room-123",
account_id: "account-123",
Expand Down Expand Up @@ -180,7 +180,7 @@ describe("handleChatCompletion", () => {

await handleChatCompletion(body, responseMessages);

expect(mockInsertRoom).not.toHaveBeenCalled();
expect(mockUpsertRoom).not.toHaveBeenCalled();
expect(mockSendNewConversationNotification).not.toHaveBeenCalled();
});
});
Expand Down Expand Up @@ -272,7 +272,7 @@ describe("handleChatCompletion", () => {
await handleChatCompletion(body, responseMessages);

// Should still create room with empty string ID (or undefined)
expect(mockInsertRoom).toHaveBeenCalled();
expect(mockUpsertRoom).toHaveBeenCalled();
});

it("handles artistId when creating room", async () => {
Expand All @@ -284,7 +284,7 @@ describe("handleChatCompletion", () => {

await handleChatCompletion(body, responseMessages);

expect(mockInsertRoom).toHaveBeenCalledWith(
expect(mockUpsertRoom).toHaveBeenCalledWith(
expect.objectContaining({
artist_id: "artist-789",
}),
Expand Down
16 changes: 8 additions & 8 deletions lib/chat/__tests__/integration/chatEndToEnd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ vi.mock("@/lib/supabase/rooms/selectRoom", () => ({
default: vi.fn(),
}));

vi.mock("@/lib/supabase/rooms/insertRoom", () => ({
insertRoom: vi.fn(),
vi.mock("@/lib/supabase/rooms/upsertRoom", () => ({
upsertRoom: vi.fn(),
}));

vi.mock("@/lib/supabase/memories/upsertMemory", () => ({
Expand Down Expand Up @@ -143,7 +143,7 @@ import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo
import { getAccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetails";
import { getKnowledgeBaseText } from "@/lib/files/getKnowledgeBaseText";
import selectRoom from "@/lib/supabase/rooms/selectRoom";
import { insertRoom } from "@/lib/supabase/rooms/insertRoom";
import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom";
import upsertMemory from "@/lib/supabase/memories/upsertMemory";
import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification";
import { handleSendEmailToolOutputs } from "@/lib/emails/handleSendEmailToolOutputs";
Expand All @@ -161,7 +161,7 @@ const mockSelectAccountInfo = vi.mocked(selectAccountInfo);
const mockGetAccountWithDetails = vi.mocked(getAccountWithDetails);
const mockGetKnowledgeBaseText = vi.mocked(getKnowledgeBaseText);
const mockSelectRoom = vi.mocked(selectRoom);
const mockInsertRoom = vi.mocked(insertRoom);
const mockUpsertRoom = vi.mocked(upsertRoom);
const mockUpsertMemory = vi.mocked(upsertMemory);
const mockSendNewConversationNotification = vi.mocked(sendNewConversationNotification);
const mockHandleSendEmailToolOutputs = vi.mocked(handleSendEmailToolOutputs);
Expand Down Expand Up @@ -193,7 +193,7 @@ describe("Chat Integration Tests", () => {
mockGetAccountWithDetails.mockResolvedValue(null);
mockGetKnowledgeBaseText.mockResolvedValue("");
mockSelectRoom.mockResolvedValue(null);
mockInsertRoom.mockResolvedValue(undefined);
mockUpsertRoom.mockResolvedValue(undefined);
mockUpsertMemory.mockResolvedValue(undefined);
mockSendNewConversationNotification.mockResolvedValue(undefined);
mockHandleSendEmailToolOutputs.mockResolvedValue(undefined);
Expand Down Expand Up @@ -433,7 +433,7 @@ describe("Chat Integration Tests", () => {

await handleChatCompletion(body as any, responseMessages as any);

expect(mockInsertRoom).toHaveBeenCalledWith(
expect(mockUpsertRoom).toHaveBeenCalledWith(
expect.objectContaining({
id: "new-room-123",
account_id: "account-123",
Expand All @@ -460,7 +460,7 @@ describe("Chat Integration Tests", () => {

await handleChatCompletion(body as any, responseMessages as any);

expect(mockInsertRoom).not.toHaveBeenCalled();
expect(mockUpsertRoom).not.toHaveBeenCalled();
expect(mockSendNewConversationNotification).not.toHaveBeenCalled();
});

Expand Down Expand Up @@ -710,7 +710,7 @@ describe("Chat Integration Tests", () => {

await handleChatCompletion(body as any, responseMessages as any);

expect(mockInsertRoom).toHaveBeenCalled();
expect(mockUpsertRoom).toHaveBeenCalled();
expect(mockUpsertMemory).toHaveBeenCalledTimes(2);

// 4. Handle credits
Expand Down
63 changes: 63 additions & 0 deletions lib/chat/__tests__/setupToolsForRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,67 @@ describe("setupToolsForRequest", () => {
expect(result).toHaveProperty("tool2");
});
});

describe("parallel execution", () => {
it("fetches MCP tools and Composio tools in parallel", async () => {
const executionOrder: string[] = [];

// Track when each operation starts and completes
mockGetMcpTools.mockImplementation(async () => {
executionOrder.push("getMcpTools:start");
await new Promise((resolve) => setTimeout(resolve, 10));
executionOrder.push("getMcpTools:end");
return { mcpTool: { description: "MCP Tool", parameters: {} } };
});

mockGetComposioTools.mockImplementation(async () => {
executionOrder.push("getComposioTools:start");
await new Promise((resolve) => setTimeout(resolve, 10));
executionOrder.push("getComposioTools:end");
return { composioTool: { description: "Composio Tool", parameters: {} } };
});

const body: ChatRequestBody = {
accountId: "account-123",
orgId: null,
authToken: "test-token-123",
messages: [{ id: "1", role: "user", content: "Hello" }],
};

await setupToolsForRequest(body);

// Both should start before either ends (parallel execution)
const mcpStartIndex = executionOrder.indexOf("getMcpTools:start");
const composioStartIndex = executionOrder.indexOf("getComposioTools:start");
const mcpEndIndex = executionOrder.indexOf("getMcpTools:end");
const composioEndIndex = executionOrder.indexOf("getComposioTools:end");

// Both operations should have started
expect(mcpStartIndex).toBeGreaterThanOrEqual(0);
expect(composioStartIndex).toBeGreaterThanOrEqual(0);

// Both starts should come before both ends
expect(mcpStartIndex).toBeLessThan(mcpEndIndex);
expect(composioStartIndex).toBeLessThan(composioEndIndex);

// At least one start should come before the other's end (proves parallelism)
const bothStartedBeforeAnyEnds =
Math.max(mcpStartIndex, composioStartIndex) < Math.min(mcpEndIndex, composioEndIndex);
expect(bothStartedBeforeAnyEnds).toBe(true);
});

it("both operations are called when authToken is provided", async () => {
const body: ChatRequestBody = {
accountId: "account-123",
orgId: null,
authToken: "test-token-123",
messages: [{ id: "1", role: "user", content: "Hello" }],
};

await setupToolsForRequest(body);

expect(mockGetMcpTools).toHaveBeenCalledTimes(1);
expect(mockGetComposioTools).toHaveBeenCalledTimes(1);
});
});
});
4 changes: 2 additions & 2 deletions lib/chat/createNewRoom.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { insertRoom } from "@/lib/supabase/rooms/insertRoom";
import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom";
import { generateChatTitle } from "@/lib/chat/generateChatTitle";
import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification";
import { UIMessage } from "ai";
Expand Down Expand Up @@ -37,7 +37,7 @@ export async function createNewRoom({
}

await Promise.all([
insertRoom({
upsertRoom({
account_id: accountId,
topic: conversationName,
artist_id: artistId || undefined,
Expand Down
4 changes: 2 additions & 2 deletions lib/chat/handleChatCompletion.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { UIMessage } from "ai";
import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails";
import selectRoom from "@/lib/supabase/rooms/selectRoom";
import { insertRoom } from "@/lib/supabase/rooms/insertRoom";
import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom";
import upsertMemory from "@/lib/supabase/memories/upsertMemory";
import { validateMessages } from "@/lib/messages/validateMessages";
import { generateChatTitle } from "@/lib/chat/generateChatTitle";
Expand Down Expand Up @@ -52,7 +52,7 @@ export async function handleChatCompletion(
const conversationName = await generateChatTitle(latestMessageText);

await Promise.all([
insertRoom({
upsertRoom({
id: roomId,
account_id: accountId,
topic: conversationName,
Expand Down
10 changes: 5 additions & 5 deletions lib/chat/setupToolsForRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import { getComposioTools } from "@/lib/composio/toolRouter";
export async function setupToolsForRequest(body: ChatRequestBody): Promise<ToolSet> {
const { accountId, roomId, excludeTools, authToken } = body;

// Only fetch MCP tools if we have an auth token
const mcpTools = authToken ? await getMcpTools(authToken) : {};

// Get Composio Tool Router tools (COMPOSIO_MANAGE_CONNECTIONS, etc.)
const composioTools = await getComposioTools(accountId, roomId);
// Fetch MCP tools and Composio tools in parallel - they're independent
const [mcpTools, composioTools] = await Promise.all([
authToken ? getMcpTools(authToken) : Promise.resolve({}),
getComposioTools(accountId, roomId),
]);

// Merge all tools
const allTools: ToolSet = {
Expand Down
30 changes: 15 additions & 15 deletions lib/chats/__tests__/createChatHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createChatHandler } from "../createChatHandler";

import { getApiKeyAccountId } from "@/lib/auth/getApiKeyAccountId";
import { validateOverrideAccountId } from "@/lib/accounts/validateOverrideAccountId";
import { insertRoom } from "@/lib/supabase/rooms/insertRoom";
import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom";
import { safeParseJson } from "@/lib/networking/safeParseJson";
import { generateChatTitle } from "../generateChatTitle";

Expand All @@ -17,8 +17,8 @@ vi.mock("@/lib/accounts/validateOverrideAccountId", () => ({
validateOverrideAccountId: vi.fn(),
}));

vi.mock("@/lib/supabase/rooms/insertRoom", () => ({
insertRoom: vi.fn(),
vi.mock("@/lib/supabase/rooms/upsertRoom", () => ({
upsertRoom: vi.fn(),
}));

vi.mock("@/lib/uuid/generateUUID", () => ({
Expand Down Expand Up @@ -61,7 +61,7 @@ describe("createChatHandler", () => {

vi.mocked(getApiKeyAccountId).mockResolvedValue(apiKeyAccountId);
vi.mocked(safeParseJson).mockResolvedValue({ artistId });
vi.mocked(insertRoom).mockResolvedValue({
vi.mocked(upsertRoom).mockResolvedValue({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -75,7 +75,7 @@ describe("createChatHandler", () => {
expect(response.status).toBe(200);
expect(json.status).toBe("success");
expect(validateOverrideAccountId).not.toHaveBeenCalled();
expect(insertRoom).toHaveBeenCalledWith({
expect(upsertRoom).toHaveBeenCalledWith({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -98,7 +98,7 @@ describe("createChatHandler", () => {
vi.mocked(validateOverrideAccountId).mockResolvedValue({
accountId: targetAccountId,
});
vi.mocked(insertRoom).mockResolvedValue({
vi.mocked(upsertRoom).mockResolvedValue({
id: "generated-uuid-123",
account_id: targetAccountId,
artist_id: artistId,
Expand All @@ -115,7 +115,7 @@ describe("createChatHandler", () => {
apiKey: "test-api-key",
targetAccountId,
});
expect(insertRoom).toHaveBeenCalledWith({
expect(upsertRoom).toHaveBeenCalledWith({
id: "generated-uuid-123",
account_id: targetAccountId,
artist_id: artistId,
Expand Down Expand Up @@ -145,7 +145,7 @@ describe("createChatHandler", () => {
expect(response.status).toBe(403);
expect(json.status).toBe("error");
expect(json.message).toBe("Access denied to specified accountId");
expect(insertRoom).not.toHaveBeenCalled();
expect(upsertRoom).not.toHaveBeenCalled();
});

it("returns 500 when validation returns API key error", async () => {
Expand All @@ -170,7 +170,7 @@ describe("createChatHandler", () => {
expect(response.status).toBe(500);
expect(json.status).toBe("error");
expect(json.message).toBe("Failed to validate API key");
expect(insertRoom).not.toHaveBeenCalled();
expect(upsertRoom).not.toHaveBeenCalled();
});
});

Expand All @@ -187,7 +187,7 @@ describe("createChatHandler", () => {
firstMessage,
});
vi.mocked(generateChatTitle).mockResolvedValue(generatedTitle);
vi.mocked(insertRoom).mockResolvedValue({
vi.mocked(upsertRoom).mockResolvedValue({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -201,7 +201,7 @@ describe("createChatHandler", () => {
expect(response.status).toBe(200);
expect(json.status).toBe("success");
expect(generateChatTitle).toHaveBeenCalledWith(firstMessage);
expect(insertRoom).toHaveBeenCalledWith({
expect(upsertRoom).toHaveBeenCalledWith({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -217,7 +217,7 @@ describe("createChatHandler", () => {
vi.mocked(safeParseJson).mockResolvedValue({
artistId,
});
vi.mocked(insertRoom).mockResolvedValue({
vi.mocked(upsertRoom).mockResolvedValue({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -229,7 +229,7 @@ describe("createChatHandler", () => {

expect(response.status).toBe(200);
expect(generateChatTitle).not.toHaveBeenCalled();
expect(insertRoom).toHaveBeenCalledWith({
expect(upsertRoom).toHaveBeenCalledWith({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -248,7 +248,7 @@ describe("createChatHandler", () => {
firstMessage,
});
vi.mocked(generateChatTitle).mockRejectedValue(new Error("AI generation failed"));
vi.mocked(insertRoom).mockResolvedValue({
vi.mocked(upsertRoom).mockResolvedValue({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand All @@ -262,7 +262,7 @@ describe("createChatHandler", () => {
expect(response.status).toBe(200);
expect(json.status).toBe("success");
expect(generateChatTitle).toHaveBeenCalledWith(firstMessage);
expect(insertRoom).toHaveBeenCalledWith({
expect(upsertRoom).toHaveBeenCalledWith({
id: "generated-uuid-123",
account_id: apiKeyAccountId,
artist_id: artistId,
Expand Down
4 changes: 2 additions & 2 deletions lib/chats/createChatHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { getCorsHeaders } from "@/lib/networking/getCorsHeaders";
import { getApiKeyAccountId } from "@/lib/auth/getApiKeyAccountId";
import { validateOverrideAccountId } from "@/lib/accounts/validateOverrideAccountId";
import { insertRoom } from "@/lib/supabase/rooms/insertRoom";
import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom";
import { generateUUID } from "@/lib/uuid/generateUUID";
import { validateCreateChatBody } from "@/lib/chats/validateCreateChatBody";
import { safeParseJson } from "@/lib/networking/safeParseJson";
Expand Down Expand Up @@ -60,7 +60,7 @@ export async function createChatHandler(request: NextRequest): Promise<NextRespo
}
}

const chat = await insertRoom({
const chat = await upsertRoom({
id: roomId,
account_id: accountId,
artist_id: artistId || null,
Expand Down
Loading