Skip to content

Commit

Permalink
unit tests for new evaluation and evaluation detail page (#866)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhendi authored Dec 25, 2024
1 parent 48409e6 commit a45d4f2
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import EvaluationDetail from '../../evaluations/EvaluationDetail.vue'

const createResponse = (data, errorMsg = null) => ({
data: { value: { data } },
error: { value: errorMsg ? { msg: errorMsg } : null }
})

const mockApiResponses = {
'/tags?scope=dataset&category=evaluation': createResponse([
{ name: 'tag1', show_name: 'Tag 1' }
]),
'/evaluations/1': createResponse({
task_name: 'task1',
submit_time: '2021-10-01',
repo_ids: ['repo1'],
task_desc: 'desc1',
datasets: [{ repo_id: 'namespace1/repo1', tags: [{ name: 'tag1' }] }],
download_url: 'test.zip'
})
}

vi.mock('../../../packs/useFetchApi', () => ({
default: (url) => ({
json: () => Promise.resolve(mockApiResponses[url] || createResponse([]))
})
}))

describe('EvaluationDetail', () => {
let wrapper

beforeEach(() => {
wrapper = mount(EvaluationDetail, {
props: {
evaluationId: '1'
}
})
})

it('renders evaluation details correctly', async () => {
const dateElement = wrapper.find('.text-gray-700.text-base')
expect(wrapper.find('.text-2xl').text()).toBe('task1')
expect(dateElement.text()).toBe('2021-10-01')
expect(wrapper.text()).includes('desc1')
})

it('displays download button when download_url is present', async () => {
const downloadBtn = wrapper.find('a.btn-primary')
expect(downloadBtn.exists()).toBe(true)
expect(downloadBtn.attributes('href')).toBe('test.zip')
})

it('groups datasets by categories correctly', async () => {
const groupedDatasets = wrapper.vm.groupedDatasets
expect(groupedDatasets).toHaveLength(1)
expect(groupedDatasets[0].name).toBe('tag1')
expect(groupedDatasets[0].datasets[0]).toBe('repo1')
})

it('formats table data correctly', async () => {
wrapper.vm.evaluationResult = {
summary: {
column: [{ key: 'score', customizeRender: true }],
data: [
{
dataset: 'dataset1',
metric: 'metric1',
score: '0.95'
}
]
}
}
wrapper.vm.scoresKey = 'score'
await wrapper.vm.$nextTick()

const tableData = wrapper.vm.getTableData('all')
expect(tableData).toHaveLength(1)
expect(tableData[0]).toEqual({
dataset: 'dataset1',
metric: 'metric1',
score: '0.95'
})
})
})
158 changes: 158 additions & 0 deletions frontend/src/components/__tests__/evaluations/NewEvaluation.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import NewEvaluation from '../../evaluations/NewEvaluation.vue'

const MOCK_USERNAME = 'testuser'
const MOCK_MODEL_PATH = `${MOCK_USERNAME}/testmodel`
const MOCK_DATASET_PATH = `${MOCK_USERNAME}/testdataset`
const MOCK_CLUSTER_ID = 'cluster1'
const MOCK_EVALUATION_PATH = `${MOCK_USERNAME}/testevaluation`

const MOCK_RESOURCE = {
id: 1,
order_detail_id: 1,
name: 'gpu-resource',
type: 'gpu',
is_available: true,
pay_mode: 'minute',
price: 1000
}

const MOCK_TRANSFORMED_RESOURCE = {
label: 'all.minutePay',
options: [
{
id: 1,
order_detail_id: 1,
name: 'gpu-resource',
type: 'gpu',
is_available: true,
pay_mode: 'minute',
price: 1000,
label: 'gpu-resource 10all.hourUnit'
}
]
}

const createResponse = (data, errorMsg = null) => ({
data: { value: { data } },
error: { value: errorMsg ? { msg: errorMsg } : null }
})

const mockApiResponses = {
[`/runtime_framework/models?search=test&deploy_type=4`]: createResponse([
{ path: MOCK_MODEL_PATH }
]),
'/cluster': createResponse([
{ cluster_id: MOCK_CLUSTER_ID, region: 'region1' }
]),
[`/space_resources?cluster_id=${MOCK_CLUSTER_ID}`]: createResponse([
MOCK_RESOURCE
]),
[`/models/${MOCK_MODEL_PATH}/runtime_framework?deploy_type=4`]:
createResponse([{ id: 1, frame_name: 'pytorch' }]),
'/tags?scope=dataset&category=evaluation': createResponse([
{ name: 'tag1', show_name: 'Tag 1' }
]),
'/datasets?tag_category=runtime_framework&tag_name=pytorch&tag_category=evaluation&tag_name=tag1':
createResponse([{ path: MOCK_DATASET_PATH }])
}

vi.mock('../../../stores/UserStore', () => ({
default: () => ({ username: MOCK_USERNAME })
}))

vi.mock('../../../packs/useFetchApi', () => ({
default: (url) => ({
json: () => Promise.resolve(mockApiResponses[url] || createResponse([])),
post: () => ({
json: () =>
Promise.resolve(createResponse({ path: MOCK_EVALUATION_PATH }))
})
})
}))

describe('NewEvaluation', () => {
let wrapper

beforeEach(() => {
vi.clearAllMocks()
wrapper = mount(NewEvaluation)
})

describe('Initial State', () => {
it('mounts and initializes with shared resource type', () => {
expect(wrapper.exists()).toBe(true)
expect(wrapper.vm.dataForm.evaluation_resource_type).toBe('shared')
})

it('loads initial data', async () => {
await wrapper.vm.$nextTick()
expect(wrapper.vm.evaluationClusters.length).toBeGreaterThan(0)
})
})

describe('Form Validation', () => {
const validateForm = () => {
return new Promise((resolve) => {
wrapper.vm.$refs.dataFormRef.validate((valid) => resolve(valid))
})
}

it('fails validation with empty required fields', async () => {
expect(await validateForm()).toBe(false)
})

it('fails validation with invalid model_id format', async () => {
wrapper.vm.dataForm.model_id = 'invalid/format/'
await wrapper.vm.$nextTick()
expect(await validateForm()).toBe(false)
})
})

describe('Resource Type UI', () => {
const getClusterSelect = () => wrapper.find('[data-test="cluster-select"]')

it('shows/hides dedicated resource fields based on type selection', async () => {
wrapper.vm.dataForm.evaluation_resource_type = 'dedicated'
await wrapper.vm.$nextTick()
expect(getClusterSelect().isVisible()).toBe(true)

wrapper.vm.dataForm.evaluation_resource_type = 'shared'
await wrapper.vm.$nextTick()
expect(getClusterSelect().isVisible()).toBe(false)
})
})

describe('API Interactions', () => {
it('fetches and formats models', async () => {
await wrapper.vm.fetchModels('test')
expect(wrapper.vm.models).toEqual([
{ key: MOCK_MODEL_PATH, value: MOCK_MODEL_PATH }
])
})

it('fetches resources for selected cluster', async () => {
wrapper.vm.dataForm.evaluation_cluster = MOCK_CLUSTER_ID
await wrapper.vm.fetchResources()
expect(wrapper.vm.evaluationResources).toEqual([
MOCK_TRANSFORMED_RESOURCE
])
})
})

describe('Form Submission', () => {
it('submits form and redirects on success', async () => {
wrapper.vm.dataForm = {
name: 'Test Evaluation',
model_id: MOCK_MODEL_PATH,
evaluation_framework: 1,
evaluation_dataset: [MOCK_DATASET_PATH],
evaluation_resource_type: 'shared'
}

await wrapper.vm.handleSubmit()
expect(window.location.href).toBe('/resource-console')
})
})
})
1 change: 1 addition & 0 deletions frontend/src/components/evaluations/NewEvaluation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
:label="t('evaluation.new.cluster')"
class="w-full"
prop="evaluation_cluster"
data-test="cluster-select"
v-show="dataForm.evaluation_resource_type === 'dedicated'"
>
<el-select
Expand Down
26 changes: 16 additions & 10 deletions makefile
Original file line number Diff line number Diff line change
@@ -1,54 +1,60 @@
.PHONY: setup run build clean

# 默认目标
# default
all: run

# 设置项目
# setup project
setup: setup-go setup-frontend

# 设置Go环境
# setup go environment
setup-go:
@echo "Setting up Go environment..."
go mod tidy
go install github.com/cosmtrek/air@latest

# 设置前端环境
# setup frontend environment
setup-frontend:
@echo "Setting up frontend environment..."
cd frontend && yarn install

# 运行项目
# run project
run:
@echo "Starting the application..."
@make -j2 run-frontend run-backend

# 运行前端
# run frontend
run-frontend:
@echo "Starting frontend..."
cd frontend && yarn dev_build

# 运行后端
# test frontend
test-frontend:
@echo "Testing frontend..."
cd frontend && yarn test

# run backend
run-backend:
@echo "Starting backend..."
air

# 构建项目
# build project
build:
@echo "Building the project..."
cd frontend && yarn build
go build -o csghub-portal ./cmd/csghub-portal

# 清理项目
# clean project
clean:
@echo "Cleaning up..."
rm -f csghub-portal

# 帮助信息
# help message
help:
@echo "Available commands:"
@echo " make setup - Set up the project (Go and frontend)"
@echo " make setup_go - Set up the project (Go)"
@echo " make setup_frontend - Set up the project (frontend)"
@echo " make test_frontend - Test the frontend"
@echo " make run - Run the application (frontend and backend)"
@echo " make build - Build the project"
@echo " make clean - Clean up build artifacts and dependencies"
Expand Down

0 comments on commit a45d4f2

Please sign in to comment.