feat: enhanced and added testcases to increase unit test coverage

Signed-off-by: Animesh Pathak <kurosakiichigo.songoku@gmail.com>
This commit is contained in:
Animesh Pathak 2025-02-13 16:17:33 +05:30
parent 1bd44986dd
commit 6d3d111bf2
7 changed files with 601 additions and 74 deletions

View file

@ -0,0 +1,94 @@
package main
import (
"context"
"testing"
"github.com/yannh/kubeconform/pkg/validator"
)
type MockOutput struct{}
func (m *MockOutput) Write(res validator.Result) error {
return nil
}
func (m *MockOutput) Flush() error {
return nil
}
// Test generated using Keploy
func TestProcessResults_SuccessWithValidResults(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
validationResults := make(chan validator.Result, 2)
validationResults <- validator.Result{Status: validator.Valid}
validationResults <- validator.Result{Status: validator.Valid}
close(validationResults)
outputMock := &MockOutput{}
exitOnError := false
resultChan := processResults(cancel, outputMock, validationResults, exitOnError)
success := <-resultChan
if !success {
t.Errorf("Expected success to be true, got false")
}
if ctx.Err() != nil {
t.Errorf("Context should not be canceled")
}
}
// Test generated using Keploy
func TestProcessResults_FailureWithInvalidResults(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
validationResults := make(chan validator.Result, 2)
validationResults <- validator.Result{Status: validator.Valid}
validationResults <- validator.Result{Status: validator.Invalid}
close(validationResults)
outputMock := &MockOutput{}
exitOnError := false
resultChan := processResults(cancel, outputMock, validationResults, exitOnError)
success := <-resultChan
if success {
t.Errorf("Expected success to be false, got true")
}
if ctx.Err() != nil {
t.Errorf("Context should not be canceled when exitOnError is false")
}
}
// Test generated using Keploy
func TestProcessResults_CancelOnInvalidWithExitOnError(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
validationResults := make(chan validator.Result, 2)
validationResults <- validator.Result{Status: validator.Valid}
validationResults <- validator.Result{Status: validator.Invalid}
close(validationResults)
outputMock := &MockOutput{}
exitOnError := true
resultChan := processResults(cancel, outputMock, validationResults, exitOnError)
success := <-resultChan
if success {
t.Errorf("Expected success to be false, got true")
}
select {
case <-ctx.Done():
default:
t.Errorf("Expected context to be canceled when exitOnError is true")
}
}

View file

@ -1,30 +1,37 @@
package output
import (
"fmt"
"io"
"fmt"
"io"
"github.com/yannh/kubeconform/pkg/validator"
"github.com/yannh/kubeconform/pkg/validator"
)
type Output interface {
Write(validator.Result) error
Flush() error
Write(validator.Result) error
Flush() error
}
func New(w io.Writer, outputFormat string, printSummary, isStdin, verbose bool) (Output, error) {
switch {
case outputFormat == "json":
return jsonOutput(w, printSummary, isStdin, verbose), nil
case outputFormat == "junit":
return junitOutput(w, printSummary, isStdin, verbose), nil
case outputFormat == "pretty":
return prettyOutput(w, printSummary, isStdin, verbose), nil
case outputFormat == "tap":
return tapOutput(w, printSummary, isStdin, verbose), nil
case outputFormat == "text":
return textOutput(w, printSummary, isStdin, verbose), nil
default:
return nil, fmt.Errorf("'outputFormat' must be 'json', 'junit', 'pretty', 'tap' or 'text'")
}
switch outputFormat {
case "json":
return jsonOutput(w, printSummary, isStdin, verbose), nil
case "junit":
return junitOutput(w, printSummary, isStdin, verbose), nil
case "pretty":
return prettyOutput(w, printSummary, isStdin, verbose), nil
case "tap":
return tapOutput(w, printSummary, isStdin, verbose), nil
case "text":
return textOutput(w, printSummary, isStdin, verbose), nil
default:
return nil, fmt.Errorf("'outputFormat' must be 'json', 'junit', 'pretty', 'tap' or 'text'")
}
}
// Mock writer for testing purposes
type mockWriter struct{}
func (m *mockWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}

84
pkg/output/output_test.go Normal file
View file

@ -0,0 +1,84 @@
package output
import (
"testing"
)
// Test generated using Keploy
func TestNew_JSONOutput(t *testing.T) {
writer := &mockWriter{}
output, err := New(writer, "json", false, false, false)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if output == nil {
t.Fatalf("Expected a valid Output implementation, got nil")
}
}
// Test generated using Keploy
func TestNew_JUnitOutput(t *testing.T) {
writer := &mockWriter{}
output, err := New(writer, "junit", false, false, false)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if output == nil {
t.Fatalf("Expected a valid Output implementation, got nil")
}
}
// Test generated using Keploy
func TestNew_PrettyOutput(t *testing.T) {
writer := &mockWriter{}
output, err := New(writer, "pretty", false, false, false)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if output == nil {
t.Fatalf("Expected a valid Output implementation, got nil")
}
}
// Test generated using Keploy
func TestNew_UnsupportedFormat(t *testing.T) {
writer := &mockWriter{}
_, err := New(writer, "unsupported", false, false, false)
if err == nil {
t.Fatalf("Expected an error, got nil")
}
expectedError := "'outputFormat' must be 'json', 'junit', 'pretty', 'tap' or 'text'"
if err.Error() != expectedError {
t.Errorf("Expected error message '%s', got '%s'", expectedError, err.Error())
}
}
// Test generated using Keploy
func TestNew_TapOutput(t *testing.T) {
writer := &mockWriter{}
output, err := New(writer, "tap", false, false, false)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if output == nil {
t.Fatalf("Expected a valid Output implementation, got nil")
}
}
// Test generated using Keploy
func TestNew_TextOutput(t *testing.T) {
writer := &mockWriter{}
output, err := New(writer, "text", false, false, false)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if output == nil {
t.Fatalf("Expected a valid Output implementation, got nil")
}
}

View file

@ -1,63 +1,61 @@
package registry
import (
"errors"
"fmt"
"io"
"log"
"os"
"errors"
"fmt"
"io"
"log"
"os"
)
type LocalRegistry struct {
pathTemplate string
strict bool
debug bool
pathTemplate string
strict bool
debug bool
schemaPathFunc func(pathTemplate, resourceKind, resourceAPIVersion, k8sVersion string, strict bool) (string, error)
}
// NewLocalSchemas creates a new "registry", that will serve schemas from files, given a list of schema filenames
func newLocalRegistry(pathTemplate string, strict bool, debug bool) (*LocalRegistry, error) {
return &LocalRegistry{
pathTemplate,
strict,
debug,
}, nil
return &LocalRegistry{
pathTemplate: pathTemplate,
strict: strict,
debug: debug,
schemaPathFunc: schemaPath,
}, nil
}
// DownloadSchema retrieves the schema from a file for the resource
func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) (string, []byte, error) {
schemaFile, err := schemaPath(r.pathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict)
if err != nil {
return schemaFile, []byte{}, nil
}
f, err := os.Open(schemaFile)
if err != nil {
if os.IsNotExist(err) {
msg := fmt.Sprintf("could not open file %s", schemaFile)
if r.debug {
log.Print(msg)
}
return schemaFile, nil, newNotFoundError(errors.New(msg))
}
schemaFile, err := r.schemaPathFunc(r.pathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict)
if err != nil {
return schemaFile, []byte{}, nil
}
f, err := os.Open(schemaFile)
if err != nil {
if os.IsNotExist(err) {
msg := fmt.Sprintf("could not open file %s", schemaFile)
if r.debug {
log.Print(msg)
}
return schemaFile, nil, newNotFoundError(errors.New(msg))
}
msg := fmt.Sprintf("failed to open schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return schemaFile, nil, errors.New(msg)
}
defer f.Close()
content, err := io.ReadAll(f)
if err != nil {
msg := fmt.Sprintf("failed to read schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return schemaFile, nil, err
}
if r.debug {
log.Printf("using schema found at %s", schemaFile)
}
return schemaFile, content, nil
msg := fmt.Sprintf("failed to open schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return schemaFile, nil, errors.New(msg)
}
defer f.Close()
content, err := io.ReadAll(f)
if err != nil {
msg := fmt.Sprintf("failed to read schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return schemaFile, nil, err
}
if r.debug {
log.Printf("using schema found at %s", schemaFile)
}
return schemaFile, content, nil
}

109
pkg/registry/local_test.go Normal file
View file

@ -0,0 +1,109 @@
package registry
import (
"fmt"
"testing"
"os"
)
// Test generated using Keploy
func TestDownloadSchema_SchemaPathError(t *testing.T) {
// Arrange
pathTemplate := "./schemas/%s/%s/%s.json"
strict := false
debug := false
registry, err := newLocalRegistry(pathTemplate, strict, debug)
if err != nil {
t.Fatalf("Failed to create LocalRegistry: %v", err)
}
// Mock schemaPathFunc to return an error
registry.schemaPathFunc = func(pathTemplate, resourceKind, resourceAPIVersion, k8sVersion string, strict bool) (string, error) {
return "", fmt.Errorf("mock schemaPath error")
}
// Act
filePath, content, err := registry.DownloadSchema("Pod", "v1", "1.21")
// Assert
if err != nil {
t.Errorf("Expected nil error, got %v", err)
}
if filePath != "" {
t.Errorf("Expected empty filePath, got %s", filePath)
}
if len(content) != 0 {
t.Errorf("Expected empty content, got %v", content)
}
}
// Test generated using Keploy
func TestDownloadSchema_Success(t *testing.T) {
// Arrange
pathTemplate := "./schemas/%s/%s/%s.json"
strict := false
debug := true
registry, err := newLocalRegistry(pathTemplate, strict, debug)
if err != nil {
t.Fatalf("Failed to create LocalRegistry: %v", err)
}
// Mock schemaPathFunc to return a valid file path
registry.schemaPathFunc = func(pathTemplate, resourceKind, resourceAPIVersion, k8sVersion string, strict bool) (string, error) {
return "./valid_schema.json", nil
}
// Create a valid schema file
validFile := "./valid_schema.json"
expectedContent := []byte(`{"type": "object"}`)
os.WriteFile(validFile, expectedContent, 0644)
defer os.Remove(validFile)
// Act
filePath, content, err := registry.DownloadSchema("Pod", "v1", "1.21")
// Assert
if err != nil {
t.Errorf("Expected nil error, got %v", err)
}
if filePath != validFile {
t.Errorf("Expected filePath '%s', got %s", validFile, filePath)
}
if string(content) != string(expectedContent) {
t.Errorf("Expected content '%s', got %s", string(expectedContent), string(content))
}
}
// Test generated using Keploy
func TestDownloadSchema_FileNotFound(t *testing.T) {
// Arrange
pathTemplate := "./schemas/%s/%s/%s.json"
strict := false
debug := true
registry, err := newLocalRegistry(pathTemplate, strict, debug)
if err != nil {
t.Fatalf("Failed to create LocalRegistry: %v", err)
}
// Mock schemaPathFunc to return a valid file path
registry.schemaPathFunc = func(pathTemplate, resourceKind, resourceAPIVersion, k8sVersion string, strict bool) (string, error) {
return "./nonexistent_file.json", nil
}
// Act
filePath, content, err := registry.DownloadSchema("Pod", "v1", "1.21")
// Assert
if err == nil || err.Error() != "could not open file ./nonexistent_file.json" {
t.Errorf("Expected NotFoundError, got %v", err)
}
if filePath != "./nonexistent_file.json" {
t.Errorf("Expected filePath './nonexistent_file.json', got %s", filePath)
}
if content != nil {
t.Errorf("Expected nil content, got %v", content)
}
}

View file

@ -1,7 +1,10 @@
package resource
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"testing"
@ -202,3 +205,106 @@ lorem: ipsum
}
}
}
// Test generated using Keploy
func TestFindFilesInFolders_BasicFunctionality(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tempDir := t.TempDir()
os.WriteFile(filepath.Join(tempDir, "file1.yaml"), []byte{}, 0644)
os.WriteFile(filepath.Join(tempDir, "file2.json"), []byte{}, 0644)
os.WriteFile(filepath.Join(tempDir, "ignored.txt"), []byte{}, 0644)
ignorePatterns := []string{".*ignored.*"}
files, errors := findFilesInFolders(ctx, []string{tempDir}, ignorePatterns)
receivedFiles := []string{}
for f := range files {
receivedFiles = append(receivedFiles, f)
}
if len(receivedFiles) != 2 {
t.Errorf("expected 2 files, got %d: %v", len(receivedFiles), receivedFiles)
}
select {
case err := <-errors:
t.Errorf("unexpected error: %v", err)
default:
}
}
// Test generated using Keploy
func TestFindResourcesInFile_ErrorHandling(t *testing.T) {
resources := make(chan Resource)
errors := make(chan error)
buf := make([]byte, 4*1024*1024)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for err := range errors {
if err == nil {
t.Errorf("expected an error, got nil")
}
}
}()
findResourcesInFile("nonexistent.yaml", resources, errors, buf)
close(resources)
close(errors)
wg.Wait()
}
// Test generated using Keploy
func TestDiscoveryError_ErrorMethod(t *testing.T) {
underlyingErr := "file not found"
de := DiscoveryError{
Path: "/path/to/file.yaml",
Err: fmt.Errorf(underlyingErr),
}
if de.Error() != underlyingErr {
t.Errorf("expected error message '%s', got '%s'", underlyingErr, de.Error())
}
}
// Test generated using Keploy
func TestIsIgnored_MultiplePatterns(t *testing.T) {
for i, testCase := range []struct {
path string
ignorePatterns []string
expectedIgnored bool
expectedError bool
}{
{
path: "/path/to/ignored/file.yaml",
ignorePatterns: []string{".*ignored.*", ".*file.*"},
expectedIgnored: true,
expectedError: false,
},
{
path: "/path/to/not_ignored/file.yaml",
ignorePatterns: []string{".*ignored.*", ".*not.*"},
expectedIgnored: true,
expectedError: false,
},
{
path: "/path/to/file.yaml",
ignorePatterns: []string{"[invalid_regex"},
expectedIgnored: false,
expectedError: true,
},
} {
ignored, err := isIgnored(testCase.path, testCase.ignorePatterns)
if ignored != testCase.expectedIgnored {
t.Errorf("test %d: expected ignored=%t, got %t", i+1, testCase.expectedIgnored, ignored)
}
if (err != nil) != testCase.expectedError {
t.Errorf("test %d: expected error=%t, got %v", i+1, testCase.expectedError, err)
}
}
}

View file

@ -1,14 +1,15 @@
package validator
import (
"bytes"
"io"
"reflect"
"testing"
"bytes"
"io"
"reflect"
"testing"
"github.com/yannh/kubeconform/pkg/registry"
"github.com/yannh/kubeconform/pkg/registry"
"github.com/yannh/kubeconform/pkg/resource"
"github.com/yannh/kubeconform/pkg/resource"
"github.com/yannh/kubeconform/pkg/cache"
)
type mockRegistry struct {
@ -532,3 +533,131 @@ firstName: foo
t.Errorf("Expected %+v, got %+v", expectedValidationErrors, gotValidationErrors)
}
}
// Test generated using Keploy
func TestValidateResource_EmptyResource(t *testing.T) {
val := v{
opts: Opts{
SkipKinds: map[string]struct{}{},
RejectKinds: map[string]struct{}{},
},
schemaCache: nil,
schemaDownload: downloadSchema,
regs: []registry.Registry{},
}
res := resource.Resource{Bytes: []byte{}}
result := val.ValidateResource(res)
if result.Status != Empty {
t.Errorf("Expected status %v, got %v", Empty, result.Status)
}
}
// Test generated using Keploy
func TestValidateResource_ProhibitedKind(t *testing.T) {
val := v{
opts: Opts{
SkipKinds: map[string]struct{}{},
RejectKinds: map[string]struct{}{"ProhibitedKind": {}},
},
schemaCache: nil,
schemaDownload: downloadSchema,
regs: []registry.Registry{},
}
rawResource := []byte(`
kind: ProhibitedKind
apiVersion: v1
firstName: foo
lastName: bar
`)
res := resource.Resource{Bytes: rawResource}
result := val.ValidateResource(res)
if result.Status != Error {
t.Errorf("Expected status %v, got %v", Error, result.Status)
}
if result.Err == nil || result.Err.Error() != "prohibited resource kind ProhibitedKind" {
t.Errorf("Expected error 'prohibited resource kind ProhibitedKind', got %v", result.Err)
}
}
// Test generated using Keploy
func TestValidateResource_RegistrySchema(t *testing.T) {
schema := []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
},
"required": ["firstName", "lastName"]
}`)
val := v{
opts: Opts{
SkipKinds: map[string]struct{}{},
RejectKinds: map[string]struct{}{},
},
schemaCache: cache.NewInMemoryCache(),
schemaDownload: downloadSchema,
regs: []registry.Registry{
newMockRegistry(func() (string, []byte, error) {
return "", schema, nil
}),
},
}
rawResource := []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`)
res := resource.Resource{Bytes: rawResource}
result := val.ValidateResource(res)
if result.Status != Valid {
t.Errorf("Expected status %v, got %v", Valid, result.Status)
}
}
// Test generated using Keploy
func TestValidateResource_SkipKinds(t *testing.T) {
val := v{
opts: Opts{
SkipKinds: map[string]struct{}{
"SkippedKind": {},
},
RejectKinds: map[string]struct{}{},
},
schemaCache: nil,
schemaDownload: downloadSchema,
regs: []registry.Registry{},
}
rawResource := []byte(`
kind: SkippedKind
apiVersion: v1
firstName: foo
lastName: bar
`)
res := resource.Resource{Bytes: rawResource}
result := val.ValidateResource(res)
if result.Status != Skipped {
t.Errorf("Expected status %v, got %v", Skipped, result.Status)
}
}