Files
awesome-coding-agents/validate_test.go
T
tiennm99andGitHub 0d5e4b3266 feat: momentum metrics, agents.yml validation, drift detection, hardening (#9)
Review fixes:
- applyMigrations resolves chained renames deterministically (cycle guard)
- GraphQL errors name the failing owner/repo, not the alias
- sanitizeCell escapes backslash + angle brackets (README md/HTML injection)
- timeNow/graphqlURL seams; new httptest fetch tests, site + delta tests
  (coverage 30% -> 65.6%)

Features:
- delta30d computed from history; dashboard gains sortable D30d column
- README shows Top 7-day mover line
- 'go run . -check' validates agents.yml offline (dupes, categories,
  name patterns); wired into CI for contributor PRs
- isArchived + rename drift warnings in daily run; archived badge on
  dashboard (E2E found 3 archived repos: gpt-engineer, void, Roo-Code)
2026-08-09 03:26:01 +07:00

175 lines
5.5 KiB
Go

package main
import (
"os"
"strings"
"testing"
)
func TestValidateAgents_ValidEntriesNoViolations(t *testing.T) {
agents := []Agent{
{Owner: "aider-ai", Repo: "aider", Category: "cli"},
{Owner: "cline", Repo: "cline", Category: "extension"},
{Owner: "a", Repo: "b.c-d_e", Category: "web"},
}
violations := validateAgents(agents)
if len(violations) != 0 {
t.Errorf("expected no violations, got %v", violations)
}
}
func TestValidateAgents_EmptyOwner(t *testing.T) {
violations := validateAgents([]Agent{{Owner: "", Repo: "repo1", Category: "cli"}})
if !anyContains(violations, "owner is empty") {
t.Errorf("expected 'owner is empty' violation, got %v", violations)
}
}
func TestValidateAgents_InvalidOwnerChars(t *testing.T) {
tests := []string{"foo_bar", "-leading", "trailing-", "double--hyphen", "foo owner"}
for _, owner := range tests {
t.Run(owner, func(t *testing.T) {
violations := validateAgents([]Agent{{Owner: owner, Repo: "repo1", Category: "cli"}})
if !anyContains(violations, "does not look like a valid GitHub username/org") {
t.Errorf("owner %q: expected invalid-owner violation, got %v", owner, violations)
}
})
}
}
func TestValidateAgents_EmptyRepo(t *testing.T) {
violations := validateAgents([]Agent{{Owner: "org", Repo: "", Category: "cli"}})
if !anyContains(violations, "repo is empty") {
t.Errorf("expected 'repo is empty' violation, got %v", violations)
}
}
func TestValidateAgents_InvalidRepoChars(t *testing.T) {
tests := []string{"foo/bar", "foo bar", "foo@bar", "foo#bar"}
for _, repo := range tests {
t.Run(repo, func(t *testing.T) {
violations := validateAgents([]Agent{{Owner: "org", Repo: repo, Category: "cli"}})
if !anyContains(violations, "characters not allowed in a GitHub repo name") {
t.Errorf("repo %q: expected invalid-repo violation, got %v", repo, violations)
}
})
}
}
func TestValidateAgents_MissingCategory(t *testing.T) {
violations := validateAgents([]Agent{{Owner: "org", Repo: "repo1", Category: ""}})
if !anyContains(violations, "category is required") {
t.Errorf("expected 'category is required' violation, got %v", violations)
}
}
func TestValidateAgents_InvalidCategory(t *testing.T) {
violations := validateAgents([]Agent{{Owner: "org", Repo: "repo1", Category: "framework"}})
if !anyContains(violations, "is not one of cli, ide, extension, library, research, web") {
t.Errorf("expected invalid-category violation, got %v", violations)
}
}
func TestValidateAgents_AllValidCategoriesAccepted(t *testing.T) {
for _, cat := range []string{"cli", "ide", "extension", "library", "research", "web"} {
t.Run(cat, func(t *testing.T) {
violations := validateAgents([]Agent{{Owner: "org", Repo: "repo1", Category: cat}})
if len(violations) != 0 {
t.Errorf("category %q: expected no violations, got %v", cat, violations)
}
})
}
}
func TestValidateAgents_DuplicateCaseInsensitive(t *testing.T) {
agents := []Agent{
{Owner: "Foo", Repo: "Bar", Category: "cli"},
{Owner: "foo", Repo: "bar", Category: "cli"},
}
violations := validateAgents(agents)
if !anyContains(violations, "duplicate of entry 0") {
t.Errorf("expected duplicate violation referencing entry 0, got %v", violations)
}
}
func TestValidateAgents_NoDuplicateForDistinctRepos(t *testing.T) {
agents := []Agent{
{Owner: "foo", Repo: "bar", Category: "cli"},
{Owner: "foo", Repo: "baz", Category: "cli"},
}
violations := validateAgents(agents)
if len(violations) != 0 {
t.Errorf("expected no violations for distinct repos, got %v", violations)
}
}
func TestValidateAgents_CollectsAllViolationsNotJustFirst(t *testing.T) {
agents := []Agent{
{Owner: "", Repo: "", Category: ""},
{Owner: "org", Repo: "repo1", Category: "not-a-category"},
}
violations := validateAgents(agents)
// entry 0: owner empty, repo empty, category empty = 3 violations.
// entry 1: invalid category = 1 violation.
if len(violations) != 4 {
t.Errorf("expected 4 violations collected across both entries, got %d: %v", len(violations), violations)
}
}
func TestRunCheck_CurrentAgentsYML(t *testing.T) {
// The real data/agents.yml must always pass -check; this is the
// regression guard for that invariant.
if err := runCheck("data/agents.yml"); err != nil {
t.Errorf("expected data/agents.yml to pass validation, got: %v", err)
}
}
func TestRunCheck_BadYAMLReportsViolationsAndError(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := tmpDir + "/bad-agents.yml"
// loadAgents itself already rejects empty owner/repo, so this fixture
// covers the violation classes only validateAgents catches: bad owner
// chars, bad repo chars, invalid category, and a case-insensitive dup.
content := `agents:
- owner: "bad owner"
repo: "valid-repo"
category: cli
- owner: "org"
repo: "bad/repo"
category: not-a-real-category
- owner: "Dup"
repo: "Repo"
category: web
- owner: "dup"
repo: "repo"
category: web
`
if err := os.WriteFile(tmpFile, []byte(content), 0600); err != nil {
t.Fatalf("WriteFile failed: %v", err)
}
err := runCheck(tmpFile)
if err == nil {
t.Fatal("expected runCheck to fail on invalid agents.yml, got nil error")
}
if !strings.Contains(err.Error(), "violation(s) found") {
t.Errorf("expected error to summarize violation count, got: %v", err)
}
}
func TestRunCheck_MissingFile(t *testing.T) {
err := runCheck("/nonexistent/path/agents.yml")
if err == nil {
t.Error("expected error for missing file, got nil")
}
}
func anyContains(list []string, substr string) bool {
for _, s := range list {
if strings.Contains(s, substr) {
return true
}
}
return false
}