Skip to content

Commit

Permalink
feat: Extracted reusable functions (#105)
Browse files Browse the repository at this point in the history
* feat: Extracted reusable functions

* added license header to qg-config files

* feat: Extracted status check, performed a small refactoring

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat: Formatted the files

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
sandualbu and pre-commit-ci[bot] authored Dec 13, 2024
1 parent 5474899 commit 22e7e3b
Show file tree
Hide file tree
Showing 11 changed files with 1,168 additions and 1,029 deletions.
175 changes: 52 additions & 123 deletions qg-api-service/qg-api-service/integration-tests/explanations.int-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { RunService } from '../src/namespace/run/run.service'
import { OpenAIService } from '../src/gp-services/openai.service'
import { handlers } from './mocks'
import { NamespaceTestEnvironment, NestTestingApp, NestUtil } from './util'
import {
NamespaceTestEnvironment,
NestTestingApp,
NestUtil,
completeRun,
createConfig,
expectStatus,
postRun,
} from './util'
import path from 'path'
import { readFile } from 'fs/promises'
import { Run, RunResult, RunStatus } from '../src/namespace/run/run.entity'
import { RunResult } from '../src/namespace/run/run.entity'
import { Prompt, getTokenLength } from '../src/gp-services/openai.utils'
import { ChatCompletion } from 'openai/resources'
import { SecretStorage } from '../src/namespace/secret/secret-storage.service'
Expand All @@ -31,42 +39,52 @@ describe('Explanations', () => {

let apiToken
let testNamespace: NamespaceTestEnvironment
const testName = 'Explanations (Integration Test)'
let testContext

const openai_testYAML = {
filepath: path.join(__dirname, 'mocks', 'openai-test.yaml'),
filename: 'openai-test.yaml',
contentType: 'multipart/form-data',
}

const openai_testJSON = {
filepath: path.join(__dirname, 'mocks', 'openai-test.json'),
filename: 'openai-test.json',
contentType: 'multipart/form-data',
}

const onekTokens = {
filepath: path.join(__dirname, 'mocks', '1k.yaml'),
filename: '1k.yaml',
contentType: 'multipart/form-data',
}
const onekTokens_2 = {
filepath: path.join(__dirname, 'mocks', '1k.yaml'),
filename: '1k_2.yaml',
contentType: 'multipart/form-data',
}

const fourkTokens = {
filepath: path.join(__dirname, 'mocks', '4k.yaml'),
filename: '4k.yaml',
contentType: 'multipart/form-data',
}
const fourkTokens_2 = {
filepath: path.join(__dirname, 'mocks', '4k.yaml'),
filename: '4k_2.yaml',
contentType: 'multipart/form-data',
}
const qg_config_large = {
filepath: path.join(__dirname, 'mocks', 'qg-config-large-openai.yaml'),
filename: 'qg-config.yaml',
contentType: 'multipart/form-data',
}

const qg_config_small = {
filepath: path.join(__dirname, 'mocks', 'qg-config-small-openai.yaml'),
filename: 'qg-config.yaml',
contentType: 'multipart/form-data',
}

const files = [
Expand Down Expand Up @@ -179,6 +197,12 @@ describe('Explanations', () => {
).mockImplementation(() =>
Promise.resolve('Cool logs\nOverall result: GREEN'),
)

testContext = {
nestTestingApp: nestTestingApp,
testNamespace: testNamespace,
apiToken: apiToken,
}
})

afterEach(async () => {
Expand All @@ -187,113 +211,17 @@ describe('Explanations', () => {
vi.restoreAllMocks()
})

async function createConfiguration(
files: {
filepath: string
filename: string
}[],
): Promise<void> {
const httpServer = await nestTestingApp.app.getHttpServer()

const response = await supertest
.agent(httpServer)
.post(`/api/v1/namespaces/${testNamespace.namespace.id}/configs`)
.send({ name: 'Explanations (Integration Test)' })
.set('Authorization', `Bearer ${apiToken}`)
.set('Content-Type', 'application/json')
.expect(HttpStatus.CREATED)
const configId = response.body.id

for (const file of files) {
const fileContent = await readFile(file.filepath)
await supertest
.agent(httpServer)
.post(
`/api/v1/namespaces/${testNamespace.namespace.id}/configs/${configId}/files`,
)
.field('filename', file.filename)
.attach('content', fileContent, {
filename: file.filename,
contentType: 'multipart/form-data',
})
.set('Authorization', `Bearer ${apiToken}`)
.expect(HttpStatus.CREATED)
}
return configId
}

async function postRun(body: any): Promise<number> {
const httpServer = await nestTestingApp.app.getHttpServer()

const response = await supertest
.agent(httpServer)
.post(`/api/v1/namespaces/${testNamespace.namespace.id}/runs`)
.send(body)
.set('Authorization', `Bearer ${apiToken}`)
.expect(HttpStatus.ACCEPTED)
expect(
response.body.id,
`The id of created run does not exist`,
).toBeDefined()
expect(
response.headers.location.endsWith(`${response.body.id}`),
`The location header of created run is not as expected`,
).toBeTruthy()
expect(
response.body.status,
`The status of created run is not as expected, it is ${response.body.status}`,
).oneOf([RunStatus.Running, RunStatus.Pending])
expect(
response.body.config,
`The config ref of created run is not as expected, it is ${response.body.config}`,
).match(/^.*\/namespaces\/\d+\/configs\/\d+$/)

return response.body.id
}
async function awaitPending(runId: number): Promise<void> {
let run = await getRun(runId)
while (run.body.status === RunStatus.Pending) {
await new Promise((resolve) => setTimeout(resolve, 1000))
run = await getRun(runId)
}
}

async function getRun(runId: number): Promise<any> {
return await supertest
.agent(nestTestingApp.app.getHttpServer())
.get(`/api/v1/namespaces/${testNamespace.namespace.id}/runs/${runId}`)
.set('Authorization', `Bearer ${apiToken}`)
.expect(HttpStatus.OK)
}

async function completeRun(runId: number, overallResult: RunResult) {
await awaitPending(runId)
await getRun(runId)

// mark run as completed
await nestTestingApp.repositories.runRepository
.createQueryBuilder()
.update(Run)
.set({
status: RunStatus.Completed,
overallResult: overallResult,
completionTime: new Date(),
})
.where('id = :id', { id: runId })
.execute()
}

describe(
'GET explainer under token limit',
async () => {
const files = [openai_testYAML, openai_testJSON, qg_config_small]

it('should be defined', async () => {
const body = {
configId: await createConfiguration(files),
configId: await createConfig(testContext, testName, files),
}
const runId = await postRun(body)
await completeRun(runId, RunResult.Green)
const runId = await postRun(testContext, body)
await completeRun(testContext, runId, RunResult.Green)

const httpServer = nestTestingApp.app.getHttpServer()
const result = await supertest
Expand All @@ -304,7 +232,7 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.OK)
expectStatus(result, HttpStatus.OK)
expect(result.body.explanation).toBeDefined()
})

Expand All @@ -325,20 +253,21 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.NOT_FOUND)
expectStatus(result, HttpStatus.NOT_FOUND)
})

it('should throw UnprocessableEntityException when YAML data import fails', async () => {
const body = {
configId: await createConfiguration([
configId: await createConfig(testContext, testName, [
{
filepath: path.join(__dirname, 'mocks', '1k.yaml'),
filename: '1k.yaml',
contentType: 'multipart/form-data',
},
]),
}
const runId = await postRun(body)
await completeRun(runId, RunResult.Green)
const runId = await postRun(testContext, body)
await completeRun(testContext, runId, RunResult.Green)

vi.spyOn(
nestTestingApp.testingModule.get<RunService>(RunService),
Expand All @@ -356,15 +285,15 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.UNPROCESSABLE_ENTITY)
expectStatus(result, HttpStatus.UNPROCESSABLE_ENTITY)
})

it('should throw UnprocessableEntityException when a non-existing chapter is selected from the QG result', async () => {
const body = {
configId: await createConfiguration(files),
configId: await createConfig(testContext, testName, files),
}
const runId = await postRun(body)
await completeRun(runId, RunResult.Green)
const runId = await postRun(testContext, body)
await completeRun(testContext, runId, RunResult.Green)

const chapter = 'non-exisiting-chapter'

Expand All @@ -377,15 +306,15 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.UNPROCESSABLE_ENTITY)
expectStatus(result, HttpStatus.UNPROCESSABLE_ENTITY)
})

it('should throw UnprocessableEntityException when a non-existing requirement is selected from the QG result', async () => {
const body = {
configId: await createConfiguration(files),
configId: await createConfig(testContext, testName, files),
}
const runId = await postRun(body)
await completeRun(runId, RunResult.Green)
const runId = await postRun(testContext, body)
await completeRun(testContext, runId, RunResult.Green)

const requirement = 'non-exisiting-requirement'

Expand All @@ -398,15 +327,15 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.UNPROCESSABLE_ENTITY)
expectStatus(result, HttpStatus.UNPROCESSABLE_ENTITY)
})

it('should throw UnprocessableEntityException when a non-existing check is selected from the QG result', async () => {
const body = {
configId: await createConfiguration(files),
configId: await createConfig(testContext, testName, files),
}
const runId = await postRun(body)
await completeRun(runId, RunResult.Green)
const runId = await postRun(testContext, body)
await completeRun(testContext, runId, RunResult.Green)

const check = 'non-exisiting-check'

Expand All @@ -419,7 +348,7 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.UNPROCESSABLE_ENTITY)
expectStatus(result, HttpStatus.UNPROCESSABLE_ENTITY)
})

it('should not exceed the token limit', async () => {
Expand All @@ -433,10 +362,10 @@ describe('Explanations', () => {
fourkTokens_2,
]
const body = {
configId: await createConfiguration(files),
configId: await createConfig(testContext, testName, files),
}
const runId = await postRun(body)
await completeRun(runId, RunResult.Green)
const runId = await postRun(testContext, body)
await completeRun(testContext, runId, RunResult.Green)

const httpServer = nestTestingApp.app.getHttpServer()
const result = await supertest
Expand All @@ -447,7 +376,7 @@ describe('Explanations', () => {
.set('Authorization', `Bearer ${apiToken}`)
.set('Accept', 'application/json')

expect(result.status).toBe(HttpStatus.OK)
expectStatus(result, HttpStatus.OK)
expect(result.body.explanation).toBeDefined()
})
},
Expand Down
Loading

0 comments on commit 22e7e3b

Please sign in to comment.