test(coverage): add unit tests across all packages; remove dead code
ci / vet, staticcheck, test, build (push) Successful in 2m25s
ci / vet, staticcheck, test, build (push) Successful in 2m25s
- Added comprehensive table-driven test suites for all packages: cmd/glint, cicontext, fetcher, graph, linter, model, resolver. Coverage reaches 98%+ statement coverage across the codebase. - Replaced os.Exit calls in cmd/glint with an `exit` variable so tests can capture exit codes without terminating the test process. - Removed unreachable code found during coverage analysis: dead guard in cicontext.parseRegexLiteral; dead len(jobs)==0 branch in graph.Pipeline; skipWin struct field and dead continue in graph.convertToPNG; pipelineSVG return type simplified to string. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.k3nny.fr/glint/internal/cicontext"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
func makeSimplePipeline() *model.Pipeline {
|
||||
return &model.Pipeline{
|
||||
Stages: []string{"build", "test"},
|
||||
Jobs: map[string]model.Job{
|
||||
"build-job": {Name: "build-job", Stage: "build"},
|
||||
"test-job": {Name: "test-job", Stage: "test"},
|
||||
".template": {Name: ".template", Stage: "build"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_Basic(t *testing.T) {
|
||||
p := makeSimplePipeline()
|
||||
out := Tree(p, nil)
|
||||
if !strings.Contains(out, "pipeline") {
|
||||
t.Error("expected 'pipeline' root node")
|
||||
}
|
||||
if !strings.Contains(out, "build-job") {
|
||||
t.Error("expected build-job in tree")
|
||||
}
|
||||
if !strings.Contains(out, "test-job") {
|
||||
t.Error("expected test-job in tree")
|
||||
}
|
||||
// hidden template jobs should not appear
|
||||
if strings.Contains(out, ".template") {
|
||||
t.Error(".template job should be excluded from tree")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_WithContext_Skipped(t *testing.T) {
|
||||
p := &model.Pipeline{
|
||||
Stages: []string{"build"},
|
||||
Jobs: map[string]model.Job{
|
||||
"run-on-tag": {Name: "run-on-tag", Stage: "build", Rules: []model.Rule{
|
||||
{If: `$CI_COMMIT_TAG != ""`, When: "on_success"},
|
||||
}},
|
||||
},
|
||||
}
|
||||
ctx := cicontext.New("main", "", "", nil)
|
||||
out := Tree(p, ctx)
|
||||
if !strings.Contains(out, "[skipped]") {
|
||||
t.Errorf("expected [skipped] annotation, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_WithContext_Manual(t *testing.T) {
|
||||
p := &model.Pipeline{
|
||||
Stages: []string{"deploy"},
|
||||
Jobs: map[string]model.Job{
|
||||
"deploy": {Name: "deploy", Stage: "deploy", When: "manual"},
|
||||
},
|
||||
}
|
||||
ctx := cicontext.New("main", "", "", nil)
|
||||
out := Tree(p, ctx)
|
||||
if !strings.Contains(out, "[manual]") {
|
||||
t.Errorf("expected [manual] annotation, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_NoContext_Annotations(t *testing.T) {
|
||||
p := &model.Pipeline{
|
||||
Stages: []string{"build"},
|
||||
Jobs: map[string]model.Job{
|
||||
"manual-job": {Name: "manual-job", Stage: "build", When: "manual"},
|
||||
"delayed-job": {Name: "delayed-job", Stage: "build", When: "delayed"},
|
||||
"trigger-job": {Name: "trigger-job", Stage: "build", Trigger: "other/project"},
|
||||
},
|
||||
}
|
||||
out := Tree(p, nil)
|
||||
if !strings.Contains(out, "[manual]") { t.Error("expected [manual]") }
|
||||
if !strings.Contains(out, "[delayed]") { t.Error("expected [delayed]") }
|
||||
if !strings.Contains(out, "[trigger]") { t.Error("expected [trigger]") }
|
||||
}
|
||||
|
||||
func TestTree_JobWithNoStage(t *testing.T) {
|
||||
// Jobs without a stage should default to "test"
|
||||
p := &model.Pipeline{
|
||||
Stages: []string{"test"},
|
||||
Jobs: map[string]model.Job{
|
||||
"nostage": {Name: "nostage"},
|
||||
},
|
||||
}
|
||||
out := Tree(p, nil)
|
||||
if !strings.Contains(out, "test") { t.Error("expected default stage 'test'") }
|
||||
}
|
||||
|
||||
func TestTree_UndeclaredStage(t *testing.T) {
|
||||
// Job in a stage not listed in p.Stages — should still appear
|
||||
p := &model.Pipeline{
|
||||
Stages: []string{},
|
||||
Jobs: map[string]model.Job{
|
||||
"myjob": {Name: "myjob", Stage: "custom"},
|
||||
},
|
||||
}
|
||||
out := Tree(p, nil)
|
||||
if !strings.Contains(out, "myjob") { t.Error("expected myjob in output") }
|
||||
}
|
||||
|
||||
// ── branchChars ───────────────────────────────────────────────────────────────
|
||||
|
||||
func TestBranchChars(t *testing.T) {
|
||||
b, c := branchChars(true)
|
||||
if b != "└── " || c != " " {
|
||||
t.Errorf("last: branch=%q cont=%q", b, c)
|
||||
}
|
||||
b, c = branchChars(false)
|
||||
if b != "├── " || c != "│ " {
|
||||
t.Errorf("not-last: branch=%q cont=%q", b, c)
|
||||
}
|
||||
}
|
||||
|
||||
// ── jobLabel ─────────────────────────────────────────────────────────────────
|
||||
|
||||
func TestJobLabel(t *testing.T) {
|
||||
job := model.Job{Name: "j"}
|
||||
if jobLabel(job, "j", nil) != "j" {
|
||||
t.Error("plain job should have plain label")
|
||||
}
|
||||
|
||||
// Multiple tags
|
||||
job2 := model.Job{Name: "j2", When: "manual", Trigger: "other"}
|
||||
label := jobLabel(job2, "j2", nil)
|
||||
if !strings.Contains(label, "manual") || !strings.Contains(label, "trigger") {
|
||||
t.Errorf("expected manual+trigger in label: %q", label)
|
||||
}
|
||||
|
||||
// With context — active job has no annotation
|
||||
emptyCtx := &cicontext.Context{}
|
||||
if jobLabel(model.Job{}, "j", emptyCtx) != "j" {
|
||||
t.Error("active job with context should have no annotation")
|
||||
}
|
||||
}
|
||||
|
||||
// TestJobLabel_ActiveWithContext covers the 'return name' path when a job runs
|
||||
// normally (neither skipped nor manual) with a non-empty context.
|
||||
func TestJobLabel_ActiveWithContext(t *testing.T) {
|
||||
// Branch context with a job that has no rules → the job is active.
|
||||
ctx := cicontext.New("main", "", "", nil)
|
||||
activeJob := model.Job{Name: "build", Script: []any{"make"}}
|
||||
label := jobLabel(activeJob, "build", ctx)
|
||||
if label != "build" {
|
||||
t.Errorf("active job with context: expected 'build', got %q", label)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user