nomadcode/services/core/internal/roadmapsync/identity_test.go
toki fad5627ae6 feat: milestone work item creation/sync 구현 및 roadmap sync 기능 추가
- Plane Todo projection 및 idempotency/retry 메커니즘 구현
- Roadmap sync identity 및 step 모델/DB 마이그레이션/쿼리 추가
- OpenAI 및 Plane adapter 개선
- roadmaps sync package: plane_format, plane_projection, retry 모듈 추가
- Storage layer: roadmap_sync_identities, roadmap_sync_steps 추가
- agent-task 아카이브 정리
2026-06-14 04:08:15 +09:00

132 lines
3.6 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.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 work item id": {
RoadmapMilestonePath: "agent-roadmap/phase/p/milestones/m.md",
Provider: "plane",
},
"missing milestone path": {
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")
}
// Matches (loose) ignores tenant/project; MatchesStrict must not.
if !base.Matches(differentTenant) {
t.Errorf("sanity: loose Matches should still ignore tenant")
}
}
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")
}
}