Compare commits

...
Author SHA1 Message Date
Vasanthdev2004 8c7210f8d2 test(tui): assert no checkout path reaches the view in the enclosing-checkout test
The test checked the branch strings and that the isolated cwd shows,
but a view could show both the isolated cwd and a real checkout path
without failing it. It now also asserts that neither the enclosing
checkout nor the directory the tests run from appears in the view.
2026-10-22 14:11:35 +05:30
Vasanthdev2004 523cbf3b66 test(tui): clear the branch in view-test models instead of trusting the cwd
isolatedCwd is a child of the isolated home, which lives under the
temporary directory. With TMPDIR inside a checkout, gitBranch walks up
from that absent path to the checkout and puts its branch in the title
bar, so the guard failed on an ordinary branch and a colliding branch
broke the nine view tests again.

newViewTestModel now builds those models: it sets the isolated cwd and
clears the branch after construction, since no path can promise there
is no checkout above it. The guard keeps only what a path can promise
(fully qualified and absent), and a new test puts the home inside a
checkout on feat/model-picker and checks that neither that branch nor
the real checkout path reaches the view.
2026-10-22 14:05:11 +05:30
Vasanthdev2004 e7fe1b47e4 Merge remote-tracking branch 'origin/main' into test/tui-overlay-cwd 2026-10-22 13:53:46 +05:30
Vasanthdev2004 c91bc7f26b Merge remote-tracking branch 'origin/main' into test/tui-overlay-cwd 2026-10-21 15:57:54 +05:30
Vasanthdev2004 e3a89846e0 test(tui): make isolatedCwd fully qualified and absent by construction
The fixed /workspace/zero path could exist on some machine, a dev
container for one, and newModel would then read whatever is there. On
Windows it was also only drive-relative: \workspace\zero is not a fully
qualified path.

isolatedCwd is now a child of the home TestMain isolates that nothing
creates, so it is fully qualified, absent and outside any checkout on
every platform, and the title bar shows ~/not-a-checkout everywhere. A
t.TempDir path would have put the user name and the test name into the
view. The guard test checks all three properties.
2026-10-21 15:50:14 +05:30
Vasanthdev2004 b4c30d70d4 test(tui): pin the cwd in tests that assert text is absent from the view
Nine tests built their model with an empty Options.Cwd, so newModel took
the package directory and the title bar showed the developer's real
branch and checkout path. Each then asserted that some text was absent
from the whole view, and failed wherever that branch or path happened to
contain it: TestSuggestionOverlayRenders on a branch with /model in its
name, TestSuggestionOverlayCapsRowsWithoutMoreText on a branch or path
containing "more" or "compact", the provider wizard tests on "offline",
"chatgpt" or "gpt-4.1", and so on.

They now pin Options.Cwd to isolatedCwd, a path that does not exist, so
no branch is read and the path is the same on every machine. A guard
test checks that isolatedCwd stays outside any git checkout.
2026-10-21 12:26:12 +05:30
7 changed files with 97 additions and 9 deletions

No files matched your search

+1 -1
View File
@@ -23,7 +23,7 @@ func testAskUserRequest() agent.AskUserRequest {
func newAskUserModel(t *testing.T, request agent.AskUserRequest, answers *[][]string) model {
t.Helper()
m := newModel(context.Background(), Options{})
m := newViewTestModel(t, Options{})
m.pending = true
m.activeRunID = 7
m.width = 96
+2 -2
View File
@@ -306,7 +306,7 @@ func TestSuggestionsSuppressedDuringSpecReview(t *testing.T) {
}
func TestSuggestionOverlayRenders(t *testing.T) {
m := newModel(context.Background(), Options{})
m := newViewTestModel(t, Options{})
m.width, m.height = 96, 30
m = typeRunes(t, m, "/mo")
@@ -387,7 +387,7 @@ func TestOverlayViewportLinesPreservesTextOutsidePanel(t *testing.T) {
}
func TestSuggestionOverlayCapsRowsWithoutMoreText(t *testing.T) {
m := newModel(context.Background(), Options{})
m := newViewTestModel(t, Options{})
m.width, m.height = 96, 30
m = typeRunes(t, m, "/")
+1 -1
View File
@@ -263,7 +263,7 @@ func TestMCPManagerNavigationOpensAddWizard(t *testing.T) {
}
func TestMCPManagerSearchFiltersMarketplace(t *testing.T) {
m := newModel(context.Background(), Options{})
m := newViewTestModel(t, Options{})
m.width = 120
m.height = 36
m.input.SetValue("/mcp")
+88
View File
@@ -4,12 +4,100 @@ import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/Twigpine/zero/internal/config"
"github.com/Twigpine/zero/internal/testutil"
)
// newViewTestModel builds the model for a test that asserts on the whole view.
// With an empty Options.Cwd, newModel uses this package's directory, so the
// title bar shows the real branch and checkout path, and a check that some text
// is absent fails wherever either one contains it: a branch named
// feat/model-picker breaks a "/model" check, a checkout under ~/more breaks a
// "more" check.
//
// The cwd is isolatedCwd, so the path in the title bar is the same on every
// machine. The branch is cleared after construction, because no path can
// promise there is none: gitBranch walks up from the cwd even though it does
// not exist, so a temporary directory inside a checkout would still put that
// checkout's branch in the title bar.
func newViewTestModel(t *testing.T, options Options) model {
t.Helper()
options.Cwd = isolatedCwd(t)
m := newModel(context.Background(), options)
m.gitBranch = ""
return m
}
// isolatedCwd is a child of the home TestMain isolates that nothing creates:
// fully qualified on every platform and holding nothing newModel could read.
// The title bar shows it as ~/not-a-checkout. A t.TempDir path would carry the
// user name and the test name into the view instead.
func isolatedCwd(t *testing.T) string {
t.Helper()
home, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
return filepath.Join(home, "not-a-checkout")
}
// Guards isolatedCwd: the view tests rely on a fully qualified path that does
// not exist. Whether a checkout encloses it depends on where the temporary
// directory is, which is why newViewTestModel clears the branch instead.
func TestIsolatedCwdIsAbsentAndFullyQualified(t *testing.T) {
cwd := isolatedCwd(t)
if !filepath.IsAbs(cwd) {
t.Fatalf("isolatedCwd = %q, want a fully qualified path", cwd)
}
if _, err := os.Lstat(cwd); !os.IsNotExist(err) {
t.Fatalf("isolatedCwd %q must not exist, got %v", cwd, err)
}
}
// A temporary directory inside a checkout puts the home, and so isolatedCwd,
// under that checkout. Its branch must still stay out of the view, or a branch
// named feat/model-picker fails the "/model" check again.
func TestViewTestModelKeepsAnEnclosingCheckoutsBranchOutOfTheView(t *testing.T) {
checkout := t.TempDir()
if err := os.MkdirAll(filepath.Join(checkout, ".git"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(checkout, ".git", "HEAD"), []byte("ref: refs/heads/feat/model-picker\n"), 0o644); err != nil {
t.Fatal(err)
}
home := filepath.Join(checkout, "home")
t.Setenv("HOME", home)
t.Setenv("USERPROFILE", home)
if got := gitBranch(isolatedCwd(t)); got != "feat/model-picker" {
t.Fatalf("fixture: gitBranch(isolatedCwd) = %q, want the enclosing checkout's feat/model-picker", got)
}
m := newViewTestModel(t, Options{})
m.width, m.height = 96, 30
m = typeRunes(t, m, "/mo")
plain := plainRender(t, m.View())
if strings.Contains(plain, "feat/model-picker") || strings.Contains(plain, "/model") {
t.Fatalf("the enclosing checkout's branch reached the view:\n%s", plain)
}
// The path is the other half: the title bar shows the isolated cwd, not the
// checkout the tests happen to run from or the one enclosing the home.
if !strings.Contains(plain, "not-a-checkout") {
t.Fatalf("the title bar does not show the isolated cwd:\n%s", plain)
}
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
for _, path := range []string{checkout, wd} {
if strings.Contains(plain, path) {
t.Fatalf("the view shows the checkout path %s:\n%s", path, plain)
}
}
}
// Guards TestMain: the per-user directories every newModel reads (and the ones
// tests could write to) must resolve inside the isolated root on every OS.
func TestUserDirsAreIsolatedFromTheDeveloperHome(t *testing.T) {
+1 -1
View File
@@ -357,7 +357,7 @@ func TestInitialRenderShowsLimeChatSurface(t *testing.T) {
}
func TestEmptyStateCollapsesAfterFirstPrompt(t *testing.T) {
m := newModel(context.Background(), Options{})
m := newViewTestModel(t, Options{})
m.width = 100
m.height = 30
m.input.SetValue("inspect the repo")
+3 -3
View File
@@ -562,7 +562,7 @@ func TestProviderWizardCustomCompatibleProviderDerivesIPName(t *testing.T) {
}
func TestProviderWizardSkipsAPIKeyForLocalProvidersAndEscCloses(t *testing.T) {
m := newModel(context.Background(), Options{})
m := newViewTestModel(t, Options{})
m = openProviderWizardForTest(t, m)
m.providerWizard.selectedProvider = providerWizardProviderIndex(t, m.providerWizard, "ollama")
@@ -773,7 +773,7 @@ func TestProviderWizardUsesAPIKeyEnvForCurrentSessionWithoutPersistingSecret(t *
func TestProviderWizardUsesLiveDiscoveredModels(t *testing.T) {
var captured config.ProviderProfile
m := newModel(context.Background(), Options{
m := newViewTestModel(t, Options{
DiscoverProviderModels: func(ctx context.Context, profile config.ProviderProfile) ([]providermodeldiscovery.Model, error) {
captured = profile
return []providermodeldiscovery.Model{{ID: "live-b"}, {ID: "live-a"}}, nil
@@ -874,7 +874,7 @@ func TestProviderWizardIgnoresStaleDiscoveryForSameProvider(t *testing.T) {
}
func TestProviderWizardKeepsFallbackModelsWhenLiveDiscoveryFails(t *testing.T) {
m := newModel(context.Background(), Options{
m := newViewTestModel(t, Options{
DiscoverProviderModels: func(context.Context, config.ProviderProfile) ([]providermodeldiscovery.Model, error) {
return nil, errors.New("offline")
},
+1 -1
View File
@@ -1585,7 +1585,7 @@ func TestTitleBarHighlightsBranchOverWorkspace(t *testing.T) {
}
func TestGenericCustomProviderDisplayUsesEndpointName(t *testing.T) {
m := newModel(context.Background(), Options{
m := newViewTestModel(t, Options{
ProviderName: "custom-openai-compatible",
ModelName: "MiniMax-M3",
ProviderProfile: config.ProviderProfile{