Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/mdsmith
eval/corpus/config.local.yml
coverage.out
102 changes: 102 additions & 0 deletions internal/rules/catalog/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3494,3 +3494,105 @@ func TestBuildCatalogEntries_Normal_NoDiagnostics(t *testing.T) {
assert.Empty(t, diags)
assert.Equal(t, "A", entries[0].fields["title"])
}

// =====================================================================
// Phase 4 coverage: Rule.Category
// =====================================================================

func TestRule_Category(t *testing.T) {
r := &Rule{}
assert.Equal(t, "meta", r.Category())
}

// =====================================================================
// Phase 4 coverage: resolveGitignore param variations
// =====================================================================

func TestResolveGitignore_DisabledByParam(t *testing.T) {
f := newTestFile(t, "index.md", "")
matcher, base := resolveGitignore(f, map[string]string{"gitignore": "false"})
assert.Nil(t, matcher)
assert.Equal(t, "", base)
}

func TestResolveGitignore_NoMatcherAvailable(t *testing.T) {
f := newTestFile(t, "index.md", "")
// GitignoreFunc is nil → GetGitignore returns nil
matcher, base := resolveGitignore(f, map[string]string{})
assert.Nil(t, matcher)
assert.Equal(t, "", base)
}

func TestResolveGitignore_WithMatcherAndSourceDir(t *testing.T) {
dir := t.TempDir()
stub := &lint.GitignoreMatcher{}
f := &lint.File{
Path: filepath.Join(dir, "index.md"),
RootDir: dir,
GitignoreFunc: func() *lint.GitignoreMatcher {
return stub
},
}
params := map[string]string{"source-dir": "docs"}
matcher, base := resolveGitignore(f, params)
assert.Same(t, stub, matcher)
assert.NotEmpty(t, base)
assert.True(t, filepath.IsAbs(base))
}

// =====================================================================
// Phase 4 coverage: scanIncludesForTarget fallback paths
// =====================================================================

func TestScanIncludesForTarget_MaxDepthExceeded(t *testing.T) {
// Pass depth > maxIncludeDepth to immediately return false.
fsys := fstest.MapFS{
"a.md": &fstest.MapFile{Data: []byte("# A\n")},
}
visited := map[string]bool{}
result := scanIncludesForTarget(fsys, "a.md", "target.md", visited, maxIncludeDepth+1, 1000)
assert.False(t, result)
}

func TestScanIncludesForTarget_FileReadError(t *testing.T) {
// File does not exist in FS → read error → returns false.
fsys := fstest.MapFS{}
visited := map[string]bool{}
result := scanIncludesForTarget(fsys, "nonexistent.md", "target.md", visited, 0, 1000)
assert.False(t, result)
}

func TestScanIncludesForTarget_NoIncludes(t *testing.T) {
// File exists but has no <?include?> directives → returns false.
fsys := fstest.MapFS{
"a.md": &fstest.MapFile{Data: []byte("# A\n\nNo includes here.\n")},
}
visited := map[string]bool{}
result := scanIncludesForTarget(fsys, "a.md", "target.md", visited, 0, 1000)
assert.False(t, result)
}

func TestScanIncludesForTarget_DirectMatch(t *testing.T) {
// File directly includes target → returns true.
fsys := fstest.MapFS{
"a.md": &fstest.MapFile{
Data: []byte("<?include\nfile: target.md\n?>\nsome content\n<?/include?>"),
},
}
visited := map[string]bool{"a.md": true}
result := scanIncludesForTarget(fsys, "a.md", "target.md", visited, 0, 1000)
assert.True(t, result)
}

func TestScanIncludesForTarget_VisitedCycleSkipped(t *testing.T) {
// File includes itself (visited) → should not recurse, returns false.
fsys := fstest.MapFS{
"a.md": &fstest.MapFile{
Data: []byte("<?include\nfile: b.md\n?>\ncontent\n<?/include?>"),
},
}
// Mark b.md as already visited to prevent recursion.
visited := map[string]bool{"a.md": true, "b.md": true}
result := scanIncludesForTarget(fsys, "a.md", "target.md", visited, 0, 1000)
assert.False(t, result)
}
112 changes: 112 additions & 0 deletions internal/rules/concisenessscoring/classifier/model_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package classifier

import (
"fmt"
"math"
"strings"
"testing"
Expand Down Expand Up @@ -278,6 +279,117 @@ func TestClassify_EmptyInputKeepsCueSliceNonNil(t *testing.T) {
}
}

// =====================================================================
// Phase 4 coverage: validateArtifact field validation
// =====================================================================

func TestValidateArtifact_EmptyModelID(t *testing.T) {
a := artifact{ModelID: "", Version: "1.0", Threshold: 0.5}
err := validateArtifact(a)
if err == nil {
t.Fatal("expected error for empty model_id")
}
if !strings.Contains(err.Error(), "model_id") {
t.Fatalf("unexpected error: %v", err)
}
}

func TestValidateArtifact_EmptyVersion(t *testing.T) {
a := artifact{ModelID: "test", Version: "", Threshold: 0.5}
err := validateArtifact(a)
if err == nil {
t.Fatal("expected error for empty version")
}
if !strings.Contains(err.Error(), "version") {
t.Fatalf("unexpected error: %v", err)
}
}

func TestValidateArtifact_InvalidThreshold(t *testing.T) {
for _, th := range []float64{0, 1, -0.1, 1.5} {
a := artifact{ModelID: "test", Version: "1.0", Threshold: th}
err := validateArtifact(a)
if err == nil {
t.Fatalf("expected error for threshold %v", th)
}
if !strings.Contains(err.Error(), "threshold") {
t.Fatalf("threshold %v: unexpected error: %v", th, err)
}
}
}

func TestValidateArtifact_EmptyWeights(t *testing.T) {
a := artifact{
ModelID: "test",
Version: "1.0",
Threshold: 0.5,
Weights: map[string]float64{},
}
err := validateArtifact(a)
if err == nil {
t.Fatal("expected error for empty weights")
}
if !strings.Contains(err.Error(), "weights") {
t.Fatalf("unexpected error: %v", err)
}
}

// =====================================================================
// Phase 4 coverage: compileLexicon per-list errors
// =====================================================================

func TestCompileLexicon_InsufficientFillerWords(t *testing.T) {
raw := lexiconArtifact{FillerWords: []string{}}
_, err := compileLexicon(raw)
if err == nil {
t.Fatal("expected error for insufficient filler_words")
}
if !strings.Contains(err.Error(), "filler_words") {
t.Fatalf("unexpected error: %v", err)
}
}

func TestCompileLexicon_InsufficientModalWords(t *testing.T) {
fillers := make([]string, minFillerWords)
for i := range fillers {
fillers[i] = fmt.Sprintf("filler%d", i)
}
raw := lexiconArtifact{
FillerWords: fillers,
ModalWords: []string{},
}
_, err := compileLexicon(raw)
if err == nil {
t.Fatal("expected error for insufficient modal_words")
}
if !strings.Contains(err.Error(), "modal_words") {
t.Fatalf("unexpected error: %v", err)
}
}

func TestCompileLexicon_InsufficientVagueWords(t *testing.T) {
fillers := make([]string, minFillerWords)
for i := range fillers {
fillers[i] = fmt.Sprintf("filler%d", i)
}
modals := make([]string, minModalWords)
for i := range modals {
modals[i] = fmt.Sprintf("modal%d", i)
}
raw := lexiconArtifact{
FillerWords: fillers,
ModalWords: modals,
VagueWords: []string{},
}
_, err := compileLexicon(raw)
if err == nil {
t.Fatal("expected error for insufficient vague_words")
}
if !strings.Contains(err.Error(), "vague_words") {
t.Fatalf("unexpected error: %v", err)
}
}

func BenchmarkClassify(b *testing.B) {
model, err := LoadEmbedded()
if err != nil {
Expand Down
107 changes: 107 additions & 0 deletions internal/rules/crossfilereferenceintegrity/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,110 @@ func TestIsWithinRoot_NonexistentOutside(t *testing.T) {
root := resolveAbsRoot(dir)
require.False(t, isWithinRoot(root, filepath.Join(parent, "nonexistent.md")))
}

// =====================================================================
// Phase 4 coverage: DefaultSettings
// =====================================================================

func TestDefaultSettings(t *testing.T) {
r := &Rule{}
ds := r.DefaultSettings()
require.Equal(t, false, ds["strict"])
include, ok := ds["include"].([]string)
require.True(t, ok)
require.Len(t, include, 0)
exclude, ok := ds["exclude"].([]string)
require.True(t, ok)
require.Len(t, exclude, 0)
}

// =====================================================================
// Phase 4 coverage: configDiag (via invalid glob in Include field)
// =====================================================================

func TestCheck_InvalidIncludeGlobReturnsConfigDiag(t *testing.T) {
dir := t.TempDir()
sourcePath := filepath.Join(dir, "doc.md")
writeFile(t, sourcePath, "# Doc\n\nSee [link](file.md).\n")

f := newLintFile(t, sourcePath)
// Bypass ApplySettings by setting Include directly to an invalid glob.
r := &Rule{Include: []string{"["}}
diags := r.Check(f)
require.Len(t, diags, 1)
require.Contains(t, diags[0].Message, "invalid rule settings")
require.Equal(t, "MDS027", diags[0].RuleID)
}

// =====================================================================
// Phase 4 coverage: parseTarget edge cases
// =====================================================================

func TestParseTarget_AnchorOnly(t *testing.T) {
target, ok := parseTarget("#section")
require.True(t, ok)
require.Equal(t, "#section", target.Raw)
require.Equal(t, "section", target.Anchor)
require.True(t, target.LocalAnchor)
require.Equal(t, "", target.Path)
}

func TestParseTarget_Empty(t *testing.T) {
_, ok := parseTarget("")
require.False(t, ok)
}

func TestParseTarget_ProtocolRelative(t *testing.T) {
_, ok := parseTarget("//example.com/path")
require.False(t, ok)
}

func TestParseTarget_AbsoluteURL(t *testing.T) {
_, ok := parseTarget("https://example.com/path")
require.False(t, ok)
}

func TestParseTarget_PathWithAnchor(t *testing.T) {
target, ok := parseTarget("guide.md#intro")
require.True(t, ok)
require.Equal(t, "guide.md", target.Path)
require.Equal(t, "intro", target.Anchor)
require.False(t, target.LocalAnchor)
}

func TestParseTarget_EncodedPath(t *testing.T) {
target, ok := parseTarget("my%20file.md")
require.True(t, ok)
// url.Parse decodes percent-encoded characters in the path.
require.Equal(t, "my file.md", target.Path)
}

// =====================================================================
// Phase 4 coverage: toStringSlice edge cases
// =====================================================================

func TestToStringSlice_MixedTypes(t *testing.T) {
_, ok := toStringSlice([]any{"valid", 123})
require.False(t, ok)
}

func TestToStringSlice_NonSlice(t *testing.T) {
_, ok := toStringSlice("not a slice")
require.False(t, ok)
}

// =====================================================================
// Phase 4 coverage: anchor-only local ref validated against self anchors
// =====================================================================

func TestCheck_AnchorOnlyLinkMissingHeading(t *testing.T) {
dir := t.TempDir()
sourcePath := filepath.Join(dir, "doc.md")
// Link to #missing which doesn't exist in the doc.
writeFile(t, sourcePath, "# Doc\n\nSee [here](#missing).\n")

f := newLintFile(t, sourcePath)
diags := (&Rule{}).Check(f)
require.Len(t, diags, 1)
require.Contains(t, diags[0].Message, "#missing")
}
Loading
Loading