6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
188 lines
5.7 KiB
TypeScript
188 lines
5.7 KiB
TypeScript
/**
|
|
* @vitest-environment node
|
|
*/
|
|
import { describe, expect, it } from 'vitest'
|
|
import { gitlabGetGroupTool } from '@/tools/gitlab/get_group'
|
|
import { gitlabListGroupsTool } from '@/tools/gitlab/list_groups'
|
|
import { gitlabListUserMembershipsTool } from '@/tools/gitlab/list_user_memberships'
|
|
|
|
interface MockResponseOptions {
|
|
ok?: boolean
|
|
status?: number
|
|
json?: unknown
|
|
text?: string
|
|
headers?: Record<string, string>
|
|
}
|
|
|
|
function mockResponse({
|
|
ok = true,
|
|
status = 200,
|
|
json,
|
|
text = '',
|
|
headers = {},
|
|
}: MockResponseOptions): Response {
|
|
return {
|
|
ok,
|
|
status,
|
|
json: async () => json,
|
|
text: async () => text,
|
|
headers: {
|
|
get: (key: string) => headers[key.toLowerCase()] ?? null,
|
|
},
|
|
} as unknown as Response
|
|
}
|
|
|
|
describe('gitlab_get_group', () => {
|
|
it('builds the group endpoint and URL-encodes a namespaced path', () => {
|
|
expect(gitlabGetGroupTool.request.url({ accessToken: 'pat', groupId: '42' })).toBe(
|
|
'https://gitlab.com/api/v4/groups/42'
|
|
)
|
|
expect(gitlabGetGroupTool.request.url({ accessToken: 'pat', groupId: 'parent/child' })).toBe(
|
|
'https://gitlab.com/api/v4/groups/parent%2Fchild'
|
|
)
|
|
})
|
|
|
|
it('honors a self-managed host', () => {
|
|
expect(
|
|
gitlabGetGroupTool.request.url({
|
|
accessToken: 'pat',
|
|
host: 'gitlab.example.com',
|
|
groupId: '7',
|
|
})
|
|
).toBe('https://gitlab.example.com/api/v4/groups/7')
|
|
})
|
|
|
|
it('returns the group on success', async () => {
|
|
const result = await gitlabGetGroupTool.transformResponse?.(
|
|
mockResponse({ json: { id: 42, name: 'Platform' } }),
|
|
{} as never
|
|
)
|
|
expect(result?.success).toBe(true)
|
|
expect(result?.output.group).toEqual({ id: 42, name: 'Platform' })
|
|
})
|
|
|
|
it('surfaces API errors', async () => {
|
|
const result = await gitlabGetGroupTool.transformResponse?.(
|
|
mockResponse({ ok: false, status: 404, text: 'Not found' }),
|
|
{} as never
|
|
)
|
|
expect(result?.success).toBe(false)
|
|
expect(result?.error).toContain('404')
|
|
})
|
|
})
|
|
|
|
describe('gitlab_list_groups', () => {
|
|
it('lists groups with no filters', () => {
|
|
expect(gitlabListGroupsTool.request.url({ accessToken: 'pat' })).toBe(
|
|
'https://gitlab.com/api/v4/groups'
|
|
)
|
|
})
|
|
|
|
it('forwards filters and pagination', () => {
|
|
const url = gitlabListGroupsTool.request.url({
|
|
accessToken: 'pat',
|
|
owned: true,
|
|
search: 'plat',
|
|
topLevelOnly: true,
|
|
orderBy: 'name',
|
|
sort: 'asc',
|
|
perPage: 50,
|
|
page: 2,
|
|
})
|
|
expect(url).toBe(
|
|
'https://gitlab.com/api/v4/groups?owned=true&search=plat&top_level_only=true&order_by=name&sort=asc&per_page=50&page=2'
|
|
)
|
|
})
|
|
|
|
it('forwards the documented visibility, min-access-level, and all-available filters', () => {
|
|
const url = gitlabListGroupsTool.request.url({
|
|
accessToken: 'pat',
|
|
visibility: 'private',
|
|
minAccessLevel: 30,
|
|
allAvailable: true,
|
|
})
|
|
expect(url).toBe(
|
|
'https://gitlab.com/api/v4/groups?visibility=private&min_access_level=30&all_available=true'
|
|
)
|
|
})
|
|
|
|
it('rejects an out-of-enum minimum access level instead of sending it to GitLab', () => {
|
|
expect(() =>
|
|
gitlabListGroupsTool.request.url({ accessToken: 'pat', minAccessLevel: 31 })
|
|
).toThrow(/Invalid GitLab access level/)
|
|
expect(() =>
|
|
gitlabListGroupsTool.request.url({ accessToken: 'pat', minAccessLevel: 0 })
|
|
).toThrow(/Invalid GitLab access level/)
|
|
})
|
|
|
|
it('rejects similarity ordering without a search term, but allows it with one', () => {
|
|
expect(() =>
|
|
gitlabListGroupsTool.request.url({ accessToken: 'pat', orderBy: 'similarity' })
|
|
).toThrow(/similarity/)
|
|
expect(
|
|
gitlabListGroupsTool.request.url({
|
|
accessToken: 'pat',
|
|
orderBy: 'similarity',
|
|
search: 'plat',
|
|
})
|
|
).toBe('https://gitlab.com/api/v4/groups?search=plat&order_by=similarity')
|
|
})
|
|
|
|
it('reads the total from the x-total header, falling back to length', async () => {
|
|
const withHeader = await gitlabListGroupsTool.transformResponse?.(
|
|
mockResponse({ json: [{ id: 1 }, { id: 2 }], headers: { 'x-total': '17' } }),
|
|
{} as never
|
|
)
|
|
expect(withHeader?.output.total).toBe(17)
|
|
|
|
const withoutHeader = await gitlabListGroupsTool.transformResponse?.(
|
|
mockResponse({ json: [{ id: 1 }, { id: 2 }] }),
|
|
{} as never
|
|
)
|
|
expect(withoutHeader?.output.total).toBe(2)
|
|
})
|
|
})
|
|
|
|
describe('gitlab_list_user_memberships', () => {
|
|
it('builds the admin memberships endpoint', () => {
|
|
expect(gitlabListUserMembershipsTool.request.url({ accessToken: 'pat', userId: '7' })).toBe(
|
|
'https://gitlab.com/api/v4/users/7/memberships'
|
|
)
|
|
})
|
|
|
|
it('forwards the type filter and pagination', () => {
|
|
const url = gitlabListUserMembershipsTool.request.url({
|
|
accessToken: 'pat',
|
|
userId: '7',
|
|
membershipType: 'Namespace',
|
|
perPage: 25,
|
|
page: 3,
|
|
})
|
|
expect(url).toBe(
|
|
'https://gitlab.com/api/v4/users/7/memberships?type=Namespace&per_page=25&page=3'
|
|
)
|
|
})
|
|
|
|
it('returns memberships on success', async () => {
|
|
const result = await gitlabListUserMembershipsTool.transformResponse?.(
|
|
mockResponse({
|
|
json: [{ source_id: 1, source_name: 'grp', source_type: 'Namespace', access_level: 30 }],
|
|
headers: { 'x-total': '1' },
|
|
}),
|
|
{} as never
|
|
)
|
|
expect(result?.success).toBe(true)
|
|
expect(result?.output.memberships).toHaveLength(1)
|
|
expect(result?.output.total).toBe(1)
|
|
})
|
|
|
|
it('surfaces a 403 for a non-admin token', async () => {
|
|
const result = await gitlabListUserMembershipsTool.transformResponse?.(
|
|
mockResponse({ ok: false, status: 403, text: 'Forbidden' }),
|
|
{} as never
|
|
)
|
|
expect(result?.success).toBe(false)
|
|
expect(result?.error).toContain('403')
|
|
})
|
|
})
|