Go 286 lines
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)
}
}