Skip to content

Commit 4098348

Browse files
authored
Merge branch 'main' into csghub-wl-jlp
2 parents aadd742 + b3ce78c commit 4098348

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2845
-94
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ To help users get up to speed with CSGHub, we have created a demo video that hig
3838

3939
- For those looking to quickly explore, experiment with CSGHub's free SaaS version on the [OpenCSG website](https://opencsg.com). Refer to this [brief quick start guide](./docs/csghub_saas_en.md) to handle LLMs/datasets and deploy LLM applications with CSGHub SaaS interface.
4040

41-
- **For users seeking further practice:** Please refer to the [CSGHub official documentation](https://opencsg.com/docs/csghub/101/introduction). The documentation currently provides installation methods using [Docker Compose](https://opencsg.com/docs/csghub/101/install/install_csghub_by_docker) and [Helm Chart](https://opencsg.com/docs/csghub/101/install/install_csghub_by_helm).
41+
- **For users seeking further practice:** Please refer to the [CSGHub official documentation](https://opencsg.com/docs/csghub/101/introduction). The documentation currently provides installation methods using [Docker Compose](https://opencsg.com/docs/en/csghub/101/install/docker/overview) and [Helm Chart](https://opencsg.com/docs/en/csghub/101/install/kubernetes/overview).
4242

4343
_For more information and advanced tutorials, please refer to the [OpenCSG Documentation Center](https://opencsg.com/docs/intro)._
4444

README_es.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Para ayudar a los usuarios a familiarizarse rápidamente con CSGHub, hemos produ
3636

3737
- **Usuarios que desean explorar rápidamente:** Pueden experimentar la versión SaaS gratuita de CSGHub en el sitio web de [OpenCSG](https://opencsg.com). Consulte esta [guía de inicio rápido](./docs/csghub_saas_zh.md) para administrar grandes modelos y conjuntos de datos a través de la interfaz de CSGHub SaaS e implementar aplicaciones de grandes modelos.
3838

39-
- **Para usuarios que buscan más práctica:** Consulten la [documentación oficial de CSGHub](https://opencsg.com/docs/csghub/101/introduction). Actualmente, la documentación proporciona métodos de instalación con [Docker Compose](https://opencsg.com/docs/csghub/101/install/install_csghub_by_docker) y [Helm Chart](https://opencsg.com/docs/csghub/101/install/install_csghub_by_helm).
39+
- **Para usuarios que buscan más práctica:** Consulten la [documentación oficial de CSGHub](https://opencsg.com/docs/csghub/101/introduction). Actualmente, la documentación proporciona métodos de instalación con [Docker Compose](https://opencsg.com/docs/en/csghub/101/install/docker/overview) y [Helm Chart](https://opencsg.com/docs/en/csghub/101/install/kubernetes/overview).
4040

4141
_Para más información y tutoriales avanzados, consulten el Centro de [Documentación de OpenCSG](https://opencsg.com/docs/intro)._
4242

README_jp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ CSGHub の主要な機能と使い方を素早く理解していただくため
3636

3737
- **探索したい方:** [OpenCSGのウェブサイト](https://opencsg.com)で、CSGHubの無料SaaS版を試用できます。[クイックスタートガイド](./docs/csghub_saas_en.md)を参考にして、CSGHub SaaSインターフェースを使い、LLMやデータセットの管理とLLMアプリケーションのデプロイをお試しください。
3838

39-
- **さらに実践したいユーザー向け:** [CSGHub公式ドキュメント](https://opencsg.com/docs/csghub/101/introduction)を参照してください。このドキュメントでは現在、[Docker Compose](https://opencsg.com/docs/csghub/101/install/install_csghub_by_docker)[Helm Chart](https://opencsg.com/docs/csghub/101/install/install_csghub_by_helm)を使用したインストール方法が紹介されています。
39+
- **さらに実践したいユーザー向け:** [CSGHub公式ドキュメント](https://opencsg.com/docs/csghub/101/introduction)を参照してください。このドキュメントでは現在、[Docker Compose](https://opencsg.com/docs/en/csghub/101/install/docker/overview)[Helm Chart](https://opencsg.com/docs/en/csghub/101/install/kubernetes/overview)を使用したインストール方法が紹介されています。
4040

4141
_詳細情報と高度なチュートリアルについては、[OpenCSGドキュメントセンター](https://opencsg.com/docs/intro)を参照してください。_
4242

README_kr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ CSGHub 를 빠르게 익힐 수 있도록 주요 기능과 사용 방법을 소
3636

3737
- **탐색을 원하는 사용자:** [OpenCSG 웹사이트](https://opencsg.com)에서 CSGHub의 무료 SaaS 버전을 사용해 보세요. [간단한 빠른 시작 가이드](./docs/csghub_saas_en.md)를 참조하여 CSGHub SaaS 인터페이스로 LLM과 데이터셋을 관리하고 LLM 애플리케이션을 배포해 보세요.
3838

39-
- **추가 연습을 원하는 사용자:** [CSGHub 공식 문서](https://opencsg.com/docs/csghub/101/introduction)를 참조하세요. 이 문서는 현재 [Docker Compose](https://opencsg.com/docs/csghub/101/install/install_csghub_by_docker)[Helm Chart](https://opencsg.com/docs/csghub/101/install/install_csghub_by_helm)를 사용한 설치 방법을 제공합니다.
39+
- **추가 연습을 원하는 사용자:** [CSGHub 공식 문서](https://opencsg.com/docs/csghub/101/introduction)를 참조하세요. 이 문서는 현재 [Docker Compose](https://opencsg.com/docs/en/csghub/101/install/docker/overview)[Helm Chart](https://opencsg.com/docs/en/csghub/101/install/kubernetes/overview) 를 사용한 설치 방법을 제공합니다.
4040

4141
_자세한 내용 및 고급 튜토리얼은 [OpenCSG 문서 센터](https://opencsg.com/docs/intro)를 참조하세요._
4242

README_zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ CSGHub 是一个开源平台,专为管理大语言模型(LLM)资产而设
3636

3737
- **想要快速探索的用户:** 可在 [OpenCSG 网站](https://opencsg.com)上体验 CSGHub 的免费 SaaS 版本。参考该[快速开始指南](./docs/csghub_saas_zh.md),通过 CSGHub SaaS 界面管理大模型和数据集,部署大模型应用。
3838

39-
- **想要进一步实践的用户:** 请参阅 [CSGHub 官方文档](https://opencsg.com/docs/csghub/101/introduction)。目前文档中已提供 [Docker Compose ](https://opencsg.com/docs/csghub/101/install/install_csghub_by_docker)[Helm Chart](https://opencsg.com/docs/csghub/101/install/install_csghub_by_helm) 安装方式。
39+
- **想要进一步实践的用户:** 请参阅 [CSGHub 官方文档](https://opencsg.com/docs/csghub/101/introduction)。目前文档中已提供 [Docker Compose ](https://opencsg.com/docs/csghub/101/install/docker/overview)[Helm Chart](https://opencsg.com/docs/csghub/101/install/kubernetes/overview) 安装方式。
4040

4141
_更多信息与高级教程请参阅 [OpenCSG 文档中心](https://opencsg.com/docs/intro)_
4242

Lines changed: 19 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest'
2+
import { mount } from '@vue/test-utils'
3+
import NotebookDetail from '@/components/notebooks/NotebookDetail.vue'
4+
5+
// Mock vue-router usage is minimal here; component uses provide/inject and props.
6+
7+
// Mock vue3-cookies
8+
vi.mock('vue3-cookies', () => ({
9+
useCookies: () => ({ cookies: { get: vi.fn(() => 'mock-jwt') } })
10+
}))
11+
12+
// Mock RepoHeader/RepoTabs/LoadingSpinner
13+
vi.mock('@/components/shared/RepoHeader.vue', () => ({
14+
default: { name: 'RepoHeader', template: '<div>RepoHeader</div>' }
15+
}))
16+
vi.mock('@/components/shared/RepoTabs.vue', () => ({
17+
default: { name: 'RepoTabs', template: '<div>RepoTabs</div>' }
18+
}))
19+
vi.mock('@/components/shared/LoadingSpinner.vue', () => ({
20+
default: { name: 'LoadingSpinner', props: ['loading','text'], template: '<div v-if="loading">loading</div>' }
21+
}))
22+
23+
// Mock RepoTabStore
24+
vi.mock('@/stores/RepoTabStore', () => ({
25+
useRepoTabStore: () => ({ setRepoTab: vi.fn() })
26+
}))
27+
28+
// Mock RepoDetailStore
29+
const storeState = () => ({
30+
deployName: '', id: '', status: '', hardware: '', notebookResource: '',
31+
repositoryId: 0, deployId: 0, nickName: '', description: '', endpoint: '',
32+
modelId: '', clusterId: '', privateVisibility: false, actualReplica: 0,
33+
activeInstance: '', failedReason: '', repoType: 'notebook',
34+
initialize: vi.fn(function (data) {
35+
this.deployName = data.deploy_name || 'nb-name'
36+
this.id = String(data.id || '1')
37+
this.status = data.status || 'Running'
38+
this.hardware = data.hardware || 'GPU'
39+
this.notebookResource = data.resource_name || 'r1'
40+
this.repositoryId = data.repository_id || 1
41+
this.deployId = data.deploy_id || 2
42+
this.endpoint = data.endpoint || 'nb.endpoint'
43+
})
44+
})
45+
vi.mock('@/stores/RepoDetailStore', () => ({
46+
default: () => ({ ...storeState(), isInitialized: { value: true } })
47+
}))
48+
49+
// Mock Element Plus
50+
vi.mock('element-plus', () => ({ ElMessage: vi.fn() }))
51+
52+
// Mock utils
53+
vi.mock('@/packs/utils', () => ({ ToNotFoundPage: vi.fn() }))
54+
55+
// Mock refreshJWT
56+
vi.mock('@/packs/refreshJWT.js', () => ({ default: vi.fn() }))
57+
58+
// Mock fetch-event-source for status sse
59+
vi.mock('@microsoft/fetch-event-source', () => ({
60+
fetchEventSource: vi.fn((url, opts) => {
61+
setTimeout(() => {
62+
opts.onopen && opts.onopen({ ok: true, status: 200 })
63+
opts.onmessage && opts.onmessage({ data: JSON.stringify({ status: 'Running', details: [{ name: 'pod-1' }] }) })
64+
}, 10)
65+
return { close: vi.fn() }
66+
})
67+
}))
68+
69+
// Mock API
70+
const apiMock = vi.fn((url) => ({
71+
json: () => {
72+
if (url.startsWith('/notebooks/')) {
73+
return Promise.resolve({
74+
response: { value: { status: 200 } },
75+
data: { value: { data: {
76+
id: 1,
77+
namespace: { Path: 'tester', Type: 'user', Avatar: '' },
78+
deploy_id: 2,
79+
repository_id: 3,
80+
deploy_name: 'NB Demo',
81+
status: 'Running',
82+
hardware: 'A100',
83+
endpoint: 'nb.endpoint',
84+
resource_id: 11,
85+
order_detail_id: 101,
86+
resource_name: 'GPU-A100'
87+
} } },
88+
error: { value: null }
89+
})
90+
}
91+
return Promise.resolve({ data: { value: { data: [] } }, error: { value: null }, response: { value: { status: 200 } } })
92+
}
93+
}))
94+
vi.mock('@/packs/useFetchApi', () => ({ default: (url) => apiMock(url) }))
95+
96+
const createWrapper = async (props = {}) => {
97+
global.ENABLE_HTTPS = 'false'
98+
const wrapper = mount(NotebookDetail, {
99+
global: {
100+
provide: { csghubServer: 'http://server' }
101+
},
102+
props: { notebookId: '1', notebookName: 'nb', ...props }
103+
})
104+
await new Promise(r => setTimeout(r, 60))
105+
await wrapper.vm.$nextTick()
106+
return wrapper
107+
}
108+
109+
describe('NotebookDetail', () => {
110+
beforeEach(() => apiMock.mockClear())
111+
112+
it('mounts and fetches detail', async () => {
113+
const wrapper = await createWrapper()
114+
expect(wrapper.exists()).toBe(true)
115+
expect(apiMock).toHaveBeenCalledWith('/notebooks/1')
116+
})
117+
118+
it('connects status SSE and updates store fields', async () => {
119+
const wrapper = await createWrapper()
120+
await new Promise(r => setTimeout(r, 100))
121+
expect(wrapper.vm.repoDetailStore.status).toBe('Running')
122+
})
123+
})
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { describe, it, expect, vi } from 'vitest'
2+
import { mount } from '@vue/test-utils'
3+
import NotebookItem from '@/components/notebooks/NotebookItem.vue'
4+
5+
// Mock sub components used inside
6+
vi.mock('@/components/application_spaces/AppStatus.vue', () => ({
7+
default: { name: 'AppStatus', template: '<div>AppStatus</div>' }
8+
}))
9+
vi.mock('@/components/application_spaces/AppPayMode.vue', () => ({
10+
default: { name: 'AppPayMode', template: '<div>AppPayMode</div>' }
11+
}))
12+
13+
// Mock clipboard util
14+
const copySpy = vi.fn()
15+
vi.mock('@/packs/clipboard', () => ({ copyToClipboard: (...args) => copySpy(...args) }))
16+
17+
const mountItem = (notebook) => mount(NotebookItem, {
18+
global: { stubs: { SvgIcon: { template: '<div />' } } },
19+
props: { notebook }
20+
})
21+
22+
describe('NotebookItem', () => {
23+
it('renders and copies endpoint when running', async () => {
24+
const wrapper = mountItem({
25+
id: 1,
26+
deploy_name: 'nb',
27+
status: 'Running',
28+
endpoint: 'nb.endpoint',
29+
hardware: 'GPU',
30+
pay_mode: 'postpaid',
31+
runtime_framework: 'PyTorch',
32+
runtime_framework_version: '2.1',
33+
resource_name: 'A100'
34+
})
35+
36+
// copy button exists and triggers copy
37+
const copyContainer = wrapper.find('div[class*="rounded-br-xl"]')
38+
expect(copyContainer.exists()).toBe(true)
39+
await copyContainer.trigger('click')
40+
expect(copySpy).toHaveBeenCalledWith('nb.endpoint')
41+
})
42+
43+
it('detailLink navigates to notebook detail by id', async () => {
44+
const originalHref = window.location.href
45+
Object.defineProperty(window, 'location', { value: { href: '' }, writable: true })
46+
47+
const wrapper = mountItem({ id: 123, deploy_name: 'nb', status: 'Stopped' })
48+
await wrapper.trigger('click')
49+
expect(window.location.href).toBe('/notebooks/123')
50+
51+
window.location.href = originalHref
52+
})
53+
})

0 commit comments

Comments
 (0)