From ba80a852b52cdb7ff9d8f5865a755651da62eff6 Mon Sep 17 00:00:00 2001 From: sec-check Date: Sun, 27 Sep 2026 16:38:01 -0400 Subject: [PATCH] [quality] cover zero-hit funcs: idea matches handler, intake config, settle FromEnv, auth FakeHub handleIdeaMatches (pkg/api/wave2.go), intake AudioEnabled/HandleConfig, settle.FromEnv, and auth FakeHub.WhoAmI/WhoAmIBearer had 0% coverage. Adds tests exercising the 503 no-engine gate, fallback (no-LLM) hive+CNCF match scoring with hive-managed CNCF filtering and persistence, passed-repo exclusion, STT config toggling, and env token resolution. api 76.2%->78.6%, auth 82.6%->91.3%, settle 84.4%->89.1%, intake 88.3%->89.0% Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: sec-check --- pkg/api/idea_matches_test.go | 123 +++++++++++++++++++++++++++++++++++ pkg/auth/fake_test.go | 35 ++++++++++ pkg/intake/config_test.go | 47 +++++++++++++ pkg/settle/fromenv_test.go | 29 +++++++++ 4 files changed, 234 insertions(+) create mode 100644 pkg/api/idea_matches_test.go create mode 100644 pkg/auth/fake_test.go create mode 100644 pkg/intake/config_test.go create mode 100644 pkg/settle/fromenv_test.go diff --git a/pkg/api/idea_matches_test.go b/pkg/api/idea_matches_test.go new file mode 100644 index 0000000..32a6d28 --- /dev/null +++ b/pkg/api/idea_matches_test.go @@ -0,0 +1,123 @@ +package api + +import ( + "encoding/json" + "net/http" + "os" + "testing" + + "github.com/hivecommons/dibs/pkg/catalog" + "github.com/hivecommons/dibs/pkg/match" + "github.com/hivecommons/dibs/pkg/store" +) + +// seedCatalog writes a cncf-catalog.json into dir and opens a catalog store +// over it. One project deliberately shares its RepoID with a hive-managed +// registry repo so the handler's "keep CNCF section non-hive only" filter +// has something to drop. +func seedCatalog(t *testing.T, dir string) *catalog.Store { + t.Helper() + projects := []catalog.Project{ + {Name: "Dibs", RepoID: "kubestellar/dibs", RepoURL: "https://github.com/kubestellar/dibs", + Maturity: "sandbox", Category: "Exchange", Description: "ideas exchange kubernetes"}, + {Name: "Istio", RepoID: "istio/istio", RepoURL: "https://github.com/istio/istio", + Maturity: "graduated", Category: "Service Mesh", Description: "ideas exchange kubernetes mesh"}, + } + raw, err := json.Marshal(projects) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if err := os.WriteFile(dir+"/cncf-catalog.json", raw, 0o644); err != nil { + t.Fatalf("WriteFile: %v", err) + } + cs, err := catalog.New(dir, "") + if err != nil { + t.Fatalf("catalog.New: %v", err) + } + return cs +} + +func TestIdeaMatchesWithoutEngineIs503(t *testing.T) { + a, mux := newAPIFixture(t) + idea := mustCreate(t, a, "bob", "Sched idea", store.VisibilityPrivate, store.StatusDraft) + rec := do(t, mux, ident("bob"), "GET", "/api/ideas/"+idea.ID+"/matches", "") + if rec.Code != http.StatusServiceUnavailable { + t.Fatalf("matches without engine: status=%d, want 503", rec.Code) + } +} + +func TestIdeaMatchesFallbackScoringAndCNCFFilter(t *testing.T) { + a, mux := newAPIFixture(t) + a.Engine = &match.Engine{Store: a.Store, Registry: a.Registry, Catalog: seedCatalog(t, t.TempDir())} + + idea := mustCreate(t, a, "bob", "Kubernetes ideas exchange", store.VisibilityPrivate, store.StatusDraft) + rec := do(t, mux, ident("bob"), "GET", "/api/ideas/"+idea.ID+"/matches", "") + if rec.Code != http.StatusOK { + t.Fatalf("matches: status=%d body=%s", rec.Code, rec.Body.String()) + } + out := decodeBody[struct { + TLDR string `json:"tldr"` + Matches []matchView `json:"matches"` + CNCF []store.CNCFMatch `json:"cncf"` + }](t, rec) + if out.TLDR == "" { + t.Fatal("matches response has empty tldr; want fallback TLDR") + } + // The registry holds two repos, but the ideator feed is capped at + // maxIdeaHiveMatches (1) and returns full repo profiles. + if len(out.Matches) != maxIdeaHiveMatches { + t.Fatalf("hive matches = %+v, want %d entry", out.Matches, maxIdeaHiveMatches) + } + if out.Matches[0].Repo == nil || out.Matches[0].Repo.RepoID == "" { + t.Fatalf("hive match missing repo profile: %+v", out.Matches[0]) + } + // kubestellar/dibs is hive-managed, so only istio/istio may appear in + // the CNCF companion section. + for _, m := range out.CNCF { + if m.RepoID == "kubestellar/dibs" { + t.Fatalf("cncf section includes hive-managed repo: %+v", out.CNCF) + } + } + if len(out.CNCF) != 1 || out.CNCF[0].RepoID != "istio/istio" { + t.Fatalf("cncf matches = %+v, want exactly istio/istio", out.CNCF) + } + + // The scored matches were persisted on the idea. + stored, err := a.Store.Get(idea.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if len(stored.Matches) == 0 || stored.MatchesUpdatedAt.IsZero() { + t.Fatalf("stored idea matches = %+v (updated %v), want persisted scores", stored.Matches, stored.MatchesUpdatedAt) + } + if stored.TLDR == "" { + t.Fatal("stored idea TLDR empty; want persisted fallback TLDR") + } +} + +func TestIdeaMatchesSkipsPassedAndOfferedRepos(t *testing.T) { + a, mux := newAPIFixture(t) + a.Engine = &match.Engine{Store: a.Store, Registry: a.Registry} + + idea := mustCreate(t, a, "bob", "Kubernetes ideas exchange", store.VisibilityPrivate, store.StatusDraft) + if _, err := a.Store.Mutate(idea.ID, false, func(i *store.Idea) error { + i.PassedRepos = append(i.PassedRepos, "kubestellar/dibs", "org/other") + return nil + }); err != nil { + t.Fatalf("Mutate: %v", err) + } + rec := do(t, mux, ident("bob"), "GET", "/api/ideas/"+idea.ID+"/matches", "") + if rec.Code != http.StatusOK { + t.Fatalf("matches: status=%d body=%s", rec.Code, rec.Body.String()) + } + out := decodeBody[struct { + Matches []matchView `json:"matches"` + CNCF []store.CNCFMatch `json:"cncf"` + }](t, rec) + if len(out.Matches) != 0 { + t.Fatalf("matches = %+v, want none after passing on every repo", out.Matches) + } + if len(out.CNCF) != 0 { + t.Fatalf("cncf = %+v, want empty with nil catalog", out.CNCF) + } +} diff --git a/pkg/auth/fake_test.go b/pkg/auth/fake_test.go new file mode 100644 index 0000000..51df864 --- /dev/null +++ b/pkg/auth/fake_test.go @@ -0,0 +1,35 @@ +package auth + +import ( + "context" + "errors" + "testing" +) + +func TestFakeHubWhoAmI(t *testing.T) { + f := &FakeHub{Sessions: map[string]Identity{"cookie-1": {Username: "alice"}}} + + id, err := f.WhoAmI(context.Background(), "cookie-1") + if err != nil || id == nil || id.Username != "alice" { + t.Fatalf("WhoAmI(cookie-1) = %+v, %v; want alice", id, err) + } + + id, err = f.WhoAmI(context.Background(), "unknown") + if !errors.Is(err, ErrUnauthenticated) || id != nil { + t.Fatalf("WhoAmI(unknown) = %+v, %v; want ErrUnauthenticated", id, err) + } +} + +func TestFakeHubWhoAmIBearer(t *testing.T) { + f := &FakeHub{BearerTokens: map[string]Identity{"tok-1": {Username: "bob"}}} + + id, err := f.WhoAmIBearer(context.Background(), "tok-1") + if err != nil || id == nil || id.Username != "bob" { + t.Fatalf("WhoAmIBearer(tok-1) = %+v, %v; want bob", id, err) + } + + id, err = f.WhoAmIBearer(context.Background(), "unknown") + if !errors.Is(err, ErrUnauthenticated) || id != nil { + t.Fatalf("WhoAmIBearer(unknown) = %+v, %v; want ErrUnauthenticated", id, err) + } +} diff --git a/pkg/intake/config_test.go b/pkg/intake/config_test.go new file mode 100644 index 0000000..f32c1ab --- /dev/null +++ b/pkg/intake/config_test.go @@ -0,0 +1,47 @@ +package intake + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +func TestAudioEnabled(t *testing.T) { + t.Setenv("DIBS_STT_URL", "") + if AudioEnabled() { + t.Fatal("AudioEnabled() = true with DIBS_STT_URL unset") + } + t.Setenv("DIBS_STT_URL", " ") + if AudioEnabled() { + t.Fatal("AudioEnabled() = true with whitespace-only DIBS_STT_URL") + } + t.Setenv("DIBS_STT_URL", "http://stt.local") + if !AudioEnabled() { + t.Fatal("AudioEnabled() = false with DIBS_STT_URL set") + } +} + +func TestHandleConfig(t *testing.T) { + for _, tc := range []struct { + sttURL string + want bool + }{ + {"", false}, + {"http://stt.local", true}, + } { + t.Setenv("DIBS_STT_URL", tc.sttURL) + rec := httptest.NewRecorder() + HandleConfig(rec, httptest.NewRequest("GET", "/api/intake/config", nil)) + if rec.Code != http.StatusOK { + t.Fatalf("HandleConfig status = %d, want 200", rec.Code) + } + var body map[string]bool + if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { + t.Fatalf("decode %q: %v", rec.Body.String(), err) + } + if body["audio"] != tc.want { + t.Fatalf("audio = %v with DIBS_STT_URL=%q, want %v", body["audio"], tc.sttURL, tc.want) + } + } +} diff --git a/pkg/settle/fromenv_test.go b/pkg/settle/fromenv_test.go new file mode 100644 index 0000000..f9e90bb --- /dev/null +++ b/pkg/settle/fromenv_test.go @@ -0,0 +1,29 @@ +package settle + +import "testing" + +func TestFromEnvUnsetReturnsNil(t *testing.T) { + t.Setenv(EnvGitHubToken, "") + t.Setenv("IDEATE_GITHUB_TOKEN", "") + if c := FromEnv(); c != nil { + t.Fatalf("FromEnv() with no token = %+v, want nil", c) + } +} + +func TestFromEnvPrimaryToken(t *testing.T) { + t.Setenv(EnvGitHubToken, " primary-tok ") + t.Setenv("IDEATE_GITHUB_TOKEN", "legacy-tok") + c := FromEnv() + if c == nil || c.Token != "primary-tok" { + t.Fatalf("FromEnv() = %+v, want Token=primary-tok", c) + } +} + +func TestFromEnvLegacyFallback(t *testing.T) { + t.Setenv(EnvGitHubToken, "") + t.Setenv("IDEATE_GITHUB_TOKEN", "legacy-tok") + c := FromEnv() + if c == nil || c.Token != "legacy-tok" { + t.Fatalf("FromEnv() = %+v, want Token=legacy-tok", c) + } +}