diff --git a/.github/workflows/webapp-integration-tests.yml b/.github/workflows/webapp-integration-tests.yml new file mode 100644 index 000000000..c0dfa9846 --- /dev/null +++ b/.github/workflows/webapp-integration-tests.yml @@ -0,0 +1,43 @@ + +name: Run Webapp Integration Tests + +on: + pull_request: + branches: [ "develop" ] + +jobs: + integration-tests: + name: Build app and run integration tests + runs-on: ubuntu-latest + env: + TERM: xterm + permissions: + contents: read + security-events: write + actions: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup firefox + id: setup-firefox + uses: browser-actions/setup-firefox@v1 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set environment variables + run: ./setemptyenv.sh + + - name: Install webapp dependencies + working-directory: ./webapp + run: npm install + + - name: run ./install.sh + run: ./install.sh + + - name: run integration tests + working-directory: ./webapp + run: npm run test:integration \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index ca5afdfbc..05e8165fd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: container_name: unstructured-api environment: - PORT=9500 - image: localhost:5000/unstructured-api + image: localhost:9500/unstructured-api docker_rabbitmq: image: rabbitmq:3.13.1-management diff --git a/setemptyenv.sh b/setemptyenv.sh new file mode 100755 index 000000000..e0f0316a3 --- /dev/null +++ b/setemptyenv.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + + +# Function to copy .env.example to .env +copy_env_example() { + # Check if .env.example exists + if [ ! -f ".env.example" ]; then + echo ".env.example file does not exist." + exit 1 + fi + + # Copy contents of .env.example to .env + cp .env.example .env + echo ".env file has been created with the contents of .env.example." +} + +# Navigate to the ./webapp directory +cd ./webapp || { + echo "Failed to navigate to ./webapp. Directory does not exist."; + exit 1; +} + +copy_env_example + +cd ../agent-backend || { + echo "Failed to navigate to ../agent-backend. Directory may not exist"; + exit 1; +} + +copy_env_example + +cd ../vector-db-proxy || { + echo "Failed to navigate to ../vector-db-proxy. Directory may not exist"; + exit 1; +} + +copy_env_example \ No newline at end of file diff --git a/vector-db-proxy/.env.example b/vector-db-proxy/.env.example new file mode 100644 index 000000000..435e511c8 --- /dev/null +++ b/vector-db-proxy/.env.example @@ -0,0 +1,21 @@ +HOST=0.0.0.0 +PORT=9001 +MONGO_URI=mongodb://docker_mongo:27017 +MONGO_DB_NAME=test +RABBITMQ_HOST=docker_rabbitmq +RABBITMQ_PORT=5672 +RABBITMQ_STREAM=streaming +RABBITMQ_EXCHANGE=agentcloud +RABBITMQ_ROUTING_KEY=key +RABBITMQ_USERNAME=guest +RABBITMQ_PASSWORD=guest +QDRANT_URI=http://qdrant:6334 +QDRANT_HOST=http://qdrant +QDRANT_PORT=6334 +REDIS_HOST=docker_redis +REDIS_PORT=6379 +WEBAPP_HOST=webapp_next +THREAD_PERCENTAGE_UTILISATION=0.8 +USE_GPU=false +LOGGING_LEVEL=warn +UNSTRUCTURED_API_URL=http://unstructured-api:9500/general/v0/general \ No newline at end of file diff --git a/webapp/.env.example b/webapp/.env.example index 502e29676..b7a9bd2a3 100644 --- a/webapp/.env.example +++ b/webapp/.env.example @@ -59,6 +59,8 @@ STRIPE_ADDON_USERS_PRODUCT_ID= STRIPE_ADDON_STORAGE_PRODUCT_ID= STRIPE_WEBHOOK_SECRET= STRIPE_ACCOUNT_SECRET= +SKIP_EMAIL=1 +SKIP_STRIPE=1 NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= WEBAPP_TEST_BASE_URL=http://localhost:3000 NEXT_PUBLIC_LOCAL_UNSTRUCTURED=1 diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 28bb59faf..4c9d9aaa2 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -99,6 +99,7 @@ "socket.io-client": "^4.7.2", "stripe": "^14.25.0", "tailwind-merge": "^2.4.0", + "tailwind-scrollbar": "^3.1.0", "tsconfig-paths": "^4.2.0", "uuid": "^9.0.1" }, @@ -19801,7 +19802,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz", "integrity": "sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==", - "dev": true, "engines": { "node": ">=12.13.0" }, diff --git a/webapp/src/lib/middleware/auth/checksession.ts b/webapp/src/lib/middleware/auth/checksession.ts index 35fd45170..32e23d794 100644 --- a/webapp/src/lib/middleware/auth/checksession.ts +++ b/webapp/src/lib/middleware/auth/checksession.ts @@ -4,7 +4,7 @@ import { dynamicResponse } from '@dr'; export default function checkSession(req, res, next) { if (!res.locals.account?._id && !res.locals.isAgentBackend) { - // console.log("checkSession, reslocals: ", res.locals); + console.log('checkSession, reslocals: ', res.locals); if (res.locals.isSocket) { return res?.locals?.socket?.disconnect(); } else { diff --git a/webapp/src/lib/middleware/auth/fetchsession.ts b/webapp/src/lib/middleware/auth/fetchsession.ts index 33261f6ba..55e05ee35 100644 --- a/webapp/src/lib/middleware/auth/fetchsession.ts +++ b/webapp/src/lib/middleware/auth/fetchsession.ts @@ -7,6 +7,7 @@ const log = debug('webapp:session'); export default async function fetchSession(req, res, next) { // log('req.session:', req.session); if (req.session && (req.session.accountId || req.session.passport?.user)) { + log('req.session.account', req.session.accountId); let account: Account; if (req.session.accountId) { account = await getAccountById(req.session.accountId); @@ -14,7 +15,7 @@ export default async function fetchSession(req, res, next) { const { oauthId, provider } = req.session.passport?.user; account = await getAccountByOAuthOrEmail(oauthId, provider, null); } - // log('account:', account); + log('account:', account); if (account) { res.locals.account = { _id: account._id.toString(), diff --git a/webapp/src/test/account.ts b/webapp/src/test/account.ts index 401b28750..3fe9f71d5 100644 --- a/webapp/src/test/account.ts +++ b/webapp/src/test/account.ts @@ -43,7 +43,7 @@ beforeAll(async () => { describe('account tests', () => { - test('register new accounts', async () => { + test.only('register new accounts', async () => { let response = await fetch(`${process.env.WEBAPP_TEST_BASE_URL}/forms/account/register`, { method: 'POST', headers: { @@ -94,7 +94,7 @@ describe('account tests', () => { await db.db().collection('accounts').deleteMany({ email: accountDetails.account3_email }); //delete these accounts so we can stress test logins in the next test }, 60 * SECONDS); //extended timeout due to multiple account creations - test('stress test registration with many accounts', async () => { + test.only('stress test registration with many accounts', async () => { const accounts = [ { name: accountDetails.account1_name, email: accountDetails.account1_email, password: accountDetails.account1_password }, { name: accountDetails.account2_name, email: accountDetails.account2_email, password: accountDetails.account2_password }, @@ -131,7 +131,7 @@ describe('account tests', () => { //TODO: refactor this to do it with a loop - test('login as new users - 11 logins', async () => { + test.only('login as new users - 11 logins', async () => { let response = await fetch(`${process.env.WEBAPP_TEST_BASE_URL}/forms/account/login`, { method: 'POST', headers: { @@ -300,7 +300,7 @@ describe('account tests', () => { }, 60 * SECONDS); // extended timeout due to multiple account logins - test('get account', async () => { + test.only('get account', async () => { const url = `${process.env.WEBAPP_TEST_BASE_URL}/account.json`; const accounts = [ { email: accountDetails.account1_email, sessionCookie: sessionCookie1 }, @@ -353,7 +353,7 @@ describe('account tests', () => { //test with valid token?? //sets the role to prevent redirects to onboarding in further tests - test('set role - onboarding', async () => { + test.only('set role - onboarding', async () => { const accounts = [ accountDetails.account1_email, accountDetails.account2_email, diff --git a/webapp/src/test/agents.ts b/webapp/src/test/agents.ts new file mode 100644 index 000000000..cceea4074 --- /dev/null +++ b/webapp/src/test/agents.ts @@ -0,0 +1,327 @@ +import { afterAll, beforeAll, describe, expect, test } from '@jest/globals'; +import * as db from '../db/index'; +import { addTeam, getTeamById, getTeamWithMembers } from '../db/team'; +import { getAccountByEmail, setStripeCustomerId, setStripePlan } from '../db/account'; +import { SubscriptionPlan } from '../lib/struct/billing'; +import { getInitialData, makeFetch, fetchTypes, accountDetails, setInitialData, updateAllAccountCsrf } from './helpers'; +import dotenv from 'dotenv'; +import { URLSearchParams } from 'url'; +import toObjectId from '../lib/misc/toobjectid'; +import { ModelList, ModelType } from '../lib/struct/model'; +import { ShareLinkTypes } from '../lib/struct/sharelink'; +import { getToolsByTeam } from "../db/tool"; +import { TeamRoles } from "../lib/permissions/roles" +import {Retriever} from '../lib/struct/tool'; +import path from 'path'; +import { defaultChunkingOptions } from '../lib/misc/defaultchunkingoptions'; + + +beforeAll(()=>{ + updateAllAccountCsrf(); +}) + +describe("Agents Tests", () => { + test.only("Can't add agent with empty modelid", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + + const teamTools = await getToolsByTeam(account1Object.resourceSlug); + const toolIds = teamTools.map(tool => (tool._id)) + + let body, url, response, responseJson; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/add` + body = { + toolIds, + name: "AddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId: '', //emptymodelId + } + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account1_email, body); + expect(response.status).toBe(400); + }); + + test.only("Can't add agent with invalid modelid", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + + const teamTools = await getToolsByTeam(account1Object.resourceSlug); + const toolIds = teamTools.map(tool => (tool._id)) + + let body, url, response, responseJson; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/add` + body = { + toolIds, + name: "AddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId: 'aaaaaaaaaaaaaaaaaaaaaaaa', //this modelId doesn't exist + } + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account1_email, body); + expect(response.status).toBe(400); + }); + + test.only("add with valid modelId", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + let body, url, response, responseJson; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/add`; + body = { + name: 'testModel1', + model: 'gpt-4o', + config: { + model: 'gpt-4o', + api_key: 'abcdefg' + }, + type: ModelType.OPENAI + }; + + const addModelResponse = await makeFetch( + url, + fetchTypes.POST, + accountDetails.account1_email, + body + ); + + expect(addModelResponse.status).toBe(200); + responseJson = await addModelResponse.json(); + expect(responseJson?._id).toBeDefined(); + const modelId = responseJson._id; //the added item ID of the model + + const teamTools = await getToolsByTeam(account1Object.resourceSlug); + const toolIds = teamTools.map(tool => (tool._id)) + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/add` + body = { + toolIds, + name: "AddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId + } + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account1_email, body); + expect(response.status).toBe(200); //successfully add model + responseJson = await response.json(); + expect(responseJson?._id).toBeDefined(); //make sure that the 200 response isn't a redirect to login or any other response than a success for adding agent + + //clean up tests by removing agent and model that were added + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/${responseJson._id}`; + body={ + agentId: responseJson._id + }; + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); + expect(response.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/${modelId}`; + body = { + modelId + } + + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); + expect(response.status).toBe(200); + }); + + test.only("can't add agent with invalid permissions", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + let body, url, response, responseJson; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/add`; + body = { + name: 'testModel1', + model: 'gpt-4o', + config: { + model: 'gpt-4o', + api_key: 'abcdefg' + }, + type: ModelType.OPENAI + }; + + const addModelResponse = await makeFetch( + url, + fetchTypes.POST, + accountDetails.account1_email, + body + ); + + expect(addModelResponse.status).toBe(200); + responseJson = await addModelResponse.json(); + expect(responseJson?._id).toBeDefined(); + const modelId = responseJson._id; //the added item ID of the model + + const teamTools = await getToolsByTeam(account1Object.resourceSlug); + const toolIds = teamTools.map(tool => (tool._id)) + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/add` + body = { + toolIds, + name: "AddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId + } + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account1_email, body); + expect(response.status).toBe(200); //successfully add model + responseJson = await response.json(); + expect(responseJson?._id).toBeDefined(); //make sure that the 200 response isn't a redirect to login or any other response than a success for adding agent + }); + + test.only("Update an agent", async ()=>{ + + }); + + test.only("Can't add agent without permissions", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + const account11Object = await getInitialData(accountDetails.account11_email); + let body, url, response, responseJson; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/add`; + body = { + name: 'testModel1', + model: 'gpt-4o', + config: { + model: 'gpt-4o', + api_key: 'abcdefg' + }, + type: ModelType.OPENAI + }; + + const addModelResponse = await makeFetch( + url, + fetchTypes.POST, + accountDetails.account1_email, + body + ); + + expect(addModelResponse.status).toBe(200); + responseJson = await addModelResponse.json(); + expect(responseJson?._id).toBeDefined(); + const modelId = responseJson._id; //the added item ID of the model + + const teamTools = await getToolsByTeam(account1Object.resourceSlug); + const toolIds = teamTools.map(tool => (tool._id)) + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/add` + body = { + toolIds, + name: "AddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId + } + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account11_email, body); //account 11 doesn't have permissions to add an agent to this team, recall teams tests, 10 members are invited (10 total in the team), 11 can't be invited due to subscription restrictions + responseJson = await response.json(); + expect(response.status).toBe(200); //This is a successful redirect to the welcome page NOT a successful creation of the agent + expect(responseJson?.redirect).toBe('/welcome?noaccess=true'); //redirects to the welcome page to gracefully handle invalid permission + + //clean up tests by removing agent and model that were added + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/${modelId}`; + body = { + modelId + } + + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); //delete the model from earlier + expect(response.status).toBe(200); + + }); + + test.only("Can't edit agent without permissions", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + const account11Object = await getInitialData(accountDetails.account11_email); + let body, url, response, responseJson; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/add`; + body = { + name: 'testModel1', + model: 'gpt-4o', + config: { + model: 'gpt-4o', + api_key: 'abcdefg' + }, + type: ModelType.OPENAI + }; + + const addModelResponse = await makeFetch( + url, + fetchTypes.POST, + accountDetails.account1_email, + body + ); + + expect(addModelResponse.status).toBe(200); + responseJson = await addModelResponse.json(); + expect(responseJson?._id).toBeDefined(); + const modelId = responseJson._id; //the added item ID of the model + + const teamTools = await getToolsByTeam(account1Object.resourceSlug); + const toolIds = teamTools.map(tool => (tool._id)) + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/add` + body = { + toolIds, + name: "AddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId + } + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account1_email, body); //account 11 doesn't have permissions to add an agent to this team, recall teams tests, 10 members are invited (10 total in the team), 11 can't be invited due to subscription restrictions + responseJson = await response.json(); + expect(response.status).toBe(200); //This is a successful redirect to the welcome page NOT a successful creation of the agent + expect(responseJson?._id).toBeDefined(); + + const addedAgentId = responseJson?._id; + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/${addedAgentId}/edit`; + body = { + toolIds, + name: "editedAddBasicAgent", + role: "AddBasicAgent", + goal: "AddBasicAgent", + backstory: "AddBasicAgent", + modelId + }; + + response = await makeFetch(url, fetchTypes.POST, accountDetails.account11_email, body); + responseJson = await response.json(); + expect(response.status).toBe(200); //this is a successful redirect to the welcome page + expect(responseJson?.redirect).toBe('/welcome?noaccess=true'); //ensure redirect path is correct + + //clean up tests by removing agent and model that were added + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/${modelId}`; + body = { + modelId + } + + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); //delete the model from earlier + expect(response.status).toBe(200); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/${addedAgentId}`; + body = { + agentId: addedAgentId + } + + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); //delete the model from earlier + responseJson = await response.json(); + expect(response.status).toBe(200); + + }); + + test.only("Edit an agent with invalid body", async ()=>{ + + }); + + test.only("Add multiple agents", async ()=>{ + + }); + + test.only("Get agents (agents.json)", async ()=>{ + + }); + +}) \ No newline at end of file diff --git a/webapp/src/test/datasources.ts b/webapp/src/test/datasources.ts new file mode 100644 index 000000000..3a2cf586a --- /dev/null +++ b/webapp/src/test/datasources.ts @@ -0,0 +1,134 @@ +import { afterAll, beforeAll, describe, expect, test } from '@jest/globals'; +import * as db from '../db/index'; +import { addTeam, getTeamById, getTeamWithMembers } from '../db/team'; +import { getAccountByEmail, setStripeCustomerId, setStripePlan } from '../db/account'; +import { SubscriptionPlan } from '../lib/struct/billing'; +import { getInitialData, makeFetch, fetchTypes, accountDetails, setInitialData, updateAllAccountCsrf, wait } from './helpers'; +import dotenv from 'dotenv'; +import { URLSearchParams } from 'url'; +import toObjectId from '../lib/misc/toobjectid'; +import { ModelList, ModelType } from '../lib/struct/model'; +import { ShareLinkTypes } from '../lib/struct/sharelink'; +import { getToolsByTeam } from "../db/tool"; +import { TeamRoles } from "../lib/permissions/roles" +import {Retriever} from '../lib/struct/tool'; +import path from 'path'; +import { defaultChunkingOptions } from '../lib/misc/defaultchunkingoptions'; +//look in components/DropZone to see how the multipart form is created and how the file is uploaded +//use Fast Embed to reduce token usage for us and to also +//use a self hosted runner in git to automate the tests into the PR process +beforeAll(()=>{ + updateAllAccountCsrf(); +}) + +const SECONDS = 1000; + +describe("Datasource Tests", () => { + + test.only("Upload a file", async ()=>{ + const account1Object = await getInitialData(accountDetails.account1_email); + //create model to be used for embedding + let body; + let url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/add`; + let config = { + model: 'fast-bge-small-en', + api_key: 'abcdefg' + } + body = { + name: 'testModel1', + model: 'fast-bge-small-en', + config: config, + type: ModelType.FASTEMBED + }; + + let response = await makeFetch( + url, + fetchTypes.POST, + accountDetails.account1_email, + body + ); + + expect(response.status).toBe(200); + + let responseJson = await response.json(); + + expect(responseJson?._id).toBeDefined(); + + const modelId = responseJson?._id; + + const formData = new FormData(); + const fs = require('fs'); + const chunkingConfig = defaultChunkingOptions + + formData.set('resourceSlug', account1Object.resourceSlug as string); + formData.set('modelId', modelId as string); + formData.set('datasourceDescription', "File Upload Test Datasource"); + formData.set('name', "TestSource"); + formData.set('retriever', Retriever.RAW as string); + formData.set('_csrf', account1Object.csrfToken as string); + const filepath = path.resolve(__dirname, 'fileUpload.txt'); + const file = fs.readFileSync(filepath); + + formData.append('file', new Blob([file]), 'uploadTest.txt') + Object.entries(chunkingConfig).forEach(([key, value]) => { + if (value != null) { + formData.set(key, value as string); + } + }); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/datasource/upload`; + + response = await fetch(url, { + headers: { + cookie : account1Object.sessionCookie + }, + method: 'POST', + body: formData + }) + + expect(response.status).toBe(200); + + responseJson = await response.json(); + + + expect(responseJson?.datasourceId).toBeDefined(); + const datasourceId = responseJson.datasourceId; + + // //TODO: find a better way to wait for embedding to be completed + // let ready=false; + // while(!ready){ + // url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/datasource/${datasourceId}.json` + // response = await makeFetch(url, fetchTypes.GET, accountDetails.account1_email); + // responseJson = await response.json(); + // if(responseJson?.status === "ready"){ + // ready = true; + // break; + // } + // else{ + // wait(1 * SECONDS); //wait 1 second if the datasource isn't ready + // } + // }; + + + //once the datasource is classed as ready then delete it to clean up the db and the vector-db + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/datasource/${datasourceId}`; + body={ + datasourceId + }; + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); + expect(response.status).toBe(200); + }, 60 * SECONDS); + + //AIRBYTE TESTS + //get connections.json + + //getSpecification + + //getJobsList + + //getDatasourceSchema + + test.only("Make Connection", async () => {//make a connection with airbyte, connection credentials will probably have to be set in .env + + }) +}); \ No newline at end of file diff --git a/webapp/src/test/fileUpload.txt b/webapp/src/test/fileUpload.txt new file mode 100644 index 000000000..b8d9b25c0 --- /dev/null +++ b/webapp/src/test/fileUpload.txt @@ -0,0 +1 @@ +Simon Says: "1+1 actually equals 4 :D" \ No newline at end of file diff --git a/webapp/src/test/helpers.ts b/webapp/src/test/helpers.ts index 40360893a..a27bb2420 100644 --- a/webapp/src/test/helpers.ts +++ b/webapp/src/test/helpers.ts @@ -1,5 +1,6 @@ let accountInformationMap: Map = new Map(); +let objectIds: string[]; export enum fetchTypes { POST = 'POST', GET = 'GET', @@ -90,4 +91,8 @@ export async function updateAllAccountCsrf() { const { sessionCookie } = await getInitialData(accountDetails[accountStr]); setInitialData(accountDetails[accountStr], { accountData: accountJson, sessionCookie }); }; +} + +export async function wait(ms: number) { + return new Promise( resolve => setTimeout(resolve, ms) ); } \ No newline at end of file diff --git a/webapp/src/test/integration.test.ts b/webapp/src/test/integration.test.ts index f2d27859d..c95a4837c 100644 --- a/webapp/src/test/integration.test.ts +++ b/webapp/src/test/integration.test.ts @@ -2,5 +2,7 @@ import './account'; import './pages'; import './teams'; import './models'; +import './datasources'; +import './agents'; import './logout'; //this MUST be last \ No newline at end of file diff --git a/webapp/src/test/logout.ts b/webapp/src/test/logout.ts index 7a1944619..453f15a13 100644 --- a/webapp/src/test/logout.ts +++ b/webapp/src/test/logout.ts @@ -34,13 +34,13 @@ afterAll(async () => { accountDetails.account11_email ] } - }); + }); await db.client().close(); }); describe("log out and wrap up tests", ()=>{ - test('log out', async () => { + test.only('log out', async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account1_email ); @@ -57,7 +57,7 @@ describe("log out and wrap up tests", ()=>{ expect(response.status).toBe(200); }); - test('cant get account with invalidated session cookie', async () => { + test.only('cant get account with invalidated session cookie', async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account1_email ); diff --git a/webapp/src/test/models.ts b/webapp/src/test/models.ts index 668fe089e..ac5e77f10 100644 --- a/webapp/src/test/models.ts +++ b/webapp/src/test/models.ts @@ -3,7 +3,7 @@ import * as db from '../db/index'; import { addTeam, getTeamById, getTeamWithMembers } from '../db/team'; import { getAccountByEmail, setStripeCustomerId, setStripePlan } from '../db/account'; import { SubscriptionPlan } from '../lib/struct/billing'; -import { getInitialData, makeFetch, fetchTypes, accountDetails, setInitialData, updateAllAccountCsrf } from './helpers'; +import { getInitialData, makeFetch, fetchTypes, accountDetails, setInitialData, updateAllAccountCsrf} from './helpers'; import dotenv from 'dotenv'; import { URLSearchParams } from 'url'; import toObjectId from '../lib/misc/toobjectid'; @@ -58,13 +58,14 @@ describe('Model Tests', () => { expect(addModelResponseJson?.error).toBeDefined(); }); - test("Test valid models with the FREE plan", async () => { + test.only("Test valid models with the FREE plan", async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account3_email ); + let body; let url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; - let body = { + body = { name: 'testModel1', model: 'gpt-4o', config: { @@ -82,17 +83,28 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + const addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson?._id).toBeDefined(); + + //clean up db with model delete + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + } + let response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(response.status).toBe(200); }); - test("Test valid embedding models with the FREE plan", async () => { + test.only("Test valid embedding models with the FREE plan", async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account3_email ); + let addModelResponseJson, body, addModelResponse, url, deleteModelResponse; - let url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; - let body = { + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1-FREE-EMBED', model: 'text-embedding-3-small', config: { @@ -102,7 +114,7 @@ describe('Model Tests', () => { type: ModelType.OPENAI }; - let addModelResponse = await makeFetch( + addModelResponse = await makeFetch( url, fetchTypes.POST, accountDetails.account3_email, @@ -110,6 +122,20 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson?._id).toBeDefined(); + + //clean up after test + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + + body = { name: 'testModel2-FREE-EMBED', model: 'text-embedding-3-large', @@ -128,6 +154,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson?._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel3-FREE-EMBED', model: 'text-embedding-ada-002', @@ -146,6 +183,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson?._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + }); @@ -160,10 +208,10 @@ describe('Model Tests', () => { const plan = SubscriptionPlan.PRO; setStripeCustomerId(initialData?.accountData?.account?._id, stripeCustomerId); setStripePlan(stripeCustomerId, plan); + let url, body, addModelResponse, addModelResponseJson; - - let url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; - let body = { + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1', model: 'gemini-1.5-pro', config: { @@ -173,27 +221,28 @@ describe('Model Tests', () => { type: ModelType.GOOGLE_AI }; - const addModelResponse = await makeFetch( + addModelResponse = await makeFetch( url, fetchTypes.POST, accountDetails.account3_email, body ); - const addModelResponseJson = await addModelResponse.json(); + addModelResponseJson = await addModelResponse.json(); expect(addModelResponse.status).toBe(403); expect(addModelResponseJson?.error).toBeDefined(); }) - test("Test valid models with PRO plan", async () => { + test.only("Test valid models with PRO plan", async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account3_email ); + let addModelResponseJson, addModelResponse, url, body, deleteModelResponse; - let url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; - let body = { + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1', model: 'claude-3-5-sonnet-20240620', config: { @@ -203,7 +252,7 @@ describe('Model Tests', () => { type: ModelType.ANTHROPIC }; - const addModelResponse = await makeFetch( + addModelResponse = await makeFetch( url, fetchTypes.POST, accountDetails.account3_email, @@ -212,15 +261,26 @@ describe('Model Tests', () => { expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + }); //switch the plan to TEAMS, test adding valid models that are off limits for FREE and for PRO and add custom models - test("Test all models with TEAMS plan", async ()=> { + test.only("Test all models with TEAMS plan", async ()=> { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account3_email ); - + let addModelResponseJson, url, body, addModelResponse, deleteModelResponse; //set org to pro plan const stripeCustomerId = initialData?.accountData?.account?.stripe?.stripeCustomerId || ""; const plan = SubscriptionPlan.TEAMS; @@ -228,19 +288,19 @@ describe('Model Tests', () => { setStripePlan(stripeCustomerId, plan); - let url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; let config = { model: 'gpt-4o-mini', api_key: 'abcdefg' } - let body = { + body = { name: 'testModel1', model: 'gpt-4o-mini', config: config, type: ModelType.OPENAI }; - let addModelResponse = await makeFetch( + addModelResponse = await makeFetch( url, fetchTypes.POST, accountDetails.account3_email, @@ -248,6 +308,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + config = { model: 'fast-bge-small-en', api_key: 'abcdefg' @@ -267,6 +338,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + let ollamaConfig = { model: 'llama2', api_key: 'abcdefg', @@ -287,6 +369,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1', model: 'claude-3-5-sonnet-20240620', @@ -305,6 +398,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + const groqConfig = { groq_api_key: 'abcdegs', model: 'llama3-70b-8192' @@ -325,6 +429,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1', model: 'gemini-1.5-pro', @@ -347,6 +462,17 @@ describe('Model Tests', () => { body ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1', model: 'gemini-1.5-pro', @@ -365,6 +491,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + body = { name: 'testModel1', model: 'gpt-4o-mini', @@ -388,6 +525,17 @@ describe('Model Tests', () => { ); expect(addModelResponse.status).toBe(200); + addModelResponseJson = await addModelResponse.json(); + expect(addModelResponseJson._id).toBeDefined(); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/${addModelResponseJson._id}`; + body = { + modelId: addModelResponseJson._id + }; + deleteModelResponse = await makeFetch(url, fetchTypes.DELETE, accountDetails.account3_email, body); + expect(deleteModelResponse.status).toBe(200); + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/model/add`; + }) diff --git a/webapp/src/test/teams.ts b/webapp/src/test/teams.ts index 6b7236f76..489a77627 100644 --- a/webapp/src/test/teams.ts +++ b/webapp/src/test/teams.ts @@ -33,17 +33,15 @@ describe('team tests', () => { }); //when debugging or creating tests, mark this test as ".only" to ensure a second team is created, this team is used in future. - test('add new team with correct stripe permissions', async () => { + test.only('add new team with correct stripe permissions', async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account1_email ); const stripeCustomerId = 'sk_12345'; //testing flags set stripe customer ID to null, need to set it to set the plan const plan = SubscriptionPlan.TEAMS; - setStripeCustomerId(initialData?.accountData?.account?._id, stripeCustomerId); - setStripePlan(stripeCustomerId, plan); - - const newAccount = await getAccountByEmail(accountDetails.account1_email); + await setStripeCustomerId(initialData?.accountData?.account?._id, stripeCustomerId); + await setStripePlan(stripeCustomerId, plan); const url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/team/add`; const body = { @@ -62,7 +60,7 @@ describe('team tests', () => { }); - test('Inviting existing account to team', async () => { + test.only('Inviting existing account to team', async () => { const { initialData, sessionCookie, resourceSlug, csrfToken } = await getInitialData( accountDetails.account1_email ); @@ -116,7 +114,7 @@ describe('team tests', () => { expect(responseJson?.team?.members.length).toBe(3); }); - test('testing TEAM_MEMBER permissions', async () => { + test.only('testing TEAM_MEMBER permissions', async () => { const account1Object = await getInitialData( //account1 is the ORG_ADMIN accountDetails.account1_email @@ -191,9 +189,26 @@ describe('team tests', () => { const addAgentResponse = await response.json(); expect(addAgentResponse?.redirect).toBeDefined(); expect(addAgentResponse?._id).toBeDefined(); + + + //clean up the db by deleting all the added elements + + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/agent/${addAgentResponse._id}`; + body={ + agentId: addAgentResponse._id + } + response = await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); + expect(response.status).toBe(200); + url = `${process.env.WEBAPP_TEST_BASE_URL}/${account1Object.resourceSlug}/forms/model/${addModelResponseJson._id}`; + body={ + modelId: addModelResponseJson._id + } + response= await makeFetch(url, fetchTypes.DELETE, accountDetails.account1_email, body); + expect(response.status).toBe(200); + }); - test('testing TEAM_ADMIN permissions', async () => { + test.only('testing TEAM_ADMIN permissions', async () => { const account1Object = await getInitialData(accountDetails.account1_email); const account2Object = await getInitialData(accountDetails.account2_email); const account3Object = await getInitialData(accountDetails.account3_email); @@ -248,7 +263,7 @@ describe('team tests', () => { // const responseJson = await response.json(); }); - test('removing TEAM_ADMIN from team, reinviting them again as a TEAM_MEMBER and testing permissions', async () => { + test.only('removing TEAM_ADMIN from team, reinviting them again as a TEAM_MEMBER and testing permissions', async () => { const account1Object = await getInitialData(accountDetails.account1_email); const account2Object = await getInitialData(accountDetails.account2_email); const account3Object = await getInitialData(accountDetails.account3_email); @@ -310,7 +325,7 @@ describe('team tests', () => { expect(responseJson?.error).toBe("Missing permission \"Add Team Member\"");//make sure it's a permissions error and not a stripe error etc... }); - test('cant add more than 10 members to TEAMS subscriptions plan', async () => { + test.only('cant add more than 10 members to TEAMS subscriptions plan', async () => { const { resourceSlug } = await getInitialData(accountDetails.account1_email); const url = `${process.env.WEBAPP_TEST_BASE_URL}/${resourceSlug}/forms/team/invite`; const accounts = [ @@ -340,6 +355,5 @@ describe('team tests', () => { } } - const teamMembers = await getTeamWithMembers(resourceSlug); }); });