mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
142 lines
4.3 KiB
Go
142 lines
4.3 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSearchSkillsReturnsNormalizedClawHubCandidates(t *testing.T) {
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/search":
|
|
if got := r.URL.Query().Get("q"); got != "react" {
|
|
t.Fatalf("expected q=react, got %q", got)
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"results": []map[string]any{
|
|
{
|
|
"slug": "react",
|
|
"displayName": "React",
|
|
"summary": "React engineering skill",
|
|
"ownerHandle": "ivangdavila",
|
|
},
|
|
{
|
|
"slug": "react-expert",
|
|
"displayName": "React Expert",
|
|
"summary": "Advanced React review",
|
|
"ownerHandle": "veeramanikandanr48",
|
|
},
|
|
},
|
|
})
|
|
case "/skills/react":
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"skill": map[string]any{
|
|
"slug": "react",
|
|
"displayName": "React",
|
|
"summary": "React engineering skill",
|
|
"stats": map[string]any{
|
|
"installsAllTime": 62,
|
|
"stars": 3,
|
|
},
|
|
},
|
|
})
|
|
case "/skills/react-expert":
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"skill": map[string]any{
|
|
"slug": "react-expert",
|
|
"displayName": "React Expert",
|
|
"summary": "Advanced React review",
|
|
"stats": map[string]any{
|
|
"installsAllTime": 11,
|
|
"stars": 7,
|
|
},
|
|
},
|
|
})
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer upstream.Close()
|
|
|
|
oldBase := clawHubAPIBase
|
|
clawHubAPIBase = upstream.URL
|
|
t.Cleanup(func() { clawHubAPIBase = oldBase })
|
|
|
|
w := httptest.NewRecorder()
|
|
req := newRequest(http.MethodGet, "/api/skills/search?q=react", nil)
|
|
testHandler.SearchSkills(w, req)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("SearchSkills: expected 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var got []map[string]any
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("SearchSkills: decode response: %v", err)
|
|
}
|
|
if len(got) != 2 {
|
|
t.Fatalf("expected 2 candidates, got %d: %#v", len(got), got)
|
|
}
|
|
first := got[0]
|
|
if first["name"] != "React" {
|
|
t.Fatalf("expected normalized name, got %#v", first["name"])
|
|
}
|
|
if first["url"] != "https://clawhub.ai/ivangdavila/react" {
|
|
t.Fatalf("expected importable ClawHub URL, got %#v", first["url"])
|
|
}
|
|
if first["source"] != "clawhub.ai" {
|
|
t.Fatalf("expected source clawhub.ai, got %#v", first["source"])
|
|
}
|
|
if first["repo"] != nil {
|
|
t.Fatalf("repo should be null when ClawHub has no GitHub repo field, got %#v", first["repo"])
|
|
}
|
|
if first["github_stars"] != nil {
|
|
t.Fatalf("github_stars should not use ClawHub stars, got %#v", first["github_stars"])
|
|
}
|
|
if first["install_count"] != float64(62) {
|
|
t.Fatalf("expected install_count from details stats, got %#v", first["install_count"])
|
|
}
|
|
if first["description"] != "React engineering skill" {
|
|
t.Fatalf("expected description from summary, got %#v", first["description"])
|
|
}
|
|
}
|
|
|
|
func TestSearchSkillsEmptyQueryReturns400(t *testing.T) {
|
|
w := httptest.NewRecorder()
|
|
req := newRequest(http.MethodGet, "/api/skills/search?q=", nil)
|
|
testHandler.SearchSkills(w, req)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("SearchSkills empty query: expected 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
if !strings.Contains(w.Body.String(), "query is required") {
|
|
t.Fatalf("expected query is required error, got %s", w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSearchSkillsUpstreamUnavailableReturnsStructuredError(t *testing.T) {
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "temporary outage", http.StatusBadGateway)
|
|
}))
|
|
defer upstream.Close()
|
|
|
|
oldBase := clawHubAPIBase
|
|
clawHubAPIBase = upstream.URL
|
|
t.Cleanup(func() { clawHubAPIBase = oldBase })
|
|
|
|
w := httptest.NewRecorder()
|
|
req := newRequest(http.MethodGet, "/api/skills/search?q=react", nil)
|
|
testHandler.SearchSkills(w, req)
|
|
if w.Code != http.StatusBadGateway {
|
|
t.Fatalf("SearchSkills outage: expected 502, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
var got map[string]string
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("decode error body: %v", err)
|
|
}
|
|
if got["code"] != "upstream_unavailable" {
|
|
t.Fatalf("expected structured upstream_unavailable code, got %#v", got)
|
|
}
|
|
}
|