188 lines
5.3 KiB
Go
188 lines
5.3 KiB
Go
package roadmapsync
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestIdentityNormalizeValid(t *testing.T) {
|
|
in := Identity{
|
|
RoadmapMilestonePath: " agent-roadmap/phase/p/milestones/m.md ",
|
|
Provider: " plane ",
|
|
Tenant: " general ",
|
|
Project: " NOMAD ",
|
|
WorkItemID: " NOMAD-13 ",
|
|
}
|
|
got, err := in.Normalize()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got.Shape != ShapeMilestone {
|
|
t.Errorf("shape = %q, want default milestone", got.Shape)
|
|
}
|
|
if got.RoadmapMilestonePath != "agent-roadmap/phase/p/milestones/m.md" {
|
|
t.Errorf("path = %q", got.RoadmapMilestonePath)
|
|
}
|
|
if got.RoadmapItemID != "m" {
|
|
t.Errorf("expected RoadmapItemID to be m, got %q", got.RoadmapItemID)
|
|
}
|
|
if got.Provider != "plane" || got.WorkItemID != "NOMAD-13" {
|
|
t.Errorf("provider/work item not trimmed: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestIdentityNormalizeRejectsMissingFields(t *testing.T) {
|
|
cases := map[string]Identity{
|
|
"missing provider": {
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
WorkItemID: "NOMAD-13",
|
|
},
|
|
"missing milestone path and item id": {
|
|
Provider: "plane",
|
|
WorkItemID: "NOMAD-13",
|
|
},
|
|
"task without parent": {
|
|
Shape: ShapeTask,
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
Provider: "plane",
|
|
WorkItemID: "NOMAD-13",
|
|
},
|
|
"unknown shape": {
|
|
Shape: Shape("epic"),
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
Provider: "plane",
|
|
WorkItemID: "NOMAD-13",
|
|
},
|
|
}
|
|
for name, in := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
if _, err := in.Normalize(); !errors.Is(err, ErrInvalidIdentity) {
|
|
t.Fatalf("err = %v, want ErrInvalidIdentity", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIdentityMatches(t *testing.T) {
|
|
base := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
Provider: "plane",
|
|
WorkItemID: "NOMAD-13",
|
|
}
|
|
same := base
|
|
same.Project = "NOMAD" // non-key field differs, still a match
|
|
if !base.Matches(same) {
|
|
t.Errorf("expected match ignoring non-key fields")
|
|
}
|
|
|
|
differentWorkItem := base
|
|
differentWorkItem.WorkItemID = "NOMAD-99"
|
|
if base.Matches(differentWorkItem) {
|
|
t.Errorf("different work item id must not match")
|
|
}
|
|
|
|
invalid := Identity{Provider: "plane"}
|
|
if base.Matches(invalid) {
|
|
t.Errorf("invalid identity must not match")
|
|
}
|
|
}
|
|
|
|
func TestIdentityMatchesStrict(t *testing.T) {
|
|
base := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
Provider: "plane",
|
|
Tenant: "general",
|
|
Project: "NOMAD",
|
|
WorkItemID: "NOMAD-13",
|
|
}
|
|
if !base.MatchesStrict(base) {
|
|
t.Errorf("identical identity must strict-match")
|
|
}
|
|
|
|
differentTenant := base
|
|
differentTenant.Tenant = "other-workspace"
|
|
if base.MatchesStrict(differentTenant) {
|
|
t.Errorf("different tenant must not strict-match")
|
|
}
|
|
|
|
differentProject := base
|
|
differentProject.Project = "OTHER"
|
|
if base.MatchesStrict(differentProject) {
|
|
t.Errorf("different project must not strict-match")
|
|
}
|
|
|
|
differentWorkItem := base
|
|
differentWorkItem.WorkItemID = "NOMAD-99"
|
|
if base.MatchesStrict(differentWorkItem) {
|
|
t.Errorf("different work item id must not strict-match")
|
|
}
|
|
|
|
// Matches (loose) ignores tenant/project; MatchesStrict must not.
|
|
if !base.Matches(differentTenant) {
|
|
t.Errorf("sanity: loose Matches should still ignore tenant")
|
|
}
|
|
}
|
|
|
|
func TestIdentityMatchesByMilestoneIDAcrossPhaseMove(t *testing.T) {
|
|
left := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p1/milestones/m.md",
|
|
Provider: "plane",
|
|
}
|
|
right := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p2/milestones/m.md", // moved phase
|
|
Provider: "plane",
|
|
}
|
|
if !left.Matches(right) {
|
|
t.Errorf("moved path must match if MilestoneID is same")
|
|
}
|
|
if !left.MatchesStrict(right) {
|
|
t.Errorf("moved path must strict-match if other key fields are same")
|
|
}
|
|
}
|
|
|
|
func TestIdentityMatchesStrictAllowsProviderWorkItemAttributeChange(t *testing.T) {
|
|
left := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
Provider: "plane",
|
|
Tenant: "general",
|
|
Project: "NOMAD",
|
|
WorkItemID: "NOMAD-13",
|
|
}
|
|
right := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
|
|
Provider: "plane",
|
|
Tenant: "general",
|
|
Project: "NOMAD",
|
|
WorkItemID: "NOMAD-13", // same work item id
|
|
}
|
|
if !left.MatchesStrict(right) {
|
|
t.Errorf("same work item id must still strict-match")
|
|
}
|
|
}
|
|
|
|
func TestIdentityNormalizeParsesSlugFromPath(t *testing.T) {
|
|
in := Identity{
|
|
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/some-slug.md",
|
|
Provider: "plane",
|
|
}
|
|
got, err := in.Normalize()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got.RoadmapItemID != "some-slug" {
|
|
t.Errorf("expected RoadmapItemID to be some-slug, got %q", got.RoadmapItemID)
|
|
}
|
|
}
|
|
|
|
func TestRevisionNormalizeDefaultsDevelop(t *testing.T) {
|
|
got := Revision{}.Normalize()
|
|
if got.Branch != DefaultBranch {
|
|
t.Errorf("branch = %q, want develop", got.Branch)
|
|
}
|
|
if !(Revision{Branch: "develop", Revision: "abc"}).OnDevelop() {
|
|
t.Errorf("expected OnDevelop true")
|
|
}
|
|
if (Revision{Branch: "feature/x"}).OnDevelop() {
|
|
t.Errorf("feature branch must not be OnDevelop")
|
|
}
|
|
}
|