feat(workspace): add ListBranches method to WorkspaceService

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 18:05:18 +08:00
parent a4a6e6a01a
commit 665d029d2b
8 changed files with 68 additions and 0 deletions
+3
View File
@@ -68,6 +68,9 @@ func (s *stubWsService) UpsertCredential(context.Context, workspace.Caller, uuid
func (s *stubWsService) GetCredentialMeta(context.Context, workspace.Caller, uuid.UUID) (*workspace.Credential, error) {
panic("stubWsService.GetCredentialMeta unused")
}
func (s *stubWsService) ListBranches(context.Context, workspace.Caller, uuid.UUID) ([]string, error) {
panic("stubWsService.ListBranches unused")
}
// stubProjectAccess satisfies acp.ProjectAccess for the e2e test.
// Only GetProjectByWorkspace is exercised; the rest panic.
+3
View File
@@ -327,6 +327,9 @@ func (s *fakeWsSvc) UpsertCredential(context.Context, workspace.Caller, uuid.UUI
func (s *fakeWsSvc) GetCredentialMeta(context.Context, workspace.Caller, uuid.UUID) (*workspace.Credential, error) {
return nil, fmt.Errorf("unused")
}
func (s *fakeWsSvc) ListBranches(context.Context, workspace.Caller, uuid.UUID) ([]string, error) {
return nil, fmt.Errorf("unused")
}
// ===== Tests =====
+4
View File
@@ -436,6 +436,10 @@ func (s *acpStubWsSvc) GetCredentialMeta(context.Context, workspace.Caller, uuid
return nil, fmt.Errorf("acpStubWsSvc.GetCredentialMeta unused")
}
func (s *acpStubWsSvc) ListBranches(context.Context, workspace.Caller, uuid.UUID) ([]string, error) {
return nil, fmt.Errorf("acpStubWsSvc.ListBranches unused")
}
// acpStubProjectAccess satisfies acp.ProjectAccess; only GetProjectByWorkspace is
// exercised by the branch=main path.
type acpStubProjectAccess struct {
+3
View File
@@ -266,6 +266,9 @@ func (f *fakeWorkspaceSvc) UpsertCredential(_ context.Context, _ workspace.Calle
func (f *fakeWorkspaceSvc) GetCredentialMeta(_ context.Context, _ workspace.Caller, _ uuid.UUID) (*workspace.Credential, error) {
panic("not implemented")
}
func (f *fakeWorkspaceSvc) ListBranches(_ context.Context, _ workspace.Caller, _ uuid.UUID) ([]string, error) {
panic("not implemented")
}
// ===== test helpers =====
+1
View File
@@ -145,6 +145,7 @@ type WorkspaceService interface {
UpsertCredential(ctx context.Context, c Caller, wsID uuid.UUID, in UpsertCredentialInput) (*Credential, error)
GetCredentialMeta(ctx context.Context, c Caller, wsID uuid.UUID) (*Credential, error)
ListBranches(ctx context.Context, c Caller, wsID uuid.UUID) ([]string, error)
}
// WorktreeService 暴露支线 worktree 的应用服务方法。
+4
View File
@@ -48,6 +48,10 @@ func (f *fakeWS) GetCredentialMeta(_ context.Context, _ Caller, _ uuid.UUID) (*C
return &Credential{Kind: CredKindNone}, nil
}
func (f *fakeWS) ListBranches(_ context.Context, _ Caller, _ uuid.UUID) ([]string, error) {
return nil, nil
}
// fakeResolver 把固定 token 解码为 uuid(沿 internal/project handler_test.go 模式)。
type fakeResolver struct{ valid map[string]uuid.UUID }
+17
View File
@@ -328,6 +328,23 @@ func (s *workspaceService) GetCredentialMeta(ctx context.Context, c Caller, wsID
return cred, nil
}
func (s *workspaceService) ListBranches(ctx context.Context, c Caller, wsID uuid.UUID) ([]string, error) {
ws, err := s.repo.GetWorkspaceByID(ctx, wsID)
if err != nil {
return nil, err
}
if canRead, _, err := s.pa.ResolveByID(ctx, c.UserID, c.IsAdmin, ws.ProjectID); err != nil {
return nil, err
} else if !canRead {
return nil, errs.New(errs.CodeForbidden, "no read access")
}
cred, err := s.loadDecryptedCredential(ctx, wsID)
if err != nil {
return nil, err
}
return s.gitr.ListRemoteBranches(ctx, ws.MainPath, cred)
}
func (s *workspaceService) encryptForUpsert(wsID uuid.UUID, in UpsertCredentialInput) (*Credential, error) {
cred := &Credential{
ID: uuid.New(),
@@ -450,3 +450,36 @@ func TestWorkspaceService_Update_DefaultBranch_SameValue_NoGitOps(t *testing.T)
require.Empty(t, fg.fetchRemoteBranchCalls)
require.Empty(t, fg.checkoutCalls)
}
func TestWorkspaceService_ListBranches(t *testing.T) {
t.Parallel()
pa := &fakePA{pid: uuid.New(), canRead: true, canWrite: true}
svc, repo := newSvc(t, pa, nil, &fakeGit{})
caller := Caller{UserID: uuid.New()}
wsID := uuid.New()
repo.wss[wsID] = &Workspace{
ID: wsID, ProjectID: pa.pid, Slug: "ws1",
DefaultBranch: "main", MainPath: "/data/ws1/main",
}
branches, err := svc.ListBranches(context.Background(), caller, wsID)
require.NoError(t, err)
// fakeGit returns nil, nil → empty slice
require.Nil(t, branches)
}
func TestWorkspaceService_ListBranches_NoReadAccess(t *testing.T) {
t.Parallel()
pa := &fakePA{pid: uuid.New(), canRead: false, canWrite: false}
svc, repo := newSvc(t, pa, nil, &fakeGit{})
caller := Caller{UserID: uuid.New()}
wsID := uuid.New()
repo.wss[wsID] = &Workspace{
ID: wsID, ProjectID: pa.pid, Slug: "ws1",
}
_, err := svc.ListBranches(context.Background(), caller, wsID)
require.Error(t, err)
}