Skip to content

Commit b3f6051

Browse files
Ajit Pratap SinghAjit Pratap Singh
authored andcommitted
fix: address architect review — port rules fix, add tests, fix bugs
- Port lintFile rules-filtering logic from PR #312 (allRules + selection) - Move package-level mutable vars into actionFlags struct - Replace *string pointers in optimizecmd with cmdutil.RootFlags - Fix IsValidSQLFileExtension('') returning true - Add tests for cmdutil package (IsValidSQLFileExtension, ExpandDirectory, LooksLikeSQL)
1 parent cdac051 commit b3f6051

File tree

5 files changed

+106
-25
lines changed

5 files changed

+106
-25
lines changed

cmd/gosqlx/cmd/root.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/spf13/cobra"
55

66
"github.com/ajitpratap0/GoSQLX/cmd/gosqlx/internal/actioncmd"
7+
"github.com/ajitpratap0/GoSQLX/cmd/gosqlx/internal/cmdutil"
78
"github.com/ajitpratap0/GoSQLX/cmd/gosqlx/internal/lspcmd"
89
"github.com/ajitpratap0/GoSQLX/cmd/gosqlx/internal/optimizecmd"
910
)
@@ -167,5 +168,9 @@ func init() {
167168
// Register subcommands from internal packages
168169
rootCmd.AddCommand(lspcmd.NewCmd())
169170
rootCmd.AddCommand(actioncmd.NewCmd())
170-
rootCmd.AddCommand(optimizecmd.NewCmd(&outputFile, &format))
171+
rootCmd.AddCommand(optimizecmd.NewCmd(&cmdutil.RootFlags{
172+
Verbose: &verbose,
173+
OutputFile: &outputFile,
174+
Format: &format,
175+
}))
171176
}

cmd/gosqlx/internal/actioncmd/action.go

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,20 @@ func builtinRules() []linter.Rule {
3636
}
3737
}
3838

39-
var (
40-
actionFiles string
41-
actionRules string
42-
actionSeverity string
43-
actionConfig string
44-
actionTimeout int
45-
)
39+
// actionFlags holds the flag values for the action subcommand,
40+
// avoiding package-level mutable state.
41+
type actionFlags struct {
42+
files string
43+
rules string
44+
severity string
45+
config string
46+
timeout int
47+
}
4648

4749
// NewCmd returns the action cobra.Command.
4850
func NewCmd() *cobra.Command {
51+
f := &actionFlags{}
52+
4953
cmd := &cobra.Command{
5054
Use: "action",
5155
Short: "Run GoSQLX checks for GitHub Actions CI",
@@ -61,14 +65,16 @@ Environment variables (also settable via flags):
6165
SEVERITY - threshold: error, warning, info (default: warning)
6266
CONFIG - path to .gosqlx.yml config file
6367
TIMEOUT - per-file timeout in seconds (default: 600)`,
64-
RunE: runAction,
68+
RunE: func(cmd *cobra.Command, args []string) error {
69+
return runAction(cmd, args, f)
70+
},
6571
}
6672

67-
cmd.Flags().StringVar(&actionFiles, "files", "", "glob pattern for SQL files (env: SQL_FILES)")
68-
cmd.Flags().StringVar(&actionRules, "rules", "", "comma-separated lint rules (env: RULES)")
69-
cmd.Flags().StringVar(&actionSeverity, "severity", "", "severity threshold: error, warning, info (env: SEVERITY)")
70-
cmd.Flags().StringVar(&actionConfig, "config", "", "path to config file (env: CONFIG)")
71-
cmd.Flags().IntVar(&actionTimeout, "timeout", 0, "per-file timeout in seconds (env: TIMEOUT)")
73+
cmd.Flags().StringVar(&f.files, "files", "", "glob pattern for SQL files (env: SQL_FILES)")
74+
cmd.Flags().StringVar(&f.rules, "rules", "", "comma-separated lint rules (env: RULES)")
75+
cmd.Flags().StringVar(&f.severity, "severity", "", "severity threshold: error, warning, info (env: SEVERITY)")
76+
cmd.Flags().StringVar(&f.config, "config", "", "path to config file (env: CONFIG)")
77+
cmd.Flags().IntVar(&f.timeout, "timeout", 0, "per-file timeout in seconds (env: TIMEOUT)")
7278

7379
return cmd
7480
}
@@ -83,13 +89,13 @@ func envDefault(flagVal, envKey, fallback string) string {
8389
return fallback
8490
}
8591

86-
func runAction(_ *cobra.Command, _ []string) error {
87-
pattern := envDefault(actionFiles, "SQL_FILES", "**/*.sql")
88-
rules := envDefault(actionRules, "RULES", "")
89-
severity := envDefault(actionSeverity, "SEVERITY", "warning")
90-
cfgPath := envDefault(actionConfig, "CONFIG", "")
92+
func runAction(_ *cobra.Command, _ []string, f *actionFlags) error {
93+
pattern := envDefault(f.files, "SQL_FILES", "**/*.sql")
94+
rules := envDefault(f.rules, "RULES", "")
95+
severity := envDefault(f.severity, "SEVERITY", "warning")
96+
cfgPath := envDefault(f.config, "CONFIG", "")
9197

92-
timeoutSec := actionTimeout
98+
timeoutSec := f.timeout
9399
if timeoutSec == 0 {
94100
if v := os.Getenv("TIMEOUT"); v != "" {
95101
if parsed, err := strconv.Atoi(v); err == nil {

cmd/gosqlx/internal/cmdutil/input.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func DetectAndReadInput(input string) (*InputResult, error) {
9191
// IsValidSQLFileExtension checks if the file extension is acceptable for SQL.
9292
func IsValidSQLFileExtension(ext string) bool {
9393
switch ext {
94-
case ".sql", ".txt", "":
94+
case ".sql", ".txt":
9595
return true
9696
default:
9797
return false
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package cmdutil
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestIsValidSQLFileExtension(t *testing.T) {
10+
tests := []struct {
11+
ext string
12+
want bool
13+
}{
14+
{".sql", true},
15+
{".txt", true},
16+
{"", false},
17+
{".go", false},
18+
{".csv", false},
19+
{".SQL", false}, // case-sensitive
20+
}
21+
for _, tt := range tests {
22+
if got := IsValidSQLFileExtension(tt.ext); got != tt.want {
23+
t.Errorf("IsValidSQLFileExtension(%q) = %v, want %v", tt.ext, got, tt.want)
24+
}
25+
}
26+
}
27+
28+
func TestExpandDirectory(t *testing.T) {
29+
dir := t.TempDir()
30+
31+
// Create test files
32+
os.WriteFile(filepath.Join(dir, "a.sql"), []byte("SELECT 1"), 0644)
33+
os.WriteFile(filepath.Join(dir, "b.txt"), []byte("SELECT 2"), 0644)
34+
os.WriteFile(filepath.Join(dir, "c.go"), []byte("package x"), 0644)
35+
os.Mkdir(filepath.Join(dir, "subdir"), 0755)
36+
37+
files, err := ExpandDirectory(dir)
38+
if err != nil {
39+
t.Fatalf("ExpandDirectory() error: %v", err)
40+
}
41+
42+
// Should find .sql and .txt but not .go or subdir
43+
if len(files) != 2 {
44+
t.Errorf("ExpandDirectory() returned %d files, want 2: %v", len(files), files)
45+
}
46+
}
47+
48+
func TestExpandDirectory_NotExist(t *testing.T) {
49+
_, err := ExpandDirectory("/nonexistent/path")
50+
if err == nil {
51+
t.Error("ExpandDirectory() expected error for nonexistent path")
52+
}
53+
}
54+
55+
func TestLooksLikeSQL(t *testing.T) {
56+
tests := []struct {
57+
input string
58+
want bool
59+
}{
60+
{"SELECT * FROM t", true},
61+
{"INSERT INTO t VALUES (1)", true},
62+
{"hello world", false},
63+
{"", false},
64+
{"CREATE TABLE t (id INT)", true},
65+
}
66+
for _, tt := range tests {
67+
if got := LooksLikeSQL(tt.input); got != tt.want {
68+
t.Errorf("LooksLikeSQL(%q) = %v, want %v", tt.input, got, tt.want)
69+
}
70+
}
71+
}

cmd/gosqlx/internal/optimizecmd/optimize.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ import (
1616

1717
// NewCmd returns the optimize cobra.Command.
1818
//
19-
// The outputFile and format parameters are pointers to the root command's
20-
// persistent flag values, allowing this subcommand to access global flags.
21-
func NewCmd(outputFile *string, format *string) *cobra.Command {
19+
// rf provides access to the root command's persistent flag values.
20+
func NewCmd(rf *cmdutil.RootFlags) *cobra.Command {
2221
cmd := &cobra.Command{
2322
Use: "optimize [file|sql]",
2423
Short: "Analyze SQL for optimization opportunities",
@@ -48,7 +47,7 @@ Output includes an optimization score (0-100), complexity classification,
4847
and detailed suggestions.`,
4948
Args: cobra.MaximumNArgs(1),
5049
RunE: func(cmd *cobra.Command, args []string) error {
51-
return runOptimize(cmd, args, outputFile, format)
50+
return runOptimize(cmd, args, rf.OutputFile, rf.Format)
5251
},
5352
}
5453

0 commit comments

Comments
 (0)