mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-08-20 22:24:35 +00:00
* test: raise statement coverage to 90% and enforce it in CI Add unit tests across the cmd and internal packages to bring total statement coverage above 90%, and gate future regressions. - Cover CLI helpers, provider TUI handlers, resume/manifest paths, and error branches in config, llm, llmloop, scan, session, agent, viewer, mcp, pathutil, and telemetry. - Raise the coverage threshold from 80% to 90% in the Makefile (COVERAGE_THRESHOLD) and in the CI "Check coverage threshold" step. - Ignore generated coverage.out and coverage.html artifacts. Total statement coverage is now 90.5%, measured consistently by both `make coverage` and the CI `go test ./...` scope. * test: widen statement coverage margin with environment-independent unit tests Add table-driven unit tests for pure, environment-independent functions to raise the statement-coverage safety margin above the 90% threshold: - session.ResumeState.ValidateScanOptions (70% -> 100%) - rules.SystemRule.UnmarshalJSON error branches (71% -> 82%) - llmloop.stripMarkdownFences no-newline branch (82% -> 100%) - diff.firstLine empty/blank-input branch - diff.extractCodeBlock missing-newline and no-closing-fence branches - main.truncate n<=1 and normalization branches - agent.Agent nil-receiver accessor guards
226 lines
6.7 KiB
Go
226 lines
6.7 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright 2026 alibaba/open-code-review Contributors
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/alibaba/open-code-review/internal/llm"
|
|
)
|
|
|
|
func TestRegistryModelsForProvider(t *testing.T) {
|
|
t.Run("known preset ignores fallback", func(t *testing.T) {
|
|
got := registryModelsForProvider("anthropic", []string{"junk"})
|
|
if len(got) == 0 {
|
|
t.Fatal("expected preset models for anthropic")
|
|
}
|
|
for _, m := range got {
|
|
if m == "junk" {
|
|
t.Error("fallback leaked into preset result")
|
|
}
|
|
}
|
|
})
|
|
t.Run("unknown uses fallback", func(t *testing.T) {
|
|
got := registryModelsForProvider("no-such-provider", []string{"a", "b"})
|
|
if len(got) != 2 || got[0] != "a" || got[1] != "b" {
|
|
t.Errorf("got %v, want [a b]", got)
|
|
}
|
|
})
|
|
t.Run("unknown without fallback is nil", func(t *testing.T) {
|
|
if got := registryModelsForProvider("no-such-provider", nil); got != nil {
|
|
t.Errorf("got %v, want nil", got)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestApplyModelDeleteToEntry(t *testing.T) {
|
|
entry := ProviderEntry{Models: []string{"m1", "m2"}, Model: "m2"}
|
|
got := applyModelDeleteToEntry(entry, "m2")
|
|
if llm.ModelListContains(got.Models, "m2") {
|
|
t.Error("deleted model still present")
|
|
}
|
|
if got.Model != "" {
|
|
t.Errorf("active model = %q, want cleared", got.Model)
|
|
}
|
|
}
|
|
|
|
func TestClearCfgActiveModelIfDeleted(t *testing.T) {
|
|
t.Run("clears matching", func(t *testing.T) {
|
|
cfg := &Config{Provider: "p", Model: "m"}
|
|
clearCfgActiveModelIfDeleted(cfg, "p", "m")
|
|
if cfg.Model != "" {
|
|
t.Errorf("model = %q, want cleared", cfg.Model)
|
|
}
|
|
})
|
|
t.Run("keeps non-matching provider", func(t *testing.T) {
|
|
cfg := &Config{Provider: "other", Model: "m"}
|
|
clearCfgActiveModelIfDeleted(cfg, "p", "m")
|
|
if cfg.Model != "m" {
|
|
t.Errorf("model = %q, want unchanged", cfg.Model)
|
|
}
|
|
})
|
|
t.Run("nil cfg is a no-op", func(t *testing.T) {
|
|
clearCfgActiveModelIfDeleted(nil, "p", "m") // must not panic
|
|
})
|
|
}
|
|
|
|
func TestRollbackCfgActiveModel(t *testing.T) {
|
|
cfg := &Config{Provider: "p", Model: ""}
|
|
rollbackCfgActiveModel(cfg, "p", "prev")
|
|
if cfg.Model != "prev" {
|
|
t.Errorf("model = %q, want prev", cfg.Model)
|
|
}
|
|
// Different provider: unchanged.
|
|
cfg2 := &Config{Provider: "other", Model: "x"}
|
|
rollbackCfgActiveModel(cfg2, "p", "prev")
|
|
if cfg2.Model != "x" {
|
|
t.Errorf("model = %q, want x", cfg2.Model)
|
|
}
|
|
rollbackCfgActiveModel(nil, "p", "prev") // must not panic
|
|
}
|
|
|
|
func TestRollbackModelDelete(t *testing.T) {
|
|
t.Run("nil cfg is a no-op", func(t *testing.T) {
|
|
m := &modelTUIModel{existingCfg: nil}
|
|
m.rollbackModelDelete(ProviderEntry{}, "") // must not panic
|
|
})
|
|
|
|
t.Run("custom provider restores entry and active model", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Provider: "cp",
|
|
Model: "",
|
|
CustomProviders: map[string]ProviderEntry{"cp": {Models: []string{"a"}}},
|
|
}
|
|
m := &modelTUIModel{
|
|
existingCfg: cfg,
|
|
providerName: "cp",
|
|
isCustomProvider: true,
|
|
}
|
|
prev := ProviderEntry{Models: []string{"a", "b"}, Model: "b"}
|
|
m.rollbackModelDelete(prev, "b")
|
|
if got := cfg.CustomProviders["cp"]; len(got.Models) != 2 {
|
|
t.Errorf("restored models = %v, want 2 entries", got.Models)
|
|
}
|
|
if cfg.Model != "b" {
|
|
t.Errorf("active model = %q, want b", cfg.Model)
|
|
}
|
|
})
|
|
|
|
t.Run("official provider restores entry", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Provider: "op",
|
|
Providers: map[string]ProviderEntry{"op": {}},
|
|
}
|
|
m := &modelTUIModel{
|
|
existingCfg: cfg,
|
|
providerName: "op",
|
|
isCustomProvider: false,
|
|
}
|
|
prev := ProviderEntry{Models: []string{"x"}, Model: "x"}
|
|
m.rollbackModelDelete(prev, "x")
|
|
if got := cfg.Providers["op"]; len(got.Models) != 1 {
|
|
t.Errorf("restored models = %v, want 1 entry", got.Models)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestModelReloadConfigAfterSaveFailure(t *testing.T) {
|
|
t.Run("empty path returns false", func(t *testing.T) {
|
|
m := &modelTUIModel{configPath: ""}
|
|
if m.reloadConfigAfterSaveFailure() {
|
|
t.Error("expected false for empty configPath")
|
|
}
|
|
})
|
|
|
|
t.Run("reloads from disk", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(path, []byte(`{"provider":"cp","custom_providers":{"cp":{"models":["m1"]}}}`), 0o644); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
m := &modelTUIModel{
|
|
configPath: path,
|
|
providerName: "cp",
|
|
isCustomProvider: true,
|
|
}
|
|
if !m.reloadConfigAfterSaveFailure() {
|
|
t.Fatal("expected reload to succeed")
|
|
}
|
|
if m.existingCfg == nil || m.existingCfg.Provider != "cp" {
|
|
t.Errorf("reloaded cfg = %+v, want provider cp", m.existingCfg)
|
|
}
|
|
})
|
|
|
|
t.Run("parse error returns false", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "bad.json")
|
|
if err := os.WriteFile(path, []byte("{not json"), 0o644); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
m := &modelTUIModel{configPath: path}
|
|
if m.reloadConfigAfterSaveFailure() {
|
|
t.Error("expected false on parse error")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestProviderReloadConfigAfterSaveFailure(t *testing.T) {
|
|
t.Run("empty path returns false", func(t *testing.T) {
|
|
m := &providerTUIModel{configPath: ""}
|
|
if m.reloadConfigAfterSaveFailure() {
|
|
t.Error("expected false for empty configPath")
|
|
}
|
|
})
|
|
|
|
t.Run("reloads and refreshes custom providers", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.json")
|
|
if err := os.WriteFile(path, []byte(`{"custom_providers":{"cp":{"url":"http://x","models":["m1"]}}}`), 0o644); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
m := &providerTUIModel{configPath: path}
|
|
if !m.reloadConfigAfterSaveFailure() {
|
|
t.Fatal("expected reload to succeed")
|
|
}
|
|
if m.existingCfg == nil {
|
|
t.Fatal("existingCfg not set after reload")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestAdjustModelIdxAfterDelete(t *testing.T) {
|
|
cfg := &Config{
|
|
CustomProviders: map[string]ProviderEntry{"cp": {Models: []string{"a"}}},
|
|
}
|
|
m := &modelTUIModel{
|
|
existingCfg: cfg,
|
|
providerName: "cp",
|
|
isCustomProvider: true,
|
|
models: []string{"a", "b"},
|
|
modelIdx: 5, // out of range
|
|
}
|
|
m.adjustModelIdxAfterDelete()
|
|
if m.modelIdx != 0 {
|
|
t.Errorf("modelIdx = %d, want clamped to 0 (single remaining model)", m.modelIdx)
|
|
}
|
|
}
|
|
|
|
func TestRefreshModelSelectionAfterAdd(t *testing.T) {
|
|
t.Run("selects matching name", func(t *testing.T) {
|
|
m := &modelTUIModel{isCustomProvider: true, models: []string{"a", "b", "c"}}
|
|
m.refreshModelSelectionAfterAdd("b")
|
|
if m.modelIdx != 1 {
|
|
t.Errorf("modelIdx = %d, want 1", m.modelIdx)
|
|
}
|
|
})
|
|
t.Run("falls back to last when absent", func(t *testing.T) {
|
|
m := &modelTUIModel{isCustomProvider: true, models: []string{"a", "b"}}
|
|
m.refreshModelSelectionAfterAdd("missing")
|
|
if m.modelIdx != 1 {
|
|
t.Errorf("modelIdx = %d, want last index 1", m.modelIdx)
|
|
}
|
|
})
|
|
}
|