gitlink-cli/shortcuts/workflow/rules/triage_test.go

159 lines
4.3 KiB
Go

package rules
import (
"testing"
"github.com/gitlink-org/gitlink-cli/shortcuts/workflow"
)
func TestTriageRuleClassifiesByKeyword(t *testing.T) {
upstream := map[string]interface{}{
"open-issues": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"project_issues_index": "1", "title": "fix crash on startup", "body": "应用启动时崩溃"},
map[string]interface{}{"project_issues_index": "2", "title": "新增导出功能", "body": ""},
map[string]interface{}{"project_issues_index": "3", "title": "如何配置SSO", "body": "请问怎么配"},
},
},
"labels": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"id": 1, "name": "bug"},
map[string]interface{}{"id": 2, "name": "enhancement"},
map[string]interface{}{"id": 3, "name": "question"},
},
},
"members": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"login": "dev1", "name": "Dev One"},
},
},
}
resp, err := TriageRule(upstream, "triage")
if err != nil {
t.Fatalf("TriageRule failed: %v", err)
}
if resp.Analysis == nil {
t.Fatal("expected non-nil Analysis")
}
analysis, ok := resp.Analysis.(map[string]interface{})
if !ok {
t.Fatal("Analysis is not a map")
}
if v := analysis["classified"]; v.(int) != 3 {
t.Fatalf("expected 3 classified, got %v", v)
}
if len(resp.Actions) == 0 {
t.Fatal("expected actions for issue triage")
}
// Verify each action type.
for _, a := range resp.Actions {
if a.Type == "api" && a.Method != "PATCH" {
t.Errorf("unexpected API method: %s", a.Method)
}
}
}
func TestTriageRuleEmptyIssues(t *testing.T) {
upstream := map[string]interface{}{
"open-issues": map[string]interface{}{"data": []interface{}{}},
"labels": map[string]interface{}{"data": []interface{}{}},
"members": map[string]interface{}{"data": []interface{}{}},
}
resp, err := TriageRule(upstream, "triage")
if err != nil {
t.Fatalf("TriageRule failed: %v", err)
}
if len(resp.Actions) != 0 {
t.Fatalf("expected 0 actions for empty issues, got %d", len(resp.Actions))
}
analysis := resp.Analysis.(map[string]interface{})
if v := analysis["classified"]; v.(int) != 0 {
t.Fatalf("expected 0 classified, got %v", v)
}
}
func TestTriageRuleGoodFirstIssue(t *testing.T) {
upstream := map[string]interface{}{
"open-issues": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"project_issues_index": "10", "title": "good first issue: add docs", "body": "easy task for beginners"},
},
},
"labels": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"id": 1, "name": "docs"},
},
},
"members": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"login": "dev1", "name": "Dev"},
},
},
}
resp, err := TriageRule(upstream, "triage")
if err != nil {
t.Fatalf("TriageRule failed: %v", err)
}
hasComment := false
for _, a := range resp.Actions {
if a.Type == "cli" && a.Module == "issue" && a.Command == "+comment" {
hasComment = true
break
}
}
if !hasComment {
t.Fatal("expected a cli comment action for good first issue")
}
}
func TestTriageRulePriority(t *testing.T) {
cases := []struct {
title string
expected int
}{
{"紧急: 安全漏洞", 4},
{"重要功能", 3},
{"普通建议", 2},
{"低优先级改进", 1},
}
for _, tc := range cases {
upstream := map[string]interface{}{
"open-issues": map[string]interface{}{
"data": []interface{}{
map[string]interface{}{"project_issues_index": "1", "title": tc.title, "body": ""},
},
},
"labels": map[string]interface{}{"data": []interface{}{}},
"members": map[string]interface{}{"data": []interface{}{}},
}
resp, err := TriageRule(upstream, "triage")
if err != nil {
t.Fatalf("TriageRule failed for %q: %v", tc.title, err)
}
if len(resp.Actions) > 0 {
body := resp.Actions[0].Body
if v, ok := body["priority_id"]; ok {
if v.(int) != tc.expected {
t.Errorf("title=%q: priority_id=%v, want %d", tc.title, v, tc.expected)
}
}
}
}
}
func TestTriageRuleOutputFormat(t *testing.T) {
resp, err := TriageRule(map[string]interface{}{}, "triage")
if err != nil {
t.Fatalf("TriageRule failed: %v", err)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
// Verify it's a valid workflow.AIResponse.
var _ *workflow.AIResponse = resp
}