Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests for new evaluation and evaluation detail page #866

Merged
merged 1 commit into from
Dec 25, 2024
Merged
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
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
Loading