Files
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

117 lines
3.7 KiB
Go

package entityrefs
import (
"encoding/json"
"errors"
"testing"
apiv1 "github.com/kandev/kandev/pkg/api/v1"
)
func TestNormalizeForSubmissionCanonicalizesAndDeduplicates(t *testing.T) {
ref := apiv1.EntityReference{
Version: apiv1.EntityReferenceVersion,
Ref: CanonicalRef("jira", "issue", " site-a ", " 10001 "),
Provider: " jira ",
Kind: " issue ",
ID: " 10001 ",
Key: " ENG-7\n",
Title: " Fix\n authentication ",
URL: " https://jira.example.test/browse/ENG-7 ",
Scope: " site-a ",
}
got, err := NormalizeForSubmission([]apiv1.EntityReference{ref, ref})
if err != nil {
t.Fatalf("normalize: %v", err)
}
if len(got) != 1 {
t.Fatalf("references = %#v, want one deduplicated reference", got)
}
want := apiv1.EntityReference{
Version: apiv1.EntityReferenceVersion,
Ref: CanonicalRef("jira", "issue", "site-a", "10001"),
Provider: "jira",
Kind: "issue",
ID: "10001",
Key: "ENG-7",
Title: "Fix authentication",
URL: "https://jira.example.test/browse/ENG-7",
Scope: "site-a",
}
if got[0] != want {
t.Fatalf("reference = %#v, want %#v", got[0], want)
}
}
func TestNormalizeForSubmissionRejectsMalformedKnownVersion(t *testing.T) {
valid := apiv1.EntityReference{
Version: apiv1.EntityReferenceVersion,
Ref: CanonicalRef("kandev", "task", "workspace-1", "task-1"),
Provider: "kandev", Kind: "task", ID: "task-1",
Title: "Task", URL: "/t/task-1", Scope: "workspace-1",
}
tests := []struct {
name string
edit func(*apiv1.EntityReference)
}{
{name: "spoofed ref", edit: func(ref *apiv1.EntityReference) { ref.Ref = "mention:v1:kandev:task:workspace-1:other" }},
{name: "unsafe scheme", edit: func(ref *apiv1.EntityReference) { ref.URL = "javascript:alert(1)" }},
{name: "external internal route", edit: func(ref *apiv1.EntityReference) {
ref.Provider = "jira"
ref.Ref = CanonicalRef("jira", "task", ref.Scope, ref.ID)
}},
{name: "wrong task route", edit: func(ref *apiv1.EntityReference) { ref.URL = "/t/other" }},
{name: "empty title", edit: func(ref *apiv1.EntityReference) { ref.Title = " \n " }},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ref := valid
test.edit(&ref)
_, err := NormalizeForSubmission([]apiv1.EntityReference{ref})
if !errors.Is(err, ErrInvalidReference) {
t.Fatalf("error = %v, want invalid reference", err)
}
})
}
}
func TestNormalizeForSubmissionOmitsUnknownVersion(t *testing.T) {
got, err := NormalizeForSubmission([]apiv1.EntityReference{{
Version: 99, Ref: "future", Provider: "future", Kind: "thing", ID: "1",
Title: "Future", URL: "https://future.test/1", Scope: "tenant",
}})
if err != nil {
t.Fatalf("normalize: %v", err)
}
if len(got) != 0 {
t.Fatalf("references = %#v, want unknown version omitted", got)
}
}
func TestNormalizePersistedDecodesJSONRoundTripAndOmitsMalformed(t *testing.T) {
valid := apiv1.EntityReference{
Version: apiv1.EntityReferenceVersion,
Ref: CanonicalRef("linear", "issue", "acme", "issue-1"),
Provider: "linear", Kind: "issue", ID: "issue-1", Key: "ENG-1",
Title: "Fix auth", URL: "https://linear.app/acme/issue/ENG-1", Scope: "acme",
}
rawJSON, err := json.Marshal([]apiv1.EntityReference{valid, {
Version: apiv1.EntityReferenceVersion,
Ref: "bad", Provider: "linear", Kind: "issue", ID: "bad",
Title: "Bad", URL: "https://linear.app/bad", Scope: "acme",
}})
if err != nil {
t.Fatalf("marshal: %v", err)
}
var roundTripped any
if err := json.Unmarshal(rawJSON, &roundTripped); err != nil {
t.Fatalf("unmarshal: %v", err)
}
got := NormalizePersisted(roundTripped)
if len(got) != 1 || got[0] != valid {
t.Fatalf("references = %#v, want only valid typed reference", got)
}
}