package main import ( "bytes" "context" "strings" "testing" "chub/internal/domain" "chub/internal/platform/browser" "chub/internal/platform/config" "chub/internal/ui" ) func TestRunCLIRejectsUnknownCommandWithStableCode(t *testing.T) { var out bytes.Buffer if code := runCLI([]string{"wat"}, &out, &out); code != 2 || !strings.Contains(out.String(), `"invalid_argument"`) { t.Fatalf("code=%d output=%s", code, out.String()) } } func TestInstanceStarterBuildsLaunchSpecFromInstanceAndSettings(t *testing.T) { launcher := &fakeProcessLauncher{handle: fakeProcessHandle{pid: 4242}} resolver := &fakeExecutableResolver{path: `C:\Browser\msedge.exe`} starter := instanceStarter{launcher: launcher, resolver: resolver} pid, err := starter.Start(context.Background(), ui.InstanceRow{ID: "edge-a", Browser: "Edge", UserDataDir: `C:\profiles\edge-a`}, ui.SettingsState{EdgePath: `C:\Configured\msedge.exe`}) if err != nil || pid != 4242 { t.Fatalf("Start() = %d, %v", pid, err) } if resolver.kind != domain.BrowserEdge || resolver.configured != `C:\Configured\msedge.exe` { t.Fatalf("resolver input = %s, %q", resolver.kind, resolver.configured) } if launcher.spec.Kind != domain.BrowserEdge || launcher.spec.Executable != resolver.path || launcher.spec.TargetURL != "" { t.Fatalf("launch spec = %#v", launcher.spec) } } func TestMergeInstanceConfigPreservesExistingLaunchOptions(t *testing.T) { existing := []config.Instance{{ID: "one", Name: "旧名称", Launch: domain.LaunchSpec{Kind: domain.BrowserChrome, UserDataDir: `C:\profiles\old`, ProfileDirectory: "Default", ProxyServer: "https://proxy.local:8443", ExtraArgs: []string{"--window-size=1280,900"}}}} updated := mergeInstanceConfig(existing, []ui.InstanceRow{{ID: "one", Name: "新名称", Browser: "Edge", UserDataDir: `C:\profiles\new`, TargetURL: "https://example.com"}}) if len(updated) != 1 { t.Fatalf("instances = %d, want 1", len(updated)) } got := updated[0] if got.Name != "新名称" || got.Launch.Kind != domain.BrowserEdge || got.Launch.UserDataDir != `C:\profiles\new` || got.Launch.TargetURL != "https://example.com" { t.Fatalf("updated instance = %#v", got) } if got.Launch.ProfileDirectory != "Default" || got.Launch.ProxyServer != "https://proxy.local:8443" || len(got.Launch.ExtraArgs) != 1 { t.Fatalf("existing launch options were lost: %#v", got.Launch) } } type fakeExecutableResolver struct { path string kind domain.BrowserKind configured string } func (r *fakeExecutableResolver) Resolve(_ context.Context, kind domain.BrowserKind, configured string) (string, error) { r.kind = kind r.configured = configured return r.path, nil } type fakeProcessLauncher struct { spec domain.LaunchSpec handle browser.ProcessHandle } func (l *fakeProcessLauncher) Start(_ context.Context, spec domain.LaunchSpec) (browser.ProcessHandle, error) { l.spec = spec return l.handle, nil } type fakeProcessHandle struct{ pid int } func (p fakeProcessHandle) PID() int { return p.pid } func (fakeProcessHandle) Wait(context.Context) (int, error) { return 0, nil }