From 9f2b39f0b44ec554fd36f94ca3ae57368772e24c Mon Sep 17 00:00:00 2001 From: "nikos.nikolakakis" Date: Thu, 21 Aug 2025 19:52:19 +0300 Subject: [PATCH 1/2] Add --group flag to override group name for stdin input - Add --group CLI flag to test command for custom group naming - Override FileName from '-' to custom group name when --group is provided - Enables better identification of policy violations in CI/CD workflows - Add unit tests for group flag functionality Fixes #1095 Signed-off-by: nikos.nikolakakis --- internal/commands/test.go | 2 + runner/test.go | 10 +++++ runner/test_test.go | 77 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 runner/test_test.go diff --git a/internal/commands/test.go b/internal/commands/test.go index ffbf06de7..29b1f90c5 100644 --- a/internal/commands/test.go +++ b/internal/commands/test.go @@ -115,6 +115,7 @@ func NewTestCommand(ctx context.Context) *cobra.Command { "junit-hide-message", "quiet", "tls", + "group", } for _, name := range flagNames { if err := viper.BindPFlag(name, cmd.Flags().Lookup(name)); err != nil { @@ -196,6 +197,7 @@ func NewTestCommand(ctx context.Context) *cobra.Command { cmd.Flags().StringSlice("proto-file-dirs", []string{}, "A list of directories containing Protocol Buffer definitions") cmd.Flags().Bool("tls", true, "Use TLS to access the registry") + cmd.Flags().String("group", "", "Override the group name for stdin input (used for output formatting)") return &cmd } diff --git a/runner/test.go b/runner/test.go index 48788c9c8..a4bb69e9b 100644 --- a/runner/test.go +++ b/runner/test.go @@ -35,6 +35,7 @@ type TestRunner struct { Combine bool Quiet bool Output string + Group string } // Run executes the TestRunner, verifying all Rego policies against the given @@ -110,6 +111,15 @@ func (t *TestRunner) Run(ctx context.Context, fileList []string) (output.CheckRe } } + // Override group name for stdin input if --group flag is provided + if t.Group != "" { + for i := range results { + if results[i].FileName == "-" { + results[i].FileName = t.Group + } + } + } + return results, nil } diff --git a/runner/test_test.go b/runner/test_test.go new file mode 100644 index 000000000..45389a630 --- /dev/null +++ b/runner/test_test.go @@ -0,0 +1,77 @@ +package runner + +import ( + "testing" + + "github.com/open-policy-agent/conftest/output" +) + +func TestTestRunner_GroupFlag(t *testing.T) { + // Create a mock TestRunner with Group set + runner := TestRunner{ + Group: "my-custom-group", + } + + // Create mock results with stdin filename + results := output.CheckResults{ + { + FileName: "-", + Namespace: "main", + Failures: []output.Result{{Message: "test failure"}}, + }, + { + FileName: "regular-file.yaml", + Namespace: "main", + Failures: []output.Result{{Message: "another failure"}}, + }, + } + + // Test the group name override logic + if runner.Group != "" { + for i := range results { + if results[i].FileName == "-" { + results[i].FileName = runner.Group + } + } + } + + // Verify the stdin result was updated + if results[0].FileName != "my-custom-group" { + t.Errorf("Expected stdin filename to be overridden to 'my-custom-group', got '%s'", results[0].FileName) + } + + // Verify the regular file result was not changed + if results[1].FileName != "regular-file.yaml" { + t.Errorf("Expected regular filename to remain 'regular-file.yaml', got '%s'", results[1].FileName) + } +} + +func TestTestRunner_GroupFlagEmpty(t *testing.T) { + // Create a mock TestRunner without Group set + runner := TestRunner{ + Group: "", + } + + // Create mock results with stdin filename + results := output.CheckResults{ + { + FileName: "-", + Namespace: "main", + Failures: []output.Result{{Message: "test failure"}}, + }, + } + + // Test the group name override logic + if runner.Group != "" { + for i := range results { + if results[i].FileName == "-" { + results[i].FileName = runner.Group + } + } + } + + // Verify the stdin result was NOT updated + if results[0].FileName != "-" { + t.Errorf("Expected stdin filename to remain '-', got '%s'", results[0].FileName) + } +} \ No newline at end of file From 673806ce7fd647c23e3e2bb14a249090b41d1e47 Mon Sep 17 00:00:00 2001 From: "nikos.nikolakakis" Date: Fri, 19 Sep 2025 13:49:54 +0300 Subject: [PATCH 2/2] refactor: rename group flag to file-name-override and improve tests - Renamed flag from 'group' to 'file-name-override' for clarity - Rewrote tests to use TestRunner.Run() method with proper test fixtures - Added comprehensive acceptance tests for the new flag - Fixed Rego v1 syntax in test policies - Added proper error handling and edge case testing Addresses review feedback from PR #1163 Signed-off-by: nikos.nikolakakis --- acceptance.bats | 27 ++++++ internal/commands/test.go | 4 +- runner/test.go | 8 +- runner/test_test.go | 175 ++++++++++++++++++++++++++++---------- 4 files changed, 161 insertions(+), 53 deletions(-) diff --git a/acceptance.bats b/acceptance.bats index 3dcee36c2..400ea0aae 100755 --- a/acceptance.bats +++ b/acceptance.bats @@ -530,3 +530,30 @@ EOF" [ "$status" -eq 1 ] [[ "$output" =~ "10 tests, 3 passed, 0 warnings, 7 failures, 0 exceptions" ]] } + +@test "File name override flag replaces stdin filename in output" { + run bash -c "./conftest test --file-name-override='my-custom-file.yaml' -p examples/kubernetes/policy - < examples/kubernetes/service.yaml" + [ "$status" -eq 0 ] + [[ "$output" =~ "my-custom-file.yaml" ]] + [[ ! "$output" =~ "-" ]] +} + +@test "File name override flag does not affect regular files" { + run ./conftest test --file-name-override='override.yaml' -p examples/kubernetes/policy examples/kubernetes/service.yaml + [ "$status" -eq 0 ] + [[ "$output" =~ "examples/kubernetes/service.yaml" ]] + [[ ! "$output" =~ "override.yaml" ]] +} + +@test "File name override flag works with JSON output" { + run bash -c "./conftest test --file-name-override='custom.json' --output json -p examples/kubernetes/policy - < examples/kubernetes/service.yaml" + [ "$status" -eq 0 ] + [[ "$output" =~ "\"filename\":\"custom.json\"" ]] + [[ ! "$output" =~ "\"filename\":\"-\"" ]] +} + +@test "Without file name override flag, stdin shows as dash" { + run bash -c "./conftest test -p examples/kubernetes/policy - < examples/kubernetes/service.yaml" + [ "$status" -eq 0 ] + [[ "$output" =~ "-" ]] +} diff --git a/internal/commands/test.go b/internal/commands/test.go index 29b1f90c5..b65333a24 100644 --- a/internal/commands/test.go +++ b/internal/commands/test.go @@ -115,7 +115,7 @@ func NewTestCommand(ctx context.Context) *cobra.Command { "junit-hide-message", "quiet", "tls", - "group", + "file-name-override", } for _, name := range flagNames { if err := viper.BindPFlag(name, cmd.Flags().Lookup(name)); err != nil { @@ -197,7 +197,7 @@ func NewTestCommand(ctx context.Context) *cobra.Command { cmd.Flags().StringSlice("proto-file-dirs", []string{}, "A list of directories containing Protocol Buffer definitions") cmd.Flags().Bool("tls", true, "Use TLS to access the registry") - cmd.Flags().String("group", "", "Override the group name for stdin input (used for output formatting)") + cmd.Flags().String("file-name-override", "", "Override the file name for stdin input (used for output formatting)") return &cmd } diff --git a/runner/test.go b/runner/test.go index a4bb69e9b..1816d1c01 100644 --- a/runner/test.go +++ b/runner/test.go @@ -35,7 +35,7 @@ type TestRunner struct { Combine bool Quiet bool Output string - Group string + FileNameOverride string `mapstructure:"file-name-override"` } // Run executes the TestRunner, verifying all Rego policies against the given @@ -111,11 +111,11 @@ func (t *TestRunner) Run(ctx context.Context, fileList []string) (output.CheckRe } } - // Override group name for stdin input if --group flag is provided - if t.Group != "" { + // Override file name for stdin input if --file-name-override flag is provided + if t.FileNameOverride != "" { for i := range results { if results[i].FileName == "-" { - results[i].FileName = t.Group + results[i].FileName = t.FileNameOverride } } } diff --git a/runner/test_test.go b/runner/test_test.go index 45389a630..816166f75 100644 --- a/runner/test_test.go +++ b/runner/test_test.go @@ -1,77 +1,158 @@ package runner import ( + "context" + "os" + "path/filepath" "testing" - - "github.com/open-policy-agent/conftest/output" ) -func TestTestRunner_GroupFlag(t *testing.T) { - // Create a mock TestRunner with Group set - runner := TestRunner{ - Group: "my-custom-group", +func TestTestRunner_FileNameOverrideFlag(t *testing.T) { + // Create a temporary directory for test files + tmpDir := t.TempDir() + + // Create a test policy file + policyDir := filepath.Join(tmpDir, "policy") + if err := os.Mkdir(policyDir, 0755); err != nil { + t.Fatalf("Failed to create policy directory: %v", err) + } + + policyContent := `package main + +deny contains msg if { + input.kind == "Deployment" + input.metadata.name == "test" + msg := "test deployment found" +} +` + policyFile := filepath.Join(policyDir, "test.rego") + if err := os.WriteFile(policyFile, []byte(policyContent), 0644); err != nil { + t.Fatalf("Failed to write policy file: %v", err) } - // Create mock results with stdin filename - results := output.CheckResults{ + // Test cases + tests := []struct { + name string + fileNameOverride string + expectedFileName string + }{ { - FileName: "-", - Namespace: "main", - Failures: []output.Result{{Message: "test failure"}}, + name: "with file-name-override", + fileNameOverride: "my-custom-file.yaml", + expectedFileName: "my-custom-file.yaml", }, { - FileName: "regular-file.yaml", - Namespace: "main", - Failures: []output.Result{{Message: "another failure"}}, + name: "without file-name-override", + fileNameOverride: "", + expectedFileName: "-", }, } - // Test the group name override logic - if runner.Group != "" { - for i := range results { - if results[i].FileName == "-" { - results[i].FileName = runner.Group + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Create TestRunner with FileNameOverride set + runner := TestRunner{ + Policy: []string{policyDir}, + Namespace: []string{"main"}, + FileNameOverride: tc.fileNameOverride, + RegoVersion: "v1", } - } + + // Create stdin input by using "-" as the file path + fileList := []string{"-"} + + // Mock stdin with test data + oldStdin := os.Stdin + r, w, _ := os.Pipe() + os.Stdin = r + testInput := `{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "name": "test" } +}` + go func() { + defer w.Close() + w.Write([]byte(testInput)) + }() + defer func() { os.Stdin = oldStdin }() + + // Run the test + ctx := context.Background() + results, err := runner.Run(ctx, fileList) + if err != nil { + t.Fatalf("Run failed: %v", err) + } + + // Verify results + if len(results) == 0 { + t.Fatal("Expected at least one result") + } - // Verify the stdin result was updated - if results[0].FileName != "my-custom-group" { - t.Errorf("Expected stdin filename to be overridden to 'my-custom-group', got '%s'", results[0].FileName) + // Check that the filename was properly overridden + if results[0].FileName != tc.expectedFileName { + t.Errorf("Expected filename to be '%s', got '%s'", tc.expectedFileName, results[0].FileName) + } + }) } +} + +func TestTestRunner_FileNameOverrideOnlyAffectsStdin(t *testing.T) { + // Create a temporary directory for test files + tmpDir := t.TempDir() - // Verify the regular file result was not changed - if results[1].FileName != "regular-file.yaml" { - t.Errorf("Expected regular filename to remain 'regular-file.yaml', got '%s'", results[1].FileName) + // Create a test policy file + policyDir := filepath.Join(tmpDir, "policy") + if err := os.Mkdir(policyDir, 0755); err != nil { + t.Fatalf("Failed to create policy directory: %v", err) } + + policyContent := `package main + +deny contains msg if { + input.kind == "Deployment" + msg := "deployment found" } +` + policyFile := filepath.Join(policyDir, "test.rego") + if err := os.WriteFile(policyFile, []byte(policyContent), 0644); err != nil { + t.Fatalf("Failed to write policy file: %v", err) + } -func TestTestRunner_GroupFlagEmpty(t *testing.T) { - // Create a mock TestRunner without Group set + // Create a test config file + configContent := `apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-file +` + configFile := filepath.Join(tmpDir, "deployment.yaml") + if err := os.WriteFile(configFile, []byte(configContent), 0644); err != nil { + t.Fatalf("Failed to write config file: %v", err) + } + + // Create TestRunner with FileNameOverride set runner := TestRunner{ - Group: "", + Policy: []string{policyDir}, + Namespace: []string{"main"}, + FileNameOverride: "overridden-name.yaml", + RegoVersion: "v1", } - // Create mock results with stdin filename - results := output.CheckResults{ - { - FileName: "-", - Namespace: "main", - Failures: []output.Result{{Message: "test failure"}}, - }, + // Run with a regular file (not stdin) + ctx := context.Background() + results, err := runner.Run(ctx, []string{configFile}) + if err != nil { + t.Fatalf("Run failed: %v", err) } - // Test the group name override logic - if runner.Group != "" { - for i := range results { - if results[i].FileName == "-" { - results[i].FileName = runner.Group - } - } + // Verify results + if len(results) == 0 { + t.Fatal("Expected at least one result") } - // Verify the stdin result was NOT updated - if results[0].FileName != "-" { - t.Errorf("Expected stdin filename to remain '-', got '%s'", results[0].FileName) + // Check that the regular file name was NOT overridden + if results[0].FileName != configFile { + t.Errorf("Expected filename to remain '%s', got '%s'", configFile, results[0].FileName) } -} \ No newline at end of file +}