- Replace custom contains/searchString with strings.Contains in tests - Fix variable shadow (r -> reposJSON) in workspace handler - Wire daemon auth token + server URL into spawned agent env vars - Remove unused CreateAgentTaskWithContext query (dead code after refactor) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/multica-ai/multica/server/internal/cli"
|
|
)
|
|
|
|
func TestTruncateID(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
id string
|
|
want string
|
|
}{
|
|
{"short", "abc", "abc"},
|
|
{"exact 8", "abcdefgh", "abcdefgh"},
|
|
{"longer than 8", "abcdefgh-1234-5678", "abcdefgh"},
|
|
{"empty", "", ""},
|
|
{"unicode", "日本語テスト文字列追加", "日本語テスト文字"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := truncateID(tt.id)
|
|
if got != tt.want {
|
|
t.Errorf("truncateID(%q) = %q, want %q", tt.id, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFormatAssignee(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
issue map[string]any
|
|
want string
|
|
}{
|
|
{"empty", map[string]any{}, ""},
|
|
{"no type", map[string]any{"assignee_id": "abc"}, ""},
|
|
{"no id", map[string]any{"assignee_type": "member"}, ""},
|
|
{"member", map[string]any{"assignee_type": "member", "assignee_id": "abcdefgh-1234"}, "member:abcdefgh"},
|
|
{"agent", map[string]any{"assignee_type": "agent", "assignee_id": "xyz"}, "agent:xyz"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := formatAssignee(tt.issue)
|
|
if got != tt.want {
|
|
t.Errorf("formatAssignee() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolveAssignee(t *testing.T) {
|
|
membersResp := []map[string]any{
|
|
{"user_id": "user-1111", "name": "Alice Smith"},
|
|
{"user_id": "user-2222", "name": "Bob Jones"},
|
|
}
|
|
agentsResp := []map[string]any{
|
|
{"id": "agent-3333", "name": "CodeBot"},
|
|
}
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/api/workspaces/ws-1/members":
|
|
json.NewEncoder(w).Encode(membersResp)
|
|
case "/api/agents":
|
|
json.NewEncoder(w).Encode(agentsResp)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
|
|
client := cli.NewAPIClient(srv.URL, "ws-1", "test-token")
|
|
ctx := context.Background()
|
|
|
|
t.Run("exact match member", func(t *testing.T) {
|
|
aType, aID, err := resolveAssignee(ctx, client, "Alice Smith")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if aType != "member" || aID != "user-1111" {
|
|
t.Errorf("got (%q, %q), want (member, user-1111)", aType, aID)
|
|
}
|
|
})
|
|
|
|
t.Run("case-insensitive substring", func(t *testing.T) {
|
|
aType, aID, err := resolveAssignee(ctx, client, "bob")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if aType != "member" || aID != "user-2222" {
|
|
t.Errorf("got (%q, %q), want (member, user-2222)", aType, aID)
|
|
}
|
|
})
|
|
|
|
t.Run("match agent", func(t *testing.T) {
|
|
aType, aID, err := resolveAssignee(ctx, client, "codebot")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if aType != "agent" || aID != "agent-3333" {
|
|
t.Errorf("got (%q, %q), want (agent, agent-3333)", aType, aID)
|
|
}
|
|
})
|
|
|
|
t.Run("no match", func(t *testing.T) {
|
|
_, _, err := resolveAssignee(ctx, client, "nobody")
|
|
if err == nil {
|
|
t.Fatal("expected error for no match")
|
|
}
|
|
})
|
|
|
|
t.Run("ambiguous", func(t *testing.T) {
|
|
// Both "Alice Smith" and "Bob Jones" contain a space — but let's use a broader query
|
|
// "e" matches "Alice Smith" and "Bob Jones" and "CodeBot"
|
|
_, _, err := resolveAssignee(ctx, client, "o")
|
|
if err == nil {
|
|
t.Fatal("expected error for ambiguous match")
|
|
}
|
|
if got := err.Error(); !strings.Contains(got, "ambiguous") {
|
|
t.Errorf("expected ambiguous error, got: %s", got)
|
|
}
|
|
})
|
|
|
|
t.Run("missing workspace ID", func(t *testing.T) {
|
|
noWSClient := cli.NewAPIClient(srv.URL, "", "test-token")
|
|
_, _, err := resolveAssignee(ctx, noWSClient, "alice")
|
|
if err == nil {
|
|
t.Fatal("expected error for missing workspace ID")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestValidIssueStatuses(t *testing.T) {
|
|
expected := map[string]bool{
|
|
"backlog": true,
|
|
"todo": true,
|
|
"in_progress": true,
|
|
"in_review": true,
|
|
"done": true,
|
|
"blocked": true,
|
|
"cancelled": true,
|
|
}
|
|
for _, s := range validIssueStatuses {
|
|
if !expected[s] {
|
|
t.Errorf("unexpected status in validIssueStatuses: %q", s)
|
|
}
|
|
}
|
|
if len(validIssueStatuses) != len(expected) {
|
|
t.Errorf("validIssueStatuses has %d entries, expected %d", len(validIssueStatuses), len(expected))
|
|
}
|
|
}
|
|
|