mirror of
https://github.com/stablyai/orca.git
synced 2026-10-15 16:02:49 +00:00
Extract GitLab project ref tests and update usage stats translations
- Move GitLab project ref parsing tests into a dedicated test file to keep modules focused and add tests for candidate parsing. - Add missing translations for the usage sessions table and breakdown section across multiple locales.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable max-lines -- Why: GitLab remote parsing coverage needs many URL/host fixtures against the same mocked git/glab helpers. */
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { gitExecFileAsyncMock, glabExecFileAsyncMock, sshExecMock } = vi.hoisted(() => ({
|
||||
@@ -22,96 +21,12 @@ import {
|
||||
getGlabKnownHosts,
|
||||
getProjectRef,
|
||||
getProjectRefForRemote,
|
||||
parseGitLabProjectRef,
|
||||
parseGlabApiResponse,
|
||||
parseGlabAuthStatusHosts,
|
||||
resolveIssueSource
|
||||
} from './gl-utils'
|
||||
import { registerSshGitProvider, unregisterSshGitProvider } from '../providers/ssh-git-dispatch'
|
||||
|
||||
describe('gitlab project ref parsing', () => {
|
||||
it('parses HTTPS and SSH GitLab.com remotes', () => {
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/acme/widgets.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
expect(parseGitLabProjectRef('git@gitlab.com:stablyai/orca.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'stablyai/orca'
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves nested group paths', () => {
|
||||
expect(parseGitLabProjectRef('git@gitlab.com:group/subgroup/project.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'group/subgroup/project'
|
||||
})
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/g1/g2/g3/proj.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'g1/g2/g3/proj'
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null for non-GitLab hosts when host not in knownHosts', () => {
|
||||
expect(parseGitLabProjectRef('git@github.com:stablyai/orca.git')).toBeNull()
|
||||
expect(parseGitLabProjectRef('git@example.com:foo/bar.git')).toBeNull()
|
||||
})
|
||||
|
||||
it('matches self-hosted hosts when included in knownHosts', () => {
|
||||
expect(
|
||||
parseGitLabProjectRef('git@gitlab.example.com:team/api.git', [
|
||||
'gitlab.com',
|
||||
'gitlab.example.com'
|
||||
])
|
||||
).toEqual({ host: 'gitlab.example.com', path: 'team/api' })
|
||||
})
|
||||
|
||||
it('parses GitLab remotes with non-standard ports without treating the port as a path segment', () => {
|
||||
expect(
|
||||
parseGitLabProjectRef('ssh://git@gitlab.example.com:2222/team/api.git', [
|
||||
'gitlab.com',
|
||||
'gitlab.example.com'
|
||||
])
|
||||
).toEqual({ host: 'gitlab.example.com', path: 'team/api' })
|
||||
expect(
|
||||
parseGitLabProjectRef('https://gitlab.example.com:8443/team/api.git', [
|
||||
'gitlab.com',
|
||||
'gitlab.example.com'
|
||||
])
|
||||
).toEqual({ host: 'gitlab.example.com', path: 'team/api' })
|
||||
})
|
||||
|
||||
it('rejects single-segment paths (host root or user-only)', () => {
|
||||
expect(parseGitLabProjectRef('git@gitlab.com:foo.git')).toBeNull()
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/foo.git')).toBeNull()
|
||||
})
|
||||
|
||||
it('handles missing .git suffix', () => {
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/acme/widgets')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
})
|
||||
|
||||
it('strips trailing slashes after .git suffixes', () => {
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/acme/widgets.git/')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
expect(parseGitLabProjectRef('ssh://git@gitlab.com/acme/widgets.git/')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves git protocol remote support', () => {
|
||||
expect(parseGitLabProjectRef('git://gitlab.com/acme/widgets.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('gitlab project ref resolution', () => {
|
||||
beforeEach(() => {
|
||||
gitExecFileAsyncMock.mockReset()
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { parseGitLabProjectRef, parseRemoteProjectRefCandidate } from './project-ref-parser'
|
||||
|
||||
describe('gitlab project ref parsing', () => {
|
||||
it('parses HTTPS and SSH GitLab.com remotes', () => {
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/acme/widgets.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
expect(parseGitLabProjectRef('git@gitlab.com:stablyai/orca.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'stablyai/orca'
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves nested group paths', () => {
|
||||
expect(parseGitLabProjectRef('git@gitlab.com:group/subgroup/project.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'group/subgroup/project'
|
||||
})
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/g1/g2/g3/proj.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'g1/g2/g3/proj'
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null for non-GitLab hosts when host not in knownHosts', () => {
|
||||
expect(parseGitLabProjectRef('git@github.com:stablyai/orca.git')).toBeNull()
|
||||
expect(parseGitLabProjectRef('git@example.com:foo/bar.git')).toBeNull()
|
||||
})
|
||||
|
||||
it('matches self-hosted hosts when included in knownHosts', () => {
|
||||
expect(
|
||||
parseGitLabProjectRef('git@gitlab.example.com:team/api.git', [
|
||||
'gitlab.com',
|
||||
'gitlab.example.com'
|
||||
])
|
||||
).toEqual({ host: 'gitlab.example.com', path: 'team/api' })
|
||||
})
|
||||
|
||||
it('parses GitLab remotes with non-standard ports without treating the port as a path segment', () => {
|
||||
expect(
|
||||
parseGitLabProjectRef('ssh://git@gitlab.example.com:2222/team/api.git', [
|
||||
'gitlab.com',
|
||||
'gitlab.example.com'
|
||||
])
|
||||
).toEqual({ host: 'gitlab.example.com', path: 'team/api' })
|
||||
expect(
|
||||
parseGitLabProjectRef('https://gitlab.example.com:8443/team/api.git', [
|
||||
'gitlab.com',
|
||||
'gitlab.example.com'
|
||||
])
|
||||
).toEqual({ host: 'gitlab.example.com', path: 'team/api' })
|
||||
})
|
||||
|
||||
it('rejects single-segment paths (host root or user-only)', () => {
|
||||
expect(parseGitLabProjectRef('git@gitlab.com:foo.git')).toBeNull()
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/foo.git')).toBeNull()
|
||||
})
|
||||
|
||||
it('handles missing .git suffix', () => {
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/acme/widgets')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
})
|
||||
|
||||
it('strips trailing slashes after .git suffixes', () => {
|
||||
expect(parseGitLabProjectRef('https://gitlab.com/acme/widgets.git/')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
expect(parseGitLabProjectRef('ssh://git@gitlab.com/acme/widgets.git/')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves git protocol remote support', () => {
|
||||
expect(parseGitLabProjectRef('git://gitlab.com/acme/widgets.git')).toEqual({
|
||||
host: 'gitlab.com',
|
||||
path: 'acme/widgets'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('gitlab remote project ref candidates', () => {
|
||||
it('extracts self-hosted candidates before the host is trusted', () => {
|
||||
expect(parseRemoteProjectRefCandidate('git@gitlab.internal:team/orca.git')).toEqual({
|
||||
host: 'gitlab.internal',
|
||||
path: 'team/orca'
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects non-git URLs and single-segment project paths', () => {
|
||||
expect(parseRemoteProjectRefCandidate('file:///tmp/repo')).toBeNull()
|
||||
expect(parseRemoteProjectRefCandidate('git@gitlab.internal:team.git')).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -69,7 +69,7 @@ export function UsageSessionsTable({
|
||||
{translate('auto.components.stats.UsageSessionsTable.c17bed0416', 'Project')}
|
||||
</th>
|
||||
<th className="px-2 py-2 font-medium">
|
||||
{translate('auto.components.stats.UsageSessionsTable.1afc25eb06', 'Model')}
|
||||
{translate('auto.components.stats.UsageSessionsTable.f6a2c8d019', 'Model')}
|
||||
</th>
|
||||
<th className="px-2 py-2 font-medium">{eventsLabel}</th>
|
||||
<th className="px-2 py-2 font-medium">
|
||||
|
||||
@@ -2991,6 +2991,22 @@
|
||||
"d9a4b3e2f1c5": "events"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UsageBreakdownSection": {
|
||||
"7765a4c3e1": "n/a",
|
||||
"247c93ca92": "• inferred pricing"
|
||||
},
|
||||
"UsageSessionsTable": {
|
||||
"1afc25eb06": "Turns",
|
||||
"0f03975d59": "Events",
|
||||
"21ea00bfa8": "Cache",
|
||||
"e0b988599d": "Total",
|
||||
"01476891c7": "Last active",
|
||||
"c17bed0416": "Project",
|
||||
"f6a2c8d019": "Model",
|
||||
"faf3444859": "Input",
|
||||
"a8b7487ff7": "Output",
|
||||
"cfe2282ffa": "Unknown"
|
||||
}
|
||||
},
|
||||
"sparse": {
|
||||
|
||||
@@ -2991,6 +2991,22 @@
|
||||
"d9a4b3e2f1c5": "eventos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UsageBreakdownSection": {
|
||||
"7765a4c3e1": "n / A",
|
||||
"247c93ca92": "• precios inferidos"
|
||||
},
|
||||
"UsageSessionsTable": {
|
||||
"1afc25eb06": "vueltas",
|
||||
"0f03975d59": "Eventos",
|
||||
"21ea00bfa8": "Cache",
|
||||
"e0b988599d": "Total",
|
||||
"01476891c7": "Último activo",
|
||||
"c17bed0416": "Proyecto",
|
||||
"f6a2c8d019": "Modelo",
|
||||
"faf3444859": "Aporte",
|
||||
"a8b7487ff7": "Producción",
|
||||
"cfe2282ffa": "Desconocido"
|
||||
}
|
||||
},
|
||||
"sparse": {
|
||||
|
||||
@@ -2991,6 +2991,22 @@
|
||||
"d9a4b3e2f1c5": "イベント"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UsageBreakdownSection": {
|
||||
"7765a4c3e1": "該当なし",
|
||||
"247c93ca92": "• 推定価格"
|
||||
},
|
||||
"UsageSessionsTable": {
|
||||
"1afc25eb06": "ターン",
|
||||
"0f03975d59": "イベント",
|
||||
"21ea00bfa8": "キャッシュ",
|
||||
"e0b988599d": "合計",
|
||||
"01476891c7": "最後にアクティブだった",
|
||||
"c17bed0416": "プロジェクト",
|
||||
"f6a2c8d019": "モデル",
|
||||
"faf3444859": "入力",
|
||||
"a8b7487ff7": "出力",
|
||||
"cfe2282ffa": "未知"
|
||||
}
|
||||
},
|
||||
"sparse": {
|
||||
|
||||
@@ -2991,6 +2991,22 @@
|
||||
"d9a4b3e2f1c5": "이벤트"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UsageBreakdownSection": {
|
||||
"7765a4c3e1": "해당 없음",
|
||||
"247c93ca92": "• 추론된 가격"
|
||||
},
|
||||
"UsageSessionsTable": {
|
||||
"1afc25eb06": "턴",
|
||||
"0f03975d59": "이벤트",
|
||||
"21ea00bfa8": "캐시",
|
||||
"e0b988599d": "총",
|
||||
"01476891c7": "마지막 활동",
|
||||
"c17bed0416": "프로젝트",
|
||||
"f6a2c8d019": "모델",
|
||||
"faf3444859": "입력",
|
||||
"a8b7487ff7": "출력",
|
||||
"cfe2282ffa": "알려지지 않은"
|
||||
}
|
||||
},
|
||||
"sparse": {
|
||||
|
||||
@@ -2991,6 +2991,22 @@
|
||||
"d9a4b3e2f1c5": "事件"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UsageBreakdownSection": {
|
||||
"7765a4c3e1": "不适用",
|
||||
"247c93ca92": "• 推断定价"
|
||||
},
|
||||
"UsageSessionsTable": {
|
||||
"1afc25eb06": "轮次",
|
||||
"0f03975d59": "事件",
|
||||
"21ea00bfa8": "缓存",
|
||||
"e0b988599d": "总计",
|
||||
"01476891c7": "最后活跃",
|
||||
"c17bed0416": "项目",
|
||||
"f6a2c8d019": "模型",
|
||||
"faf3444859": "输入",
|
||||
"a8b7487ff7": "输出",
|
||||
"cfe2282ffa": "未知"
|
||||
}
|
||||
},
|
||||
"sparse": {
|
||||
|
||||
Reference in New Issue
Block a user