mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 08:02:51 +00:00
* Link Jira issues from workspace create dialog Add Jira issue linking to workspace creation, matching existing GitHub and Linear workflows. Users can paste Jira issue URLs in the smart name field to auto-populate workspace names and link the issue to the created workspace/worktree. Linked Jira issues appear on workspace cards via the new 'jira-issue' card property. Implements cancellable searches and summary reads to prevent stalled requests from blocking the shared Jira pool. Persists paired issue + source context metadata with validation of provider/site identity. Fixes git-username rate-limit handling to reject malformed JSON responses so garbage never becomes branch prefixes. * feat(jira): link issues during workspace creation - Display linked Jira issues on worktree cards - Fetch issue summaries and timestamps via Jira API - Gate Jira linking behind runtime capability check - Preserve user-typed names during async lookups * Enforce git check-ref-format rules in login validation Extend isBranchSafeHostedLogin to reject usernames that git rejects as invalid branch components: trailing dots, consecutive dots, and .lock suffix. Prevents invalid branch names from login usernames. * Enforce filesystem filename cap for branch-safe logins Loose refs store logins as single filenames, so the real constraint is the 255-byte filesystem cap, not git check-ref-format rules. This allows longer provider-agnostic logins while staying platform-safe.
127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
buildJiraWorkspaceSource,
|
|
buildLinearWorkspaceSource,
|
|
buildWorkspaceSourceSelection,
|
|
getWorkspaceSourceName,
|
|
getWorkspaceSourceProvider,
|
|
shouldApplyWorkspaceSourceAutoName,
|
|
shouldPreserveWorkspaceSourceOnRepoChange
|
|
} from './workspace-source'
|
|
|
|
describe('workspace source policy', () => {
|
|
const linear = buildLinearWorkspaceSource({
|
|
identifier: 'ENG-42',
|
|
title: 'Ship mobile parity',
|
|
url: 'https://linear.app/acme/issue/ENG-42/ship-mobile-parity',
|
|
workspaceId: 'workspace-1',
|
|
branchName: ' team/eng-42-ship-mobile-parity '
|
|
})
|
|
|
|
it('builds one Linear identity for desktop and mobile create flows', () => {
|
|
expect(linear).toMatchObject({
|
|
provider: 'linear',
|
|
number: 0,
|
|
linearIdentifier: 'ENG-42',
|
|
linearWorkspaceId: 'workspace-1',
|
|
linearOrganizationUrlKey: 'acme',
|
|
linearBranchName: 'team/eng-42-ship-mobile-parity'
|
|
})
|
|
expect(getWorkspaceSourceName(linear)).toEqual({
|
|
seedName: 'eng-42-ship-mobile-parity',
|
|
displayName: 'ENG-42 Ship mobile parity'
|
|
})
|
|
})
|
|
|
|
it('persists a Jira title without repeating its separately stored identifier', () => {
|
|
expect(
|
|
buildJiraWorkspaceSource({
|
|
key: 'ORCA-123',
|
|
title: 'Fix Jira card details',
|
|
url: 'https://company.atlassian.net/browse/ORCA-123'
|
|
})
|
|
).toEqual({
|
|
provider: 'jira',
|
|
type: 'issue',
|
|
number: 0,
|
|
title: 'Fix Jira card details',
|
|
url: 'https://company.atlassian.net/browse/ORCA-123',
|
|
jiraIdentifier: 'ORCA-123'
|
|
})
|
|
})
|
|
|
|
it('preserves global work-item sources across repo changes', () => {
|
|
expect(shouldPreserveWorkspaceSourceOnRepoChange(linear)).toBe(true)
|
|
expect(
|
|
shouldPreserveWorkspaceSourceOnRepoChange({
|
|
provider: 'jira',
|
|
type: 'issue',
|
|
number: 0,
|
|
title: 'Workspace scoped',
|
|
url: 'https://acme.atlassian.net/browse/FUS-1'
|
|
})
|
|
).toBe(true)
|
|
// Why: Jira items picked from smart search may arrive without an explicit
|
|
// provider; preservation must still hold via URL/identifier inference.
|
|
expect(
|
|
shouldPreserveWorkspaceSourceOnRepoChange({
|
|
type: 'issue',
|
|
number: 0,
|
|
title: 'Inferred Jira',
|
|
url: 'https://acme.atlassian.net/browse/FUS-1',
|
|
jiraIdentifier: 'FUS-1'
|
|
})
|
|
).toBe(true)
|
|
expect(
|
|
shouldPreserveWorkspaceSourceOnRepoChange({
|
|
provider: 'github',
|
|
type: 'issue',
|
|
number: 1,
|
|
title: 'Repo scoped',
|
|
url: 'https://github.com/o/r/issues/1'
|
|
})
|
|
).toBe(false)
|
|
// Why: GitLab is repo-scoped; pin both the explicit MR and the
|
|
// URL-inferred shape clear, since folder-source/project-group paths delegate here.
|
|
expect(
|
|
shouldPreserveWorkspaceSourceOnRepoChange({
|
|
provider: 'gitlab',
|
|
type: 'mr',
|
|
number: 2,
|
|
title: 'Repo scoped MR',
|
|
url: 'https://gitlab.com/o/r/-/merge_requests/2'
|
|
})
|
|
).toBe(false)
|
|
expect(
|
|
shouldPreserveWorkspaceSourceOnRepoChange({
|
|
type: 'issue',
|
|
number: 3,
|
|
title: 'Inferred GitLab',
|
|
url: 'https://gitlab.example.com/g/p/-/work_items/3'
|
|
})
|
|
).toBe(false)
|
|
// Why: a null source (branch-only) has nothing to preserve; callers guard on this.
|
|
expect(shouldPreserveWorkspaceSourceOnRepoChange(null)).toBe(false)
|
|
})
|
|
|
|
it('shares provider inference, selection labels, and auto-name gates', () => {
|
|
const legacyGitLab = {
|
|
type: 'issue' as const,
|
|
number: 7,
|
|
title: 'Self hosted',
|
|
url: 'https://gitlab.example.com/g/p/-/work_items/7'
|
|
}
|
|
expect(getWorkspaceSourceProvider(legacyGitLab)).toBe('gitlab')
|
|
expect(buildWorkspaceSourceSelection({ linkedWorkItem: legacyGitLab })).toMatchObject({
|
|
kind: 'gitlab-issue',
|
|
label: '#7 Self hosted'
|
|
})
|
|
expect(shouldApplyWorkspaceSourceAutoName({ currentName: '#42', lastAutoName: 'old' })).toBe(
|
|
true
|
|
)
|
|
expect(
|
|
shouldApplyWorkspaceSourceAutoName({ currentName: 'my workspace', lastAutoName: 'old' })
|
|
).toBe(false)
|
|
})
|
|
})
|