From 6806b20651a24793a725b0a973b6067158d596fc Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 09:02:06 +0000 Subject: [PATCH] fix(dx): fix build error, replace os.* with go-io, add scanner tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix RegisterCommands call to match single-arg signature; register locales separately via i18n.RegisterLocales - Replace os.Stat/os.ReadDir with coreio.Local equivalents in detect.go, format.go, main.go, cmd_docblock.go - Add tests for languagesFromRules, IsExcludedDir, ScanFile edge cases - Coverage: pkg/lint 71.0% → 72.9%, pkg/detect 100%, pkg/php 68.7% Co-Authored-By: Virgil --- cmd/core-lint/main.go | 3 +- cmd/qa/cmd_docblock.go | 3 +- cmd/qa/cmd_qa.go | 5 ++- pkg/detect/detect.go | 12 +++--- pkg/lint/scanner_test.go | 83 ++++++++++++++++++++++++++++++++++++++++ pkg/php/format.go | 4 +- 6 files changed, 99 insertions(+), 11 deletions(-) diff --git a/cmd/core-lint/main.go b/cmd/core-lint/main.go index 0e9eeef..6c13e9c 100644 --- a/cmd/core-lint/main.go +++ b/cmd/core-lint/main.go @@ -7,6 +7,7 @@ import ( "strings" "forge.lthn.ai/core/cli/pkg/cli" + coreio "forge.lthn.ai/core/go-io" coreerr "forge.lthn.ai/core/go-log" lint "forge.lthn.ai/core/lint" lintpkg "forge.lthn.ai/core/lint/pkg/lint" @@ -66,7 +67,7 @@ func addLintCommands(root *cli.Command) { var allFindings []lintpkg.Finding for _, p := range paths { - info, err := os.Stat(p) + info, err := coreio.Local.Stat(p) if err != nil { return coreerr.E("cmd.check", "stat "+p, err) } diff --git a/cmd/qa/cmd_docblock.go b/cmd/qa/cmd_docblock.go index 5b33593..1811a55 100644 --- a/cmd/qa/cmd_docblock.go +++ b/cmd/qa/cmd_docblock.go @@ -20,6 +20,7 @@ import ( "strings" "forge.lthn.ai/core/cli/pkg/cli" + coreio "forge.lthn.ai/core/go-io" "forge.lthn.ai/core/go-i18n" ) @@ -235,7 +236,7 @@ func expandPatterns(patterns []string) ([]string, error) { // hasGoFiles checks if a directory contains Go files. func hasGoFiles(dir string) bool { - entries, err := os.ReadDir(dir) + entries, err := coreio.Local.List(dir) if err != nil { return false } diff --git a/cmd/qa/cmd_qa.go b/cmd/qa/cmd_qa.go index 674595e..0b88bfb 100644 --- a/cmd/qa/cmd_qa.go +++ b/cmd/qa/cmd_qa.go @@ -12,12 +12,13 @@ package qa import ( "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go-i18n" + i18n "forge.lthn.ai/core/go-i18n" "forge.lthn.ai/core/lint/locales" ) func init() { - cli.RegisterCommands(AddQACommands, locales.FS) + i18n.RegisterLocales(locales.FS, ".") + cli.RegisterCommands(AddQACommands) } // Style aliases from shared package diff --git a/pkg/detect/detect.go b/pkg/detect/detect.go index 284dee2..203a052 100644 --- a/pkg/detect/detect.go +++ b/pkg/detect/detect.go @@ -1,7 +1,11 @@ // Package detect identifies project types by examining filesystem markers. package detect -import "os" +import ( + "path/filepath" + + coreio "forge.lthn.ai/core/go-io" +) // ProjectType identifies a project's language/framework. type ProjectType string @@ -13,14 +17,12 @@ const ( // IsGoProject returns true if dir contains a go.mod file. func IsGoProject(dir string) bool { - _, err := os.Stat(dir + "/go.mod") - return err == nil + return coreio.Local.Exists(filepath.Join(dir, "go.mod")) } // IsPHPProject returns true if dir contains a composer.json file. func IsPHPProject(dir string) bool { - _, err := os.Stat(dir + "/composer.json") - return err == nil + return coreio.Local.Exists(filepath.Join(dir, "composer.json")) } // DetectAll returns all detected project types in the directory. diff --git a/pkg/lint/scanner_test.go b/pkg/lint/scanner_test.go index 717c390..f9bdb5f 100644 --- a/pkg/lint/scanner_test.go +++ b/pkg/lint/scanner_test.go @@ -209,6 +209,89 @@ func TestScanDir_Good_Subdirectories(t *testing.T) { require.Len(t, findings, 1) } +func TestLanguagesFromRules_Good(t *testing.T) { + rules := []Rule{ + {Languages: []string{"go", "php"}}, + {Languages: []string{"go", "ts"}}, + {Languages: []string{"py"}}, + } + langs := languagesFromRules(rules) + assert.Equal(t, []string{"go", "php", "py", "ts"}, langs) +} + +func TestLanguagesFromRules_Good_Empty(t *testing.T) { + langs := languagesFromRules(nil) + assert.Empty(t, langs) +} + +func TestIsExcludedDir_Good(t *testing.T) { + tests := []struct { + name string + want bool + }{ + {"vendor", true}, + {"node_modules", true}, + {".git", true}, + {"testdata", true}, + {".core", true}, + {".hidden", true}, // any dot-prefixed dir + {".idea", true}, // any dot-prefixed dir + {"src", false}, + {"pkg", false}, + {"cmd", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, IsExcludedDir(tt.name)) + }) + } +} + +func TestScanFile_Bad_UnrecognisedExtension(t *testing.T) { + dir := t.TempDir() + file := filepath.Join(dir, "readme.txt") + require.NoError(t, os.WriteFile(file, []byte("TODO: fix this"), 0o644)) + + rules := []Rule{ + { + ID: "test-001", + Title: "Found TODO", + Severity: "low", + Languages: []string{"go"}, + Pattern: `TODO`, + Fix: "Remove TODO", + Detection: "regex", + }, + } + + s, err := NewScanner(rules) + require.NoError(t, err) + + findings, err := s.ScanFile(file) + require.NoError(t, err) + assert.Empty(t, findings, "should not match unrecognised extensions") +} + +func TestScanFile_Bad_NonexistentFile(t *testing.T) { + rules := []Rule{ + { + ID: "test-001", + Title: "Test", + Severity: "low", + Languages: []string{"go"}, + Pattern: `TODO`, + Fix: "Fix", + Detection: "regex", + }, + } + + s, err := NewScanner(rules) + require.NoError(t, err) + + _, err = s.ScanFile("/nonexistent/test.go") + assert.Error(t, err) +} + func TestScanDir_Bad_NonexistentDir(t *testing.T) { rules := []Rule{ { diff --git a/pkg/php/format.go b/pkg/php/format.go index b7104f8..8521aa7 100644 --- a/pkg/php/format.go +++ b/pkg/php/format.go @@ -8,13 +8,13 @@ import ( "os/exec" "path/filepath" + coreio "forge.lthn.ai/core/go-io" coreerr "forge.lthn.ai/core/go-log" ) // fileExists reports whether the named file or directory exists. func fileExists(path string) bool { - _, err := os.Stat(path) - return err == nil + return coreio.Local.Exists(path) } // FormatOptions configures PHP code formatting. -- 2.45.3