tests(api): refactor to mock database and network operations (#494)

This commit is contained in:
Danny Avila 2023-06-13 00:04:01 -04:00 committed by GitHub
parent f92e4f28be
commit 72e9828b76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 103 additions and 27 deletions

View file

@ -1,7 +1,16 @@
const mongoose = require('mongoose');
const { HumanChatMessage, AIChatMessage } = require('langchain/schema');
const ChatAgent = require('./ChatAgent');
const connectDb = require('../../lib/db/connectDb');
const Conversation = require('../../models/Conversation');
const crypto = require('crypto');
jest.mock('../../lib/db/connectDb');
jest.mock('../../models/Conversation', () => {
return function () {
return {
save: jest.fn(),
deleteConvos: jest.fn()
};
};
});
describe('ChatAgent', () => {
let TestAgent;
@ -13,26 +22,72 @@ describe('ChatAgent', () => {
max_tokens: 2
},
agentOptions: {
model: 'gpt-3.5-turbo',
model: 'gpt-3.5-turbo'
}
};
let parentMessageId;
let conversationId;
const fakeMessages = [];
const userMessage = 'Hello, ChatGPT!';
const apiKey = process.env.OPENAI_API_KEY;
beforeAll(async () => {
await connectDb();
});
const apiKey = 'fake-api-key';
beforeEach(() => {
TestAgent = new ChatAgent(apiKey, options);
});
TestAgent.loadHistory = jest
.fn()
.mockImplementation((conversationId, parentMessageId = null) => {
if (!conversationId) {
TestAgent.currentMessages = [];
return Promise.resolve([]);
}
afterAll(async () => {
// Delete the messages and conversation created by the test
await Conversation.deleteConvos(null, { conversationId });
await mongoose.connection.close();
const orderedMessages = TestAgent.constructor.getMessagesForConversation(
fakeMessages,
parentMessageId
);
const chatMessages = orderedMessages.map((msg) =>
msg?.isCreatedByUser || msg?.role.toLowerCase() === 'user'
? new HumanChatMessage(msg.text)
: new AIChatMessage(msg.text)
);
TestAgent.currentMessages = orderedMessages;
return Promise.resolve(chatMessages);
});
TestAgent.sendMessage = jest.fn().mockImplementation(async (message, opts = {}) => {
if (opts && typeof opts === 'object') {
TestAgent.setOptions(opts);
}
const conversationId = opts.conversationId || crypto.randomUUID();
const parentMessageId = opts.parentMessageId || '00000000-0000-0000-0000-000000000000';
const userMessageId = opts.overrideParentMessageId || crypto.randomUUID();
this.pastMessages = await TestAgent.loadHistory(
conversationId,
TestAgent.options?.parentMessageId
);
const userMessage = {
text: message,
sender: 'ChatGPT',
isCreatedByUser: true,
messageId: userMessageId,
parentMessageId,
conversationId
};
const response = {
sender: 'ChatGPT',
text: 'Hello, User!',
isCreatedByUser: false,
messageId: crypto.randomUUID(),
parentMessageId: userMessage.messageId,
conversationId
};
fakeMessages.push(userMessage);
fakeMessages.push(response);
return response;
});
});
test('initializes ChatAgent without crashing', () => {

View file

@ -1,8 +1,25 @@
/* eslint-disable jest/no-conditional-expect */
require('dotenv').config({ path: '../../../.env' });
const mongoose = require('mongoose');
const mockUser = {
_id: 'fakeId',
save: jest.fn(),
findByIdAndDelete: jest.fn(),
};
var mockPluginService = {
updateUserPluginAuth: jest.fn(),
deleteUserPluginAuth: jest.fn(),
getUserPluginAuthValue: jest.fn()
};
jest.mock('../../../models/User', () => {
return function() {
return mockUser;
};
});
jest.mock('../../../server/services/PluginService', () => mockPluginService);
const User = require('../../../models/User');
const connectDb = require('../../../lib/db/connectDb');
const { validateTools, loadTools, availableTools } = require('./index');
const PluginService = require('../../../server/services/PluginService');
const { BaseChatModel } = require('langchain/chat_models/openai');
@ -21,7 +38,16 @@ describe('Tool Handlers', () => {
const authConfigs = mainPlugin.authConfig;
beforeAll(async () => {
await connectDb();
mockUser.save.mockResolvedValue(undefined);
const userAuthValues = {};
mockPluginService.getUserPluginAuthValue.mockImplementation((userId, authField) => {
return userAuthValues[`${userId}-${authField}`];
});
mockPluginService.updateUserPluginAuth.mockImplementation((userId, authField, _pluginKey, credential) => {
userAuthValues[`${userId}-${authField}`] = credential;
});
fakeUser = new User({
name: 'Fake User',
username: 'fakeuser',
@ -41,17 +67,11 @@ describe('Tool Handlers', () => {
}
});
// afterEach(async () => {
// // Clean up any test-specific data.
// });
afterAll(async () => {
// Delete the fake user & plugin auth
await User.findByIdAndDelete(fakeUser._id);
await mockUser.findByIdAndDelete(fakeUser._id);
for (const authConfig of authConfigs) {
await PluginService.deleteUserPluginAuth(fakeUser._id, authConfig.authField);
}
await mongoose.connection.close();
});
describe('validateTools', () => {
@ -128,6 +148,7 @@ describe('Tool Handlers', () => {
try {
await loadTool2();
} catch (error) {
// eslint-disable-next-line jest/no-conditional-expect
expect(error).toBeDefined();
}
});