Files
kandev/apps/backend/internal/mentions/provider_azure_test.go
T
José Almeida 7de788ceed feat: add cross-integration entity references (#1862)
* feat: add cross-integration entity references

* fix(web): anchor entity reference menu to composer

* fix: keep Kandev tasks under @ mentions

* fix: address entity reference review findings

* test(e2e): use option role for prompt menu

* test(e2e): update passthrough mention roles

* fix: preserve queued references and paginate Azure PRs
2026-07-23 09:03:56 +01:00

151 lines
5.9 KiB
Go

package mentions
import (
"context"
"errors"
"testing"
"github.com/kandev/kandev/internal/azuredevops"
apiv1 "github.com/kandev/kandev/pkg/api/v1"
)
type fakeAzureMentionService struct {
workItems []azuredevops.MentionWorkItem
prs []azuredevops.MentionPullRequest
project *azuredevops.MentionProject
repository *azuredevops.MentionRepository
err error
}
func (s *fakeAzureMentionService) SearchMentionWorkItemsForWorkspace(
context.Context, string, string, int,
) ([]azuredevops.MentionWorkItem, error) {
return s.workItems, s.err
}
func (s *fakeAzureMentionService) SearchMentionPullRequestsForWorkspace(
context.Context, string, string, int,
) ([]azuredevops.MentionPullRequest, error) {
return s.prs, s.err
}
func (s *fakeAzureMentionService) ResolveMentionProjectForWorkspace(
context.Context, string, string,
) (*azuredevops.MentionProject, error) {
return s.project, s.err
}
func (s *fakeAzureMentionService) ResolveMentionRepositoryForWorkspace(
context.Context, string, string, string,
) (*azuredevops.MentionRepository, error) {
return s.repository, s.err
}
func TestAzureProvidersMapWorkItemsAndPullRequests(t *testing.T) {
service := &fakeAzureMentionService{
workItems: []azuredevops.MentionWorkItem{{
ID: 101, Title: "Fix auth", OrganizationURL: "https://dev.azure.com/acme",
ProjectID: "project-1", ProjectName: "Platform",
}},
prs: []azuredevops.MentionPullRequest{{
ID: 42, Title: "Land auth fix", OrganizationURL: "https://dev.azure.com/acme",
ProjectID: "project-1", ProjectName: "Platform", RepositoryID: "repo-1", RepositoryName: "widgets",
}},
}
workProvider := NewAzureWorkItemProvider(service)
prProvider := NewAzurePullRequestProvider(service)
if descriptor := workProvider.Descriptor(); descriptor.Provider != "azure_devops" || descriptor.Kind != "work_item" || descriptor.Order != 60 {
t.Fatalf("work descriptor = %#v", descriptor)
}
if descriptor := prProvider.Descriptor(); descriptor.Provider != "azure_devops" || descriptor.Kind != "pull_request" || descriptor.Order != 61 {
t.Fatalf("PR descriptor = %#v", descriptor)
}
work, err := workProvider.Search(t.Context(), SearchRequest{WorkspaceID: "workspace-1", Query: "auth", Limit: 5})
if err != nil {
t.Fatalf("search work: %v", err)
}
prs, err := prProvider.Search(t.Context(), SearchRequest{WorkspaceID: "workspace-1", Query: "auth", Limit: 5})
if err != nil {
t.Fatalf("search PRs: %v", err)
}
wantWork := Candidate{
ID: "101", Key: "Platform#101", Title: "Fix auth",
URL: "https://dev.azure.com/acme/Platform/_workitems/edit/101",
Scope: "dev.azure.com/acme/project-1",
}
wantPR := Candidate{
ID: "42", Key: "Platform/widgets!42", Title: "Land auth fix",
URL: "https://dev.azure.com/acme/Platform/_git/widgets/pullrequest/42",
Scope: "dev.azure.com/acme/project-1/repo-1",
}
if len(work) != 1 || work[0] != wantWork {
t.Fatalf("work = %#v, want %#v", work, wantWork)
}
if len(prs) != 1 || prs[0] != wantPR {
t.Fatalf("PRs = %#v, want %#v", prs, wantPR)
}
}
func TestAzureProviderAuthorizesConfiguredProjectAndCanonicalRoute(t *testing.T) {
service := &fakeAzureMentionService{
project: &azuredevops.MentionProject{
OrganizationURL: "https://dev.azure.com/acme", ProjectID: "project-1", ProjectName: "Platform",
},
repository: &azuredevops.MentionRepository{
OrganizationURL: "https://dev.azure.com/acme", ProjectID: "project-1", ProjectName: "Platform",
RepositoryID: "repo-1", RepositoryName: "widgets",
},
}
workProvider := NewAzureWorkItemProvider(service)
prProvider := NewAzurePullRequestProvider(service)
workRef := apiv1.EntityReference{
Version: apiv1.EntityReferenceVersion,
Ref: canonicalRef("azure_devops", "work_item", "dev.azure.com/acme/project-1", "101"),
Provider: "azure_devops", Kind: "work_item", ID: "101", Key: "Platform#101", Title: "Fix auth",
URL: "https://dev.azure.com/acme/Platform/_workitems/edit/101", Scope: "dev.azure.com/acme/project-1",
}
prRef := apiv1.EntityReference{
Version: apiv1.EntityReferenceVersion,
Ref: canonicalRef("azure_devops", "pull_request", "dev.azure.com/acme/project-1/repo-1", "42"),
Provider: "azure_devops", Kind: "pull_request", ID: "42", Key: "Platform/widgets!42", Title: "Land auth",
URL: "https://dev.azure.com/acme/Platform/_git/widgets/pullrequest/42", Scope: "dev.azure.com/acme/project-1/repo-1",
}
for _, test := range []struct {
provider MentionProvider
ref apiv1.EntityReference
}{
{workProvider, workRef}, {prProvider, prRef},
} {
authorizer := test.provider.(ReferenceAuthorizer)
if err := authorizer.AuthorizeReference(t.Context(), ReferenceAuthorizationRequest{
WorkspaceID: "workspace-1", Purpose: ReferencePurposeSubmission, Reference: test.ref,
}); err != nil {
t.Fatalf("authorize %s: %v", test.ref.Kind, err)
}
invalid := test.ref
invalid.URL += "?redirect=1"
if err := authorizer.AuthorizeReference(t.Context(), ReferenceAuthorizationRequest{
WorkspaceID: "workspace-1", Purpose: ReferencePurposeSubmission, Reference: invalid,
}); err == nil {
t.Fatalf("unsafe %s URL authorized", test.ref.Kind)
}
}
foreign := workRef
foreign.Scope = "dev.azure.com/foreign/project-1"
foreign.Ref = canonicalRef("azure_devops", "work_item", foreign.Scope, foreign.ID)
if err := workProvider.(ReferenceAuthorizer).AuthorizeReference(t.Context(), ReferenceAuthorizationRequest{
WorkspaceID: "workspace-1", Purpose: ReferencePurposeSubmission, Reference: foreign,
}); err == nil {
t.Fatal("foreign organization scope authorized")
}
}
func TestAzureProviderClassifiesMissingConfig(t *testing.T) {
provider := NewAzureWorkItemProvider(&fakeAzureMentionService{err: azuredevops.ErrNotConfigured})
_, err := provider.Search(context.Background(), SearchRequest{WorkspaceID: "workspace-1", Query: "auth", Limit: 5})
var providerErr *ProviderError
if !errors.As(err, &providerErr) || providerErr.Status != StatusNotConfigured {
t.Fatalf("error = %v, want not configured", err)
}
}