Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ jobs:
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.23'
run: |
total=$(go tool cover -func=coverage.out | awk '/^total:/ {gsub("%","",$3); print $3}')
# Floor target: 90%. Codex PRs will incrementally raise this value.
min=30.0
# Current enforced coverage floor. Codex PRs raise this incrementally toward 90%.
min=35.0
awk -v t="$total" -v m="$min" 'BEGIN {
if (t+0 < m+0) {
printf "Coverage %.1f%% is below floor %.1f%%\n", t, m
Expand All @@ -71,7 +71,7 @@ jobs:
label: coverage
message: ${{ env.COVERAGE }}%
valColorRange: ${{ env.COVERAGE }}
minColorRange: 30
minColorRange: 35
maxColorRange: 90

- name: Upload coverage
Expand Down
83 changes: 83 additions & 0 deletions render/clone_animation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package render

import (
"bytes"
"strings"
"testing"
)

func TestTruncate(t *testing.T) {
tests := []struct {
name string
input string
max int
expected string
}{
{name: "short string unchanged", input: "repo", max: 10, expected: "repo"},
{name: "same length unchanged", input: "abcdefghij", max: 10, expected: "abcdefghij"},
{name: "long string truncated", input: "very-long-repository-name", max: 10, expected: "very-lon.."},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := truncate(tt.input, tt.max)
if got != tt.expected {
t.Fatalf("truncate(%q, %d) = %q, want %q", tt.input, tt.max, got, tt.expected)
}
})
}
}

func TestNewCloneAnimation(t *testing.T) {
var buf bytes.Buffer
a := NewCloneAnimation(&buf, "repo")
if a == nil {
t.Fatal("expected animation instance")
}
if a.w != &buf {
t.Fatal("expected writer to be set")
}
if a.repoName != "repo" {
t.Fatalf("expected repoName to be %q, got %q", "repo", a.repoName)
}
}

func TestCloneAnimationRenderClampsProgress(t *testing.T) {
tests := []struct {
name string
progress int
expectedProgress string
}{
{name: "negative becomes zero", progress: -10, expectedProgress: "0%"},
{name: "over hundred becomes hundred", progress: 120, expectedProgress: "100%"},
{name: "middle value", progress: 45, expectedProgress: "45%"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
a := NewCloneAnimation(&buf, "example/repo")
a.Render(tt.progress)

out := buf.String()
if !strings.Contains(out, "\r\033[K") {
t.Fatalf("expected cursor clear sequence, got %q", out)
}
if !strings.Contains(out, tt.expectedProgress) {
t.Fatalf("expected output to contain %q, got %q", tt.expectedProgress, out)
}
})
}
}

func TestCloneAnimationBuildFrame(t *testing.T) {
a := NewCloneAnimation(&bytes.Buffer{}, "very-very-long-repository-name-that-needs-truncation")
frame := a.buildFrame(50)

checks := []string{"50%", "..", "🗺️"}
for _, check := range checks {
if !strings.Contains(frame, check) {
t.Fatalf("expected frame to contain %q, got %q", check, frame)
}
}
}
105 changes: 105 additions & 0 deletions render/depgraph_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package render

import (
"bytes"
"strings"
"testing"

"codemap/scanner"
)

func TestDepgraphTitleCase(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{name: "empty", input: "", expected: ""},
{name: "single word", input: "hello", expected: "Hello"},
{name: "multiple words", input: "hello world", expected: "Hello World"},
{name: "extra spaces", input: " hello world ", expected: "Hello World"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := titleCase(tt.input)
if got != tt.expected {
t.Fatalf("titleCase(%q) = %q, want %q", tt.input, got, tt.expected)
}
})
}
}

func TestDepgraphGetSystemName(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{name: "skips generic prefix", input: "src/payment_service", expected: "Payment Service"},
{name: "supports windows separators", input: "internal\\auth-module", expected: "Auth Module"},
{name: "falls back to last segment", input: "src", expected: "Src"},
{name: "root marker", input: ".", expected: "."},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := getSystemName(tt.input)
if got != tt.expected {
t.Fatalf("getSystemName(%q) = %q, want %q", tt.input, got, tt.expected)
}
})
}
}

func TestDepgraphNoSourceFiles(t *testing.T) {

Check failure on line 55 in render/depgraph_test.go

View workflow job for this annotation

GitHub Actions / Lint

other declaration of TestDepgraphNoSourceFiles (compile)
project := scanner.DepsProject{
Root: t.TempDir(),
Files: nil,
}

var buf bytes.Buffer
Depgraph(&buf, project)

output := buf.String()
if !strings.Contains(output, "No source files found.") {
t.Fatalf("expected no files message, got:\n%s", output)
}
}

func TestDepgraphRendersExternalDepsAndSummarySection(t *testing.T) {
project := scanner.DepsProject{
Root: t.TempDir(),
Files: []scanner.FileAnalysis{
{
Path: "src/main.go",
Functions: []string{"main"},
},
},
ExternalDeps: map[string][]string{
"go": {"github.com/acme/module/v2", "github.com/acme/pkg", "github.com/acme/pkg"},
"javascript": {"react", "react"},
},
}

var buf bytes.Buffer
Depgraph(&buf, project)
output := buf.String()

expectedSnippets := []string{
"Dependency Flow",
"Go: module, pkg",
"JavaScript: react",
"Src",
"+1 standalone files",
"1 files",
"1 functions",
"0 deps",
}

for _, snippet := range expectedSnippets {
if !strings.Contains(output, snippet) {
t.Fatalf("expected output to contain %q, got:\n%s", snippet, output)
}
}
}
Loading
Loading