← back to Cli Printing Press
test: add coverage for research, dogfood, comparative, textfilter, readme_augment
02d022fe2f31e37ad6b9963db9e903a9f9e78e7d · 2026-03-25 00:00:42 -0700 · Matt Van Horn
30 new test cases across 5 files:
- research_test.go: novelty scoring, deduplication, recommendations, round-trip
- dogfood_test.go: tier1 commands, credential detection, score computation
- comparative_test.go: alternative scoring, gap analysis, no-research fallback
- textfilter_test.go: AI slop detection, normal text passthrough, formatting
- readme_augment_test.go: marker replacement, no-evidence passthrough, append mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Files touched
A internal/generator/readme_augment_test.goA internal/generator/textfilter_test.goA internal/pipeline/comparative_test.goA internal/pipeline/dogfood_test.goA internal/pipeline/research_test.go
Diff
commit 02d022fe2f31e37ad6b9963db9e903a9f9e78e7d
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date: Wed Mar 25 00:00:42 2026 -0700
test: add coverage for research, dogfood, comparative, textfilter, readme_augment
30 new test cases across 5 files:
- research_test.go: novelty scoring, deduplication, recommendations, round-trip
- dogfood_test.go: tier1 commands, credential detection, score computation
- comparative_test.go: alternative scoring, gap analysis, no-research fallback
- textfilter_test.go: AI slop detection, normal text passthrough, formatting
- readme_augment_test.go: marker replacement, no-evidence passthrough, append mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
internal/generator/readme_augment_test.go | 70 ++++++++++++++++++
internal/generator/textfilter_test.go | 63 ++++++++++++++++
internal/pipeline/comparative_test.go | 57 +++++++++++++++
internal/pipeline/dogfood_test.go | 116 ++++++++++++++++++++++++++++++
internal/pipeline/research_test.go | 97 +++++++++++++++++++++++++
5 files changed, 403 insertions(+)
diff --git a/internal/generator/readme_augment_test.go b/internal/generator/readme_augment_test.go
new file mode 100644
index 00000000..e7c89b65
--- /dev/null
+++ b/internal/generator/readme_augment_test.go
@@ -0,0 +1,70 @@
+package generator
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestAugmentREADME_WithMarkers(t *testing.T) {
+ dir := t.TempDir()
+ evidenceDir := filepath.Join(dir, "evidence")
+ require.NoError(t, os.MkdirAll(evidenceDir, 0o755))
+
+ readmePath := filepath.Join(dir, "README.md")
+ readmeContent := "# My CLI\n\n<!-- HELP_OUTPUT -->\n\nSome other text.\n"
+ require.NoError(t, os.WriteFile(readmePath, []byte(readmeContent), 0o644))
+
+ helpOutput := "Usage: mycli [command]\n\nAvailable commands:\n list List items\n get Get an item\n"
+ require.NoError(t, os.WriteFile(filepath.Join(evidenceDir, "tier1-help.txt"), []byte(helpOutput), 0o644))
+
+ err := AugmentREADME(readmePath, evidenceDir)
+ require.NoError(t, err)
+
+ result, err := os.ReadFile(readmePath)
+ require.NoError(t, err)
+ assert.Contains(t, string(result), "Usage: mycli [command]")
+ assert.NotContains(t, string(result), "<!-- HELP_OUTPUT -->")
+}
+
+func TestAugmentREADME_NoEvidence(t *testing.T) {
+ dir := t.TempDir()
+ evidenceDir := filepath.Join(dir, "evidence-missing")
+ // Do not create the evidence dir
+
+ readmePath := filepath.Join(dir, "README.md")
+ original := "# My CLI\n\nNothing to see here.\n"
+ require.NoError(t, os.WriteFile(readmePath, []byte(original), 0o644))
+
+ err := AugmentREADME(readmePath, evidenceDir)
+ require.NoError(t, err)
+
+ result, err := os.ReadFile(readmePath)
+ require.NoError(t, err)
+ assert.Equal(t, original, string(result))
+}
+
+func TestAugmentREADME_AppendMode(t *testing.T) {
+ dir := t.TempDir()
+ evidenceDir := filepath.Join(dir, "evidence")
+ require.NoError(t, os.MkdirAll(evidenceDir, 0o755))
+
+ readmePath := filepath.Join(dir, "README.md")
+ original := "# My CLI\n\nA simple tool.\n"
+ require.NoError(t, os.WriteFile(readmePath, []byte(original), 0o644))
+
+ helpOutput := "Usage: mycli [command]\n"
+ require.NoError(t, os.WriteFile(filepath.Join(evidenceDir, "tier1-help.txt"), []byte(helpOutput), 0o644))
+
+ err := AugmentREADME(readmePath, evidenceDir)
+ require.NoError(t, err)
+
+ result, err := os.ReadFile(readmePath)
+ require.NoError(t, err)
+ assert.Contains(t, string(result), "## Real Usage Examples")
+ assert.Contains(t, string(result), "Usage: mycli [command]")
+ assert.Contains(t, string(result), "# My CLI")
+}
diff --git a/internal/generator/textfilter_test.go b/internal/generator/textfilter_test.go
new file mode 100644
index 00000000..c26c5095
--- /dev/null
+++ b/internal/generator/textfilter_test.go
@@ -0,0 +1,63 @@
+package generator
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCheckText_CatchesAISlop(t *testing.T) {
+ t.Run("comprehensive solution triggers warning", func(t *testing.T) {
+ warnings := CheckText("This is a comprehensive solution for your needs.")
+ assert.NotEmpty(t, warnings)
+ found := false
+ for _, w := range warnings {
+ if w.Match == "comprehensive" {
+ found = true
+ }
+ }
+ assert.True(t, found)
+ })
+
+ t.Run("robust framework triggers warning", func(t *testing.T) {
+ warnings := CheckText("A robust framework for building CLIs.")
+ assert.NotEmpty(t, warnings)
+ found := false
+ for _, w := range warnings {
+ if w.Match == "robust" {
+ found = true
+ }
+ }
+ assert.True(t, found)
+ })
+}
+
+func TestCheckText_NormalText(t *testing.T) {
+ t.Run("the API returns JSON has zero warnings", func(t *testing.T) {
+ warnings := CheckText("the API returns JSON")
+ assert.Empty(t, warnings)
+ })
+
+ t.Run("run the command has zero warnings", func(t *testing.T) {
+ warnings := CheckText("run the command")
+ assert.Empty(t, warnings)
+ })
+}
+
+func TestFormatWarnings_Empty(t *testing.T) {
+ result := FormatWarnings(nil)
+ assert.Equal(t, "", result)
+}
+
+func TestFormatWarnings_WithMatches(t *testing.T) {
+ warnings := []AITextWarning{
+ {Pattern: "p1", Match: "comprehensive", Line: 1, Context: "a comprehensive guide"},
+ {Pattern: "p2", Match: "robust", Line: 3, Context: "robust solution"},
+ {Pattern: "p3", Match: "seamless", Line: 5, Context: "seamless integration"},
+ }
+ result := FormatWarnings(warnings)
+ assert.Contains(t, result, "3 warning(s)")
+ assert.Contains(t, result, "comprehensive")
+ assert.Contains(t, result, "robust")
+ assert.Contains(t, result, "seamless")
+}
diff --git a/internal/pipeline/comparative_test.go b/internal/pipeline/comparative_test.go
new file mode 100644
index 00000000..16bd0307
--- /dev/null
+++ b/internal/pipeline/comparative_test.go
@@ -0,0 +1,57 @@
+package pipeline
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestScoreAlternative(t *testing.T) {
+ t.Run("binary install gets 20", func(t *testing.T) {
+ alt := Alternative{Name: "go-cli", InstallMethod: "binary"}
+ scored := scoreAlternative(alt, 10)
+ assert.Equal(t, 20, scored.InstallFriction)
+ })
+
+ t.Run("npm install gets 10", func(t *testing.T) {
+ alt := Alternative{Name: "node-cli", InstallMethod: "npm"}
+ scored := scoreAlternative(alt, 10)
+ assert.Equal(t, 10, scored.InstallFriction)
+ })
+
+ t.Run("unknown install gets 5", func(t *testing.T) {
+ alt := Alternative{Name: "weird-cli", InstallMethod: "something-else"}
+ scored := scoreAlternative(alt, 10)
+ assert.Equal(t, 5, scored.InstallFriction)
+ })
+}
+
+func TestCompareGapsAndAdvantages(t *testing.T) {
+ result := &ComparativeResult{
+ OurScore: 95,
+ Alternatives: []AltScore{
+ {Name: "some-tool", Breadth: 10, Total: 40},
+ },
+ }
+ gaps, advantages := compareGapsAndAdvantages(result)
+ assert.NotEmpty(t, advantages)
+
+ foundGoBinary := false
+ for _, a := range advantages {
+ if a == "Go binary - zero runtime dependencies, instant startup" {
+ foundGoBinary = true
+ }
+ }
+ assert.True(t, foundGoBinary, "should always include Go binary advantage")
+ assert.NotEmpty(t, gaps)
+}
+
+func TestRunComparative(t *testing.T) {
+ dir := t.TempDir()
+ // No research.json exists - should still produce a result without panicking
+ result, err := RunComparative(dir, 10)
+ assert.NoError(t, err)
+ assert.NotNil(t, result)
+ assert.Equal(t, 95, result.OurScore)
+ assert.Equal(t, "ship", result.Recommendation)
+}
diff --git a/internal/pipeline/dogfood_test.go b/internal/pipeline/dogfood_test.go
new file mode 100644
index 00000000..2abc66f3
--- /dev/null
+++ b/internal/pipeline/dogfood_test.go
@@ -0,0 +1,116 @@
+package pipeline
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestBuildTier1Commands(t *testing.T) {
+ t.Run("zero resources returns 3 commands", func(t *testing.T) {
+ cmds := buildTier1Commands("/bin/fake", nil)
+ assert.Len(t, cmds, 3)
+ })
+
+ t.Run("two resources returns 5 commands", func(t *testing.T) {
+ cmds := buildTier1Commands("/bin/fake", []string{"users", "projects"})
+ assert.Len(t, cmds, 5)
+ })
+}
+
+func TestHasCredentials(t *testing.T) {
+ t.Run("empty slice returns false", func(t *testing.T) {
+ assert.False(t, hasCredentials(nil))
+ assert.False(t, hasCredentials([]string{}))
+ })
+
+ t.Run("unset vars return false", func(t *testing.T) {
+ assert.False(t, hasCredentials([]string{"TOTALLY_UNSET_VAR_12345"}))
+ })
+
+ t.Run("set var returns true", func(t *testing.T) {
+ t.Setenv("TEST_CRED_ABC", "secret123")
+ assert.True(t, hasCredentials([]string{"TEST_CRED_ABC"}))
+ })
+}
+
+func TestComputeDogfoodScore(t *testing.T) {
+ t.Run("all pass returns 40+", func(t *testing.T) {
+ r := &DogfoodResults{
+ Tier: 1,
+ TotalCommands: 5,
+ PassedCommands: 5,
+ }
+ score := computeDogfoodScore(r)
+ assert.GreaterOrEqual(t, score, 40)
+ })
+
+ t.Run("half pass returns ~20", func(t *testing.T) {
+ r := &DogfoodResults{
+ Tier: 1,
+ TotalCommands: 10,
+ PassedCommands: 5,
+ }
+ score := computeDogfoodScore(r)
+ assert.Equal(t, 20, score)
+ })
+
+ t.Run("zero pass returns 0", func(t *testing.T) {
+ r := &DogfoodResults{
+ Tier: 1,
+ TotalCommands: 5,
+ PassedCommands: 0,
+ }
+ score := computeDogfoodScore(r)
+ assert.Equal(t, 0, score)
+ })
+
+ t.Run("zero total returns 0", func(t *testing.T) {
+ r := &DogfoodResults{
+ Tier: 1,
+ TotalCommands: 0,
+ }
+ score := computeDogfoodScore(r)
+ assert.Equal(t, 0, score)
+ })
+
+ t.Run("tier bonus adds points", func(t *testing.T) {
+ r := &DogfoodResults{
+ Tier: 2,
+ TotalCommands: 5,
+ PassedCommands: 5,
+ }
+ score := computeDogfoodScore(r)
+ assert.Equal(t, 45, score) // 40 base + 5 tier2 bonus
+ })
+}
+
+func TestWriteAndLoadDogfoodResults(t *testing.T) {
+ dir := t.TempDir()
+ original := &DogfoodResults{
+ Tier: 2,
+ TotalCommands: 4,
+ PassedCommands: 3,
+ FailedCommands: 1,
+ Score: 35,
+ Commands: []CommandResult{
+ {Tier: 1, Command: "--help", ExitCode: 0, Pass: true},
+ {Tier: 1, Command: "version", ExitCode: 0, Pass: true},
+ {Tier: 1, Command: "doctor", ExitCode: 0, Pass: true},
+ {Tier: 2, Command: "users list --json", ExitCode: 1, Pass: false},
+ },
+ }
+
+ err := writeDogfoodResults(original, dir)
+ require.NoError(t, err)
+
+ loaded, err := LoadDogfoodResults(dir)
+ require.NoError(t, err)
+ assert.Equal(t, 2, loaded.Tier)
+ assert.Equal(t, 4, loaded.TotalCommands)
+ assert.Equal(t, 3, loaded.PassedCommands)
+ assert.Equal(t, 1, loaded.FailedCommands)
+ assert.Equal(t, 35, loaded.Score)
+ assert.Len(t, loaded.Commands, 4)
+}
diff --git a/internal/pipeline/research_test.go b/internal/pipeline/research_test.go
new file mode 100644
index 00000000..d459a2b2
--- /dev/null
+++ b/internal/pipeline/research_test.go
@@ -0,0 +1,97 @@
+package pipeline
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestScoreNovelty(t *testing.T) {
+ t.Run("no alternatives returns 10", func(t *testing.T) {
+ score := scoreNovelty(nil)
+ assert.Equal(t, 10, score)
+ })
+
+ t.Run("one alt with 10000 stars returns 2", func(t *testing.T) {
+ alts := []Alternative{{Name: "popular-cli", Stars: 10000}}
+ score := scoreNovelty(alts)
+ assert.Equal(t, 2, score)
+ })
+
+ t.Run("one alt with 50 stars returns 7", func(t *testing.T) {
+ alts := []Alternative{{Name: "small-cli", Stars: 50}}
+ score := scoreNovelty(alts)
+ assert.Equal(t, 7, score)
+ })
+}
+
+func TestDeduplicateAlts(t *testing.T) {
+ alts := []Alternative{
+ {Name: "cli-a", URL: "https://github.com/org/cli-a"},
+ {Name: "cli-b", URL: "https://github.com/org/cli-b"},
+ {Name: "cli-a-dup", URL: "https://github.com/org/cli-a"},
+ }
+ result := deduplicateAlts(alts)
+ assert.Len(t, result, 2)
+ assert.Equal(t, "cli-a", result[0].Name)
+ assert.Equal(t, "cli-b", result[1].Name)
+}
+
+func TestRecommend(t *testing.T) {
+ tests := []struct {
+ score int
+ expected string
+ }{
+ {1, "skip"},
+ {2, "skip"},
+ {3, "skip"},
+ {4, "proceed-with-gaps"},
+ {5, "proceed-with-gaps"},
+ {6, "proceed-with-gaps"},
+ {7, "proceed"},
+ {8, "proceed"},
+ {9, "proceed"},
+ {10, "proceed"},
+ }
+ for _, tt := range tests {
+ t.Run("", func(t *testing.T) {
+ assert.Equal(t, tt.expected, recommend(tt.score))
+ })
+ }
+}
+
+func TestAnalyzeAlternatives(t *testing.T) {
+ alts := []Alternative{
+ {Name: "tool-a", Language: "python", HasJSON: false},
+ {Name: "tool-b", Language: "typescript", HasJSON: true},
+ }
+ gaps, patterns := analyzeAlternatives(alts)
+ assert.NotEmpty(t, gaps)
+ assert.NotEmpty(t, patterns)
+}
+
+func TestWriteAndLoadResearch(t *testing.T) {
+ dir := t.TempDir()
+ result := &ResearchResult{
+ APIName: "test-api",
+ NoveltyScore: 8,
+ Recommendation: "proceed",
+ Alternatives: []Alternative{
+ {Name: "alt-1", URL: "https://example.com/alt-1"},
+ },
+ Gaps: []string{"no --json"},
+ Patterns: []string{"standard CRUD"},
+ }
+
+ err := writeResearchJSON(result, dir)
+ require.NoError(t, err)
+
+ loaded, err := LoadResearch(dir)
+ require.NoError(t, err)
+ assert.Equal(t, "test-api", loaded.APIName)
+ assert.Equal(t, 8, loaded.NoveltyScore)
+ assert.Equal(t, "proceed", loaded.Recommendation)
+ assert.Len(t, loaded.Alternatives, 1)
+ assert.Equal(t, "alt-1", loaded.Alternatives[0].Name)
+}
← 2370b45b fix(templates): guard readme.md.tmpl against empty Auth.EnvV
·
back to Cli Printing Press
·
feat(catalog): generate Plaid CLI from official OpenAPI spec 66bc4ea8 →