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 ffbf06de7..b65333a24 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", + "file-name-override", } 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("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 48788c9c8..1816d1c01 100644 --- a/runner/test.go +++ b/runner/test.go @@ -35,6 +35,7 @@ type TestRunner struct { Combine bool Quiet bool Output string + FileNameOverride string `mapstructure:"file-name-override"` } // 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 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.FileNameOverride + } + } + } + return results, nil } diff --git a/runner/test_test.go b/runner/test_test.go new file mode 100644 index 000000000..07b3a9c37 --- /dev/null +++ b/runner/test_test.go @@ -0,0 +1,155 @@ +package runner + +import ( + "os" + "path/filepath" + "testing" +) + +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) + } + + // Test cases + tests := []struct { + name string + fileNameOverride string + expectedFileName string + }{ + { + name: "with file-name-override", + fileNameOverride: "my-custom-file.yaml", + expectedFileName: "my-custom-file.yaml", + }, + { + name: "without file-name-override", + fileNameOverride: "", + expectedFileName: "-", + }, + } + + 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" + } +}` + w.Write([]byte(testInput)) + w.Close() + defer func() { os.Stdin = oldStdin }() + + // Run the test + ctx := t.Context() + 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") + } + + // 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() + + // 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) + } + + // 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{ + Policy: []string{policyDir}, + Namespace: []string{"main"}, + FileNameOverride: "overridden-name.yaml", + RegoVersion: "v1", + } + + // Run with a regular file (not stdin) + ctx := t.Context() + results, err := runner.Run(ctx, []string{configFile}) + if err != nil { + t.Fatalf("Run failed: %v", err) + } + + // Verify results + if len(results) == 0 { + t.Fatal("Expected at least one result") + } + + // 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) + } +}