mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-11 23:40:28 -10:00
* stage 13: launch readiness (first-run + benchmark) First-run, model-agnostic-first: - provideronboarding: detect local OpenAI-compatible runtimes (Ollama/LM Studio) on their default ports and offer them with no API key required - provideronboarding: ClassifySetupProbe maps a live connectivity probe to a specific, fixable error class (bad key, wrong base URL, model not found, rate limit, config) instead of a stack trace; never panics - zero setup --verify: probe the provider after saving and report the fixable error on failure; print a concrete 'try this' headless example on success zero doctor hardening: - new sandbox.backend check (bubblewrap/sandbox-exec) with a platform-specific install remedy; warn (not fail) on policy-only fallback - new lsp.servers check listing missing language servers with per-binary install remedies; injectable GOOS/LookupExecutable for deterministic tests Self-correct surface + benchmark: - zero exec --self-correct wires the post-edit verify-and-correct loop via the project verifier; nil corrector when off keeps the agent loop byte-identical - perfbench task harness: reproducible Terminal-Bench-style runner over headless zero exec + stream-json; records model + commit + self-correct flag + date - zero-perf-bench tasks subcommand (with --dry-run) and a sample task set - docs/BENCHMARK.md: the number, the exact command, the model, and the with/without-self-correct methodology * stage 13: address review (verify-state, fixture paths, run_end exit, 404 classification) * stage 13: verifier inherits process environment (PATH/HOME/toolchain)
144 lines
5.4 KiB
Go
144 lines
5.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Gitlawb/zero/internal/config"
|
|
"github.com/Gitlawb/zero/internal/providerhealth"
|
|
"github.com/Gitlawb/zero/internal/tui"
|
|
)
|
|
|
|
func TestFormatSetupCompleteIncludesTryThisExample(t *testing.T) {
|
|
out := formatSetupComplete(tui.SetupResult{
|
|
ConfigPath: "/home/u/.config/zero/config.json",
|
|
Provider: config.ProviderProfile{
|
|
Name: "openai",
|
|
ProviderKind: config.ProviderKindOpenAI,
|
|
APIKey: "sk-x",
|
|
Model: "gpt-4.1",
|
|
},
|
|
})
|
|
if !strings.Contains(out, "Zero setup complete") {
|
|
t.Fatalf("missing completion header: %q", out)
|
|
}
|
|
// The working-state confirmation must include a concrete one-line "try this"
|
|
// example so the user can immediately verify a real run.
|
|
if !strings.Contains(out, `zero exec "`) || !strings.Contains(out, "--model gpt-4.1") {
|
|
t.Fatalf("missing try-this exec example: %q", out)
|
|
}
|
|
}
|
|
|
|
func TestRunSetupVerifyReportsClassifiedProbeFailure(t *testing.T) {
|
|
var stdout, stderr bytes.Buffer
|
|
configPath := filepath.Join(t.TempDir(), "config.json")
|
|
probed := false
|
|
deps := appDeps{
|
|
userConfigPath: func() (string, error) { return configPath, nil },
|
|
probeProviderHealth: func(_ context.Context, options providerhealth.Options) providerhealth.Result {
|
|
probed = true
|
|
if !options.Connectivity {
|
|
t.Fatalf("setup --verify must request a connectivity probe")
|
|
}
|
|
return providerhealth.Result{
|
|
Status: providerhealth.StatusFail,
|
|
Checks: []providerhealth.Check{
|
|
{ID: "provider.connectivity", Status: providerhealth.StatusFail, Category: providerhealth.CategoryAuth, Message: "Provider endpoint returned 401: invalid api key"},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
exitCode := runWithDeps([]string{"setup", "openai", "--api-key-env", "OPENAI_API_KEY", "--verify"}, &stdout, &stderr, deps)
|
|
|
|
if !probed {
|
|
t.Fatalf("expected setup --verify to run a probe")
|
|
}
|
|
if exitCode != exitProvider {
|
|
t.Fatalf("exit code = %d, want %d (a failed probe is a provider error)", exitCode, exitProvider)
|
|
}
|
|
output := stderr.String()
|
|
// The failure must be specific and fixable, not a stack trace.
|
|
if !strings.Contains(strings.ToLower(output), "api key") {
|
|
t.Fatalf("expected a fixable auth remedy on stderr, got %q", output)
|
|
}
|
|
}
|
|
|
|
func TestRunSetupVerifySucceedsAndConfirmsWorking(t *testing.T) {
|
|
var stdout, stderr bytes.Buffer
|
|
configPath := filepath.Join(t.TempDir(), "config.json")
|
|
deps := appDeps{
|
|
userConfigPath: func() (string, error) { return configPath, nil },
|
|
probeProviderHealth: func(context.Context, providerhealth.Options) providerhealth.Result {
|
|
return providerhealth.Result{
|
|
Status: providerhealth.StatusPass,
|
|
Checks: []providerhealth.Check{
|
|
{ID: "provider.connectivity", Status: providerhealth.StatusPass, Message: "reachable"},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
exitCode := runWithDeps([]string{"setup", "openai", "--api-key-env", "OPENAI_API_KEY", "--verify"}, &stdout, &stderr, deps)
|
|
|
|
if exitCode != exitSuccess {
|
|
t.Fatalf("exit code = %d, want %d: %s", exitCode, exitSuccess, stderr.String())
|
|
}
|
|
out := stdout.String()
|
|
if !strings.Contains(out, "Zero setup complete") {
|
|
t.Fatalf("missing completion header: %q", out)
|
|
}
|
|
if !strings.Contains(strings.ToLower(out), "verified") && !strings.Contains(strings.ToLower(out), "reachable") {
|
|
t.Fatalf("a passing probe should confirm the provider is verified/reachable, got %q", out)
|
|
}
|
|
}
|
|
|
|
func TestVerifySetupProviderWithoutProbeIsNotVerified(t *testing.T) {
|
|
// When no probe is wired, the verification did not run. It must report Ran=false
|
|
// (and not OK=true), so the caller never claims the provider is verified.
|
|
verification, err := verifySetupProvider(appDeps{probeProviderHealth: nil}, config.ProviderProfile{Name: "openai"})
|
|
if err != nil {
|
|
t.Fatalf("verifySetupProvider returned error: %v", err)
|
|
}
|
|
if verification.Ran {
|
|
t.Fatalf("a skipped probe must report Ran=false, got Ran=true")
|
|
}
|
|
if verification.OK {
|
|
t.Fatalf("a skipped probe must not report OK=true, got OK=true")
|
|
}
|
|
}
|
|
|
|
func TestSetupJSONPayloadGatesVerifiedOnRan(t *testing.T) {
|
|
// A verification that did not run must not emit a "verified" flag at all, so a
|
|
// machine consumer never reads a skipped probe as a passing verification.
|
|
payload := setupJSONPayload(tui.SetupResult{Provider: config.ProviderProfile{Name: "openai", Model: "gpt-4.1"}}, &setupVerification{Ran: false, OK: false, Summary: "probe unavailable; skipped"})
|
|
if _, present := payload["verified"]; present {
|
|
t.Fatalf("a probe that did not run must not emit a verified flag, got %#v", payload["verified"])
|
|
}
|
|
|
|
ranPayload := setupJSONPayload(tui.SetupResult{Provider: config.ProviderProfile{Name: "openai"}}, &setupVerification{Ran: true, OK: true, Summary: "reachable"})
|
|
if got, present := ranPayload["verified"]; !present || got != true {
|
|
t.Fatalf("a probe that ran and passed must emit verified=true, got present=%v value=%#v", present, got)
|
|
}
|
|
}
|
|
|
|
func TestRunSetupWithoutVerifyDoesNotProbe(t *testing.T) {
|
|
var stdout, stderr bytes.Buffer
|
|
configPath := filepath.Join(t.TempDir(), "config.json")
|
|
deps := appDeps{
|
|
userConfigPath: func() (string, error) { return configPath, nil },
|
|
probeProviderHealth: func(context.Context, providerhealth.Options) providerhealth.Result {
|
|
t.Fatal("setup without --verify must not probe")
|
|
return providerhealth.Result{}
|
|
},
|
|
}
|
|
|
|
exitCode := runWithDeps([]string{"setup", "ollama"}, &stdout, &stderr, deps)
|
|
if exitCode != exitSuccess {
|
|
t.Fatalf("exit code = %d, want %d: %s", exitCode, exitSuccess, stderr.String())
|
|
}
|
|
}
|