package scaffold import ( "os" "path/filepath" "testing" "nub/internal/config" ) func TestInit_CreatesConfigAndAgentsFile(t *testing.T) { dir := t.TempDir() res, err := Init(dir) if err != nil { t.Fatal(err) } if !res.ConfigCreated { t.Error("expected config.toml to be created") } if !res.AgentsCreated { t.Error("expected AGENTS.md to be created") } if _, err := os.Stat(filepath.Join(dir, ".nub", "config.toml")); err != nil { t.Errorf(".nub/config.toml missing: %v", err) } if _, err := os.Stat(filepath.Join(dir, "AGENTS.md")); err != nil { t.Errorf("AGENTS.md missing: %v", err) } content, err := os.ReadFile(filepath.Join(dir, ".nub", "config.toml")) if err != nil { t.Fatal(err) } if !contains(string(content), "[permissions]") { t.Error("expected the example config to include a [permissions] section") } if !contains(string(content), "version = 1") { t.Error("expected the example config to declare version = 1") } } func TestInit_DoesNotOverwriteExistingFiles(t *testing.T) { dir := t.TempDir() if err := os.MkdirAll(filepath.Join(dir, ".nub"), 0o700); err != nil { t.Fatal(err) } customConfig := "version = 1\n# my custom config, do not touch\n" if err := os.WriteFile(filepath.Join(dir, ".nub", "config.toml"), []byte(customConfig), 0o600); err != nil { t.Fatal(err) } customAgents := "# my custom AGENTS.md\n" if err := os.WriteFile(filepath.Join(dir, "AGENTS.md"), []byte(customAgents), 0o644); err != nil { t.Fatal(err) } res, err := Init(dir) if err != nil { t.Fatal(err) } if res.ConfigCreated { t.Error("expected an existing config.toml not to be reported as created") } if res.AgentsCreated { t.Error("expected an existing AGENTS.md not to be reported as created") } gotConfig, _ := os.ReadFile(filepath.Join(dir, ".nub", "config.toml")) if string(gotConfig) != customConfig { t.Error("existing config.toml must not be overwritten") } gotAgents, _ := os.ReadFile(filepath.Join(dir, "AGENTS.md")) if string(gotAgents) != customAgents { t.Error("existing AGENTS.md must not be overwritten") } } func TestInit_GeneratedConfigParsesAndRoundTripsPermissions(t *testing.T) { dir := t.TempDir() t.Setenv("NUB_MODEL", "") t.Setenv("NUB_BASE_URL", "") t.Setenv("NUB_API_KEY", "") t.Setenv("OPENAI_API_KEY", "dummy-for-this-test") if _, err := Init(dir); err != nil { t.Fatal(err) } cfg, err := config.Load(dir) if err != nil { t.Fatalf("generated config.toml failed to parse: %v", err) } if cfg.Permissions.Write != "ask" || cfg.Permissions.Edit != "ask" || cfg.Permissions.Bash != "ask" { t.Errorf("expected write/edit/bash = ask in the generated config, got %+v", cfg.Permissions) } if len(cfg.Permissions.DenyPaths) == 0 { t.Error("expected deny_paths to be populated in the generated config") } } func TestInit_SetsUpGitExclude(t *testing.T) { dir := t.TempDir() if err := os.MkdirAll(filepath.Join(dir, ".git", "info"), 0o755); err != nil { t.Fatal(err) } if _, err := Init(dir); err != nil { t.Fatal(err) } excludeContent, err := os.ReadFile(filepath.Join(dir, ".git", "info", "exclude")) if err != nil { t.Fatal(err) } if !contains(string(excludeContent), ".nub/") { t.Error("expected .nub/ to be added to .git/info/exclude") } } func contains(s, sub string) bool { return len(s) >= len(sub) && (func() bool { for i := 0; i+len(sub) <= len(s); i++ { if s[i:i+len(sub)] == sub { return true } } return false })() }