package docs import ( "os" "path/filepath" "slices" "strings" "testing" ) func TestAllTargets_ContainsVisionAndReadme(t *testing.T) { got := AllTargets() if len(got) == 0 { t.Fatal("AllTargets returned no targets") } if !slices.Contains(got, TargetVision) { t.Errorf("AllTargets missing TargetVision; got %v", got) } if !slices.Contains(got, TargetReadme) { t.Errorf("AllTargets missing TargetReadme; got %v", got) } } func TestTarget_Filename(t *testing.T) { cases := []struct { in Target want string }{ {TargetVision, "VISION.md"}, {TargetReadme, "README.md"}, {Target("bogus"), ""}, } for _, tc := range cases { if got := tc.in.Filename(); got != tc.want { t.Errorf("Filename(%q) = %q, want %q", tc.in, got, tc.want) } } } func TestRender_VisionAllCompanionsPresent(t *testing.T) { p := Params{ Project: "demo", Version: "v1.10.0", HasReadme: true, HasUsage: true, HasArch: true, } got, err := Render(TargetVision, p) if err != nil { t.Fatalf("Render: %v", err) } for _, want := range []string{ "# demo — vision", "Scaffolded by `eeco docs new vision` (eeco v1.10.0)", "## What it gives you", "## Non-goals", "## Roadmap", "## See also", "[README.md](README.md)", "[docs/USAGE.md](docs/USAGE.md)", "[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)", } { if !strings.Contains(got, want) { t.Errorf("Render missing %q:\n%s", want, got) } } } func TestRender_VisionNoCompanions(t *testing.T) { got, err := Render(TargetVision, Params{Project: "lonely", Version: "v0.0.0-dev"}) if err != nil { t.Fatalf("Render: %v", err) } for _, absent := range []string{ "[README.md](README.md)", "[docs/USAGE.md](docs/USAGE.md)", "[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)", } { if strings.Contains(got, absent) { t.Errorf("Render included %q when companion was absent:\n%s", absent, got) } } if !strings.Contains(got, "(no related docs detected") { t.Errorf("Render missing empty-See-also placeholder:\n%s", got) } } func TestRender_Deterministic(t *testing.T) { p := Params{Project: "demo", Version: "v1.10.0", HasReadme: true} a, err := Render(TargetVision, p) if err != nil { t.Fatalf("Render a: %v", err) } b, err := Render(TargetVision, p) if err != nil { t.Fatalf("Render b: %v", err) } if a != b { t.Errorf("Render is not deterministic across calls with the same Params") } } func TestRender_UnknownTarget(t *testing.T) { if _, err := Render(Target("nope"), Params{}); err == nil { t.Fatal("expected error for unknown target") } } func TestScaffold_WritesVision(t *testing.T) { root := t.TempDir() p := Params{Project: filepath.Base(root), Version: "v1.10.0", HasReadme: true} written, err := Scaffold(TargetVision, root, false, p) if err != nil { t.Fatalf("Scaffold: %v", err) } if written != "VISION.md" { t.Errorf("written path = %q, want VISION.md", written) } body, err := os.ReadFile(filepath.Join(root, "VISION.md")) if err != nil { t.Fatalf("read written file: %v", err) } if !strings.Contains(string(body), "— vision") { t.Errorf("written file missing vision heading:\n%s", body) } } func TestScaffold_RefusesExisting(t *testing.T) { root := t.TempDir() full := filepath.Join(root, "VISION.md") if err := os.WriteFile(full, []byte("operator content\n"), 0o644); err != nil { t.Fatal(err) } _, err := Scaffold(TargetVision, root, false, Params{Project: "demo"}) if err == nil { t.Fatal("Scaffold should refuse to overwrite an existing file") } if !strings.Contains(err.Error(), "already exists") || !strings.Contains(err.Error(), "--overwrite") { t.Errorf("error should name file + flag, got %q", err) } // The pre-existing content must be intact. body, err := os.ReadFile(full) if err != nil { t.Fatal(err) } if string(body) != "operator content\n" { t.Errorf("Scaffold mutated the existing file: %q", body) } } func TestScaffold_OverwriteReplaces(t *testing.T) { root := t.TempDir() full := filepath.Join(root, "VISION.md") if err := os.WriteFile(full, []byte("stale content\n"), 0o644); err != nil { t.Fatal(err) } if _, err := Scaffold(TargetVision, root, true, Params{Project: "demo", Version: "v1.10.0"}); err != nil { t.Fatalf("Scaffold --overwrite: %v", err) } body, err := os.ReadFile(full) if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "demo — vision") { t.Errorf("overwrite did not render new template:\n%s", body) } } func TestScaffold_UnknownTarget(t *testing.T) { root := t.TempDir() if _, err := Scaffold(Target("nope"), root, false, Params{}); err == nil { t.Fatal("expected error for unknown target") } } func TestRender_ReadmeAllCompanionsPresent(t *testing.T) { p := Params{ Project: "demo", Version: "v2.6.0", HasReadme: true, HasUsage: true, HasArch: true, } got, err := Render(TargetReadme, p) if err != nil { t.Fatalf("Render: %v", err) } for _, want := range []string{ "# demo", "Scaffolded by `eeco docs new readme` (eeco v2.6.0)", "## Quick start", "## How it works", "## See also", "[docs/USAGE.md](docs/USAGE.md)", "[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)", } { if !strings.Contains(got, want) { t.Errorf("Render missing %q:\n%s", want, got) } } if strings.Contains(got, "[README.md](README.md)") { t.Errorf("readme template must not self-link to README.md:\n%s", got) } } func TestRender_ReadmeNoCompanions(t *testing.T) { got, err := Render(TargetReadme, Params{Project: "lonely", Version: "v0.0.0-dev"}) if err != nil { t.Fatalf("Render: %v", err) } for _, absent := range []string{ "[docs/USAGE.md](docs/USAGE.md)", "[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)", } { if strings.Contains(got, absent) { t.Errorf("Render included %q when companion was absent:\n%s", absent, got) } } if !strings.Contains(got, "(no related docs detected") { t.Errorf("Render missing empty-See-also placeholder:\n%s", got) } } func TestScaffold_WritesReadme(t *testing.T) { root := t.TempDir() p := Params{Project: filepath.Base(root), Version: "v2.6.0", HasUsage: true} written, err := Scaffold(TargetReadme, root, false, p) if err != nil { t.Fatalf("Scaffold: %v", err) } if written != "README.md" { t.Errorf("written path = %q, want README.md", written) } body, err := os.ReadFile(filepath.Join(root, "README.md")) if err != nil { t.Fatalf("read written file: %v", err) } if !strings.Contains(string(body), "## Quick start") { t.Errorf("written file missing Quick start heading:\n%s", body) } } func TestScaffold_ReadmeRefusesExisting(t *testing.T) { root := t.TempDir() full := filepath.Join(root, "README.md") if err := os.WriteFile(full, []byte("operator content\n"), 0o644); err != nil { t.Fatal(err) } _, err := Scaffold(TargetReadme, root, false, Params{Project: "demo"}) if err == nil { t.Fatal("Scaffold should refuse to overwrite an existing README.md") } if !strings.Contains(err.Error(), "already exists") || !strings.Contains(err.Error(), "--overwrite") { t.Errorf("error should name file + flag, got %q", err) } body, err := os.ReadFile(full) if err != nil { t.Fatal(err) } if string(body) != "operator content\n" { t.Errorf("Scaffold mutated the existing file: %q", body) } } func TestScaffold_ReadmeOverwriteReplaces(t *testing.T) { root := t.TempDir() full := filepath.Join(root, "README.md") if err := os.WriteFile(full, []byte("stale content\n"), 0o644); err != nil { t.Fatal(err) } if _, err := Scaffold(TargetReadme, root, true, Params{Project: "demo", Version: "v2.6.0"}); err != nil { t.Fatalf("Scaffold --overwrite: %v", err) } body, err := os.ReadFile(full) if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "## Quick start") { t.Errorf("overwrite did not render new template:\n%s", body) } }