Repository: datreeio/datree
Pull request #604 (2022-05-04 13:02:48)

feat: datree validate yaml command


Patch:
diff --git a/bl/evaluation/printer.go b/bl/evaluation/printer.go
index f05a2d1..a3c2be0 100644
--- a/bl/evaluation/printer.go
+++ b/bl/evaluation/printer.go
@@ -171,7 +171,7 @@ func parseInvalidYamlFilesToWarnings(invalidYamlFiles []*extractor.InvalidFile)
 
 	for _, invalidFile := range invalidYamlFiles {
 		warnings = append(warnings, printer.Warning{
-			Title:       fmt.Sprintf(">>  File: %s\n", invalidFile.Path),
+			Title:       invalidFile.Path,
 			FailedRules: []printer.FailedRule{},
 			InvalidYamlInfo: printer.InvalidYamlInfo{
 				ValidationErrors: invalidFile.ValidationErrors,
@@ -187,7 +187,7 @@ func parseInvalidK8sFilesToWarnings(invalidK8sFiles []*extractor.InvalidFile, k8
 
 	for _, invalidFile := range invalidK8sFiles {
 		warnings = append(warnings, printer.Warning{
-			Title:       fmt.Sprintf(">>  File: %s\n", invalidFile.Path),
+			Title:       invalidFile.Path,
 			FailedRules: []printer.FailedRule{},
 			InvalidK8sInfo: printer.InvalidK8sInfo{
 				ValidationErrors: invalidFile.ValidationErrors,
@@ -277,7 +277,7 @@ func parseToPrinterWarnings(results *EvaluationResults, invalidYamlFiles []*extr
 			relativePath, _ := filepath.Rel(pwd, filename)
 
 			warnings = append(warnings, printer.Warning{
-				Title:           fmt.Sprintf(">>  File: %s\n", relativePath),
+				Title:           relativePath,
 				FailedRules:     failedRules,
 				SkippedRules:    skippedRules,
 				InvalidYamlInfo: printer.InvalidYamlInfo{},
diff --git a/bl/files/files.go b/bl/files/files.go
index c2fb73c..be046a0 100644
--- a/bl/files/files.go
+++ b/bl/files/files.go
@@ -93,3 +93,7 @@ func (f *FilesExtractor) ExtractYamlFileToUnknownStruct(path string) (UnknownStr
 
 	return policies, nil
 }
+
+func (f *FilesExtractor) ExtractConfigurationsFromYamlFile(path string) (*[]extractor.Configuration, string, *extractor.InvalidFile) {
+	return extractor.ExtractConfigurationsFromYamlFile(path)
+}
diff --git a/cmd/root.go b/cmd/root.go
index a51e631..eec0167 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -11,8 +11,9 @@ import (
 	"github.com/datreeio/datree/cmd/config"
 	"github.com/datreeio/datree/cmd/kustomize"
 	"github.com/datreeio/datree/cmd/publish"
-	schema_validator "github.com/datreeio/datree/cmd/schema-validator"
+	schemaValidator "github.com/datreeio/datree/cmd/schema-validator"
 	"github.com/datreeio/datree/cmd/test"
+	validateYaml "github.com/datreeio/datree/cmd/validate-yaml"
 	"github.com/datreeio/datree/cmd/version"
 	"github.com/datreeio/datree/pkg/ciContext"
 	"github.com/datreeio/datree/pkg/cliClient"
@@ -86,11 +87,16 @@ func NewRootCommand(app *App) *cobra.Command {
 
 	rootCmd.AddCommand(completion.New())
 
-	rootCmd.AddCommand(schema_validator.New(&schema_validator.JSONSchemaValidatorCommandContext{
+	rootCmd.AddCommand(schemaValidator.New(&schemaValidator.JSONSchemaValidatorCommandContext{
 		JSONSchemaValidator: app.Context.JSONSchemaValidator,
 		Printer:             app.Context.Printer,
 	}))
 
+	rootCmd.AddCommand(validateYaml.New(&validateYaml.ValidateYamlCommandContext{
+		Printer:   app.Context.Printer,
+		Reader:    app.Context.Reader,
+		Extractor: app.Context.FilesExtractor,
+	}))
 	return rootCmd
 }
 
diff --git a/cmd/validate-yaml/main.go b/cmd/validate-yaml/main.go
new file mode 100644
index 0000000..0db865c
--- /dev/null
+++ b/cmd/validate-yaml/main.go
@@ -0,0 +1,99 @@
+package validate_yaml
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	pkgExtractor "github.com/datreeio/datree/pkg/extractor"
+	"github.com/datreeio/datree/pkg/utils"
+	"github.com/datreeio/datree/pkg/yamlValidator"
+	"github.com/spf13/cobra"
+)
+
+type IReader interface {
+	FilterFiles(paths []string) ([]string, error)
+}
+
+type IPrinter interface {
+	PrintFilename(title string)
+	PrintYamlValidationErrors(validationErrors []error)
+	PrintYamlValidationSummary(passedFiles int, allFiles int)
+	PrintMessage(messageText string, messageColor string)
+}
+
+type ValidateYamlCommandContext struct {
+	Printer   IPrinter
+	Reader    IReader
+	Extractor yamlValidator.IExtractor
+}
+
+var YamlNotValidError = errors.New("")
+
+func SetSilentMode(cmd *cobra.Command) {
+	cmd.SilenceUsage = true
+	cmd.SilenceErrors = true
+}
+
+func New(ctx *ValidateYamlCommandContext) *cobra.Command {
+	return &cobra.Command{
+		Use:   "validate-yaml <files>",
+		Short: "validates yaml files structure",
+		Long:  "Validates yaml files <files> structure.",
+		Example: utils.Example(`
+		# Validate yaml files using file path
+		datree validate-yaml kube-prod/deployment.yaml
+
+		# Validate yaml files using glob pattern
+		datree validate-yaml kube-*/*.*
+		`),
+		Hidden: true,
+		Args: func(cmd *cobra.Command, args []string) error {
+			return nil
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			SetSilentMode(cmd)
+			var err error = nil
+
+			defer func() {
+				if err != nil {
+					ctx.Printer.PrintMessage(strings.Join([]string{"\n", err.Error(), "\n"}, ""), "error")
+				}
+			}()
+
+			filesPaths, err := ctx.Reader.FilterFiles(args)
+			if err != nil {
+				return err
+			}
+
+			filesCount := len(filesPaths)
+			if filesCount == 0 {
+				err = fmt.Errorf("No files detected")
+				return err
+			}
+
+			newYamlValidator := yamlValidator.New(&yamlValidator.YamlValidatorOptions{
+				Extractor: ctx.Extractor,
+			})
+
+			invalidYamlFiles := newYamlValidator.ValidateFiles(filesPaths)
+			PrintValidationResults(ctx.Printer, invalidYamlFiles, filesCount)
+
+			if len(invalidYamlFiles) > 0 {
+				return YamlNotValidError
+			}
+
+			return nil
+		},
+	}
+}
+
+func PrintValidationResults(printer IPrinter, invalidFiles []*pkgExtractor.InvalidFile, filesCount int) {
+	for _, invalidFile := range invalidFiles {
+		printer.PrintFilename(invalidFile.Path)
+		printer.PrintYamlValidationErrors(invalidFile.ValidationErrors)
+	}
+
+	validFilesCount := filesCount - len(invalidFiles)
+	printer.PrintYamlValidationSummary(validFilesCount, filesCount)
+}
diff --git a/main.go b/main.go
index f75bdc6..e6e5384 100644
--- a/main.go
+++ b/main.go
@@ -26,6 +26,7 @@ import (
 	"github.com/datreeio/datree/bl/validation"
 	"github.com/datreeio/datree/cmd"
 	"github.com/datreeio/datree/cmd/test"
+	validateYaml "github.com/datreeio/datree/cmd/validate-yaml"
 )
 
 const DEFAULT_ERR_EXIT_CODE = 1
@@ -68,7 +69,7 @@ func main() {
 	}()
 
 	if err := cmd.Execute(); err != nil {
-		if errors.Is(err, test.ViolationsFoundError) {
+		if errors.Is(err, test.ViolationsFoundError) || errors.Is(err, validateYaml.YamlNotValidError) {
 			os.Exit(VIOLATIONS_FOUND_EXIT_CODE)
 		}
 		reporter.ReportUnexpectedError(err)
diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go
index 611de28..b3cf123 100644
--- a/pkg/printer/printer.go
+++ b/pkg/printer/printer.go
@@ -68,17 +68,21 @@ func (p *Printer) SetTheme(theme *Theme) {
 }
 
 func (p *Printer) printYamlValidationWarning(warning Warning) {
+	p.PrintYamlValidationErrors(warning.InvalidYamlInfo.ValidationErrors)
+
+	p.printInColor("[?] Kubernetes schema validation didn't run for this file\n", p.Theme.Colors.White)
+	p.printSkippedPolicyCheck()
+	fmt.Fprintln(out)
+}
+
+func (p *Printer) PrintYamlValidationErrors(yamlValidationErrors []error) {
 	p.printInColor("[X] YAML validation\n", p.Theme.Colors.White)
 	fmt.Fprintln(out)
-	for _, validationError := range warning.InvalidYamlInfo.ValidationErrors {
+	for _, validationError := range yamlValidationErrors {
 		validationError := p.Theme.Colors.RedBold.Sprint(validationError.Error())
 		fmt.Fprintf(out, "%v %v\n", p.Theme.Emoji.Error, validationError)
 	}
 	fmt.Fprintln(out)
-
-	p.printInColor("[?] Kubernetes schema validation didn't run for this file\n", p.Theme.Colors.White)
-	p.printSkippedPolicyCheck()
-	fmt.Fprintln(out)
 }
 
 func (p *Printer) printK8sValidationError(warning Warning) {
@@ -126,8 +130,7 @@ func (p *Printer) PrintYamlSchemaResults(errorsResult []jsonschema.Detailed, err
 
 func (p *Printer) PrintWarnings(warnings []Warning) {
 	for _, warning := range warnings {
-		p.printInColor(warning.Title, p.Theme.Colors.Yellow)
-		fmt.Fprintln(out)
+		p.PrintFilename(warning.Title)
 
 		if len(warning.InvalidYamlInfo.ValidationErrors) > 0 {
 			p.printYamlValidationWarning(warning)
@@ -224,18 +227,28 @@ type EvaluationSummary struct {
 	PassedPolicyCheckCount    int
 }
 
+func (p *Printer) PrintFilename(title string) {
+	p.printInColor(fmt.Sprintf(">>  File: %s\n", title), p.Theme.Colors.Yellow)
+	fmt.Fprintln(out)
+}
+
 func (p *Printer) PrintEvaluationSummary(summary EvaluationSummary, k8sVersion string) {
 	p.printInColor("(Summary)\n", p.Theme.Colors.White)
 	fmt.Fprintln(out)
 
-	fmt.Fprintf(out, "- Passing YAML validation: %v/%v\n", summary.PassedYamlValidationCount, summary.FilesCount)
-	fmt.Fprintln(out)
+	p.PrintYamlValidationSummary(summary.PassedYamlValidationCount, summary.FilesCount)
+
 	fmt.Fprintf(out, "- Passing Kubernetes (%s) schema validation: %s\n", k8sVersion, summary.K8sValidation)
 	fmt.Fprintln(out)
 	fmt.Fprintf(out, "- Passing policy check: %v/%v\n", summary.PassedPolicyCheckCount, summary.FilesCount)
 	fmt.Fprintln(out)
 }
 
+func (p *Printer) PrintYamlValidationSummary(passedFiles int, allFiles int) {
+	fmt.Fprintf(out, "- Passing YAML validation: %v/%v\n", passedFiles, allFiles)
+	fmt.Fprintln(out)
+}
+
 func (p *Printer) PrintSummaryTable(summary Summary) {
 	summaryTable := tablewriter.NewWriter(out)
 	summaryTable.SetAutoWrapText(false)
diff --git a/pkg/yamlValidator/yamlValidator.go b/pkg/yamlValidator/yamlValidator.go
new file mode 100644
index 0000000..ef4535e
--- /dev/null
+++ b/pkg/yamlValidator/yamlValidator.go
@@ -0,0 +1,42 @@
+package yamlValidator
+
+import (
+	"github.com/datreeio/datree/bl/files"
+	pkgExtractor "github.com/datreeio/datree/pkg/extractor"
+)
+
+type YamlValidatorOptions struct {
+	Extractor IExtractor
+}
+type YamlValidator struct {
+	extractor IExtractor
+}
+
+type IExtractor interface {
+	ExtractConfigurationsFromYamlFile(path string) (*[]pkgExtractor.Configuration, string, *pkgExtractor.InvalidFile)
+}
+
+func New(options *YamlValidatorOptions) *YamlValidator {
+
+	if options != nil {
+		return &YamlValidator{
+			options.Extractor,
+		}
+	}
+
+	return &YamlValidator{
+		extractor: files.New(),
+	}
+}
+
+func (yv *YamlValidator) ValidateFiles(filesPaths []string) []*pkgExtractor.InvalidFile {
+	var invalidYamlFiles []*pkgExtractor.InvalidFile
+	for _, filePath := range filesPaths {
+		_, _, invalidYamlFile := yv.extractor.ExtractConfigurationsFromYamlFile(filePath)
+		if invalidYamlFile != nil {
+			invalidYamlFiles = append(invalidYamlFiles, invalidYamlFile)
+		}
+	}
+
+	return invalidYamlFiles
+}


Test patch:
diff --git a/cmd/validate-yaml/main_test.go b/cmd/validate-yaml/main_test.go
new file mode 100644
index 0000000..4826885
--- /dev/null
+++ b/cmd/validate-yaml/main_test.go
@@ -0,0 +1,137 @@
+package validate_yaml
+
+import (
+	"bytes"
+	"errors"
+	"testing"
+
+	pkgExtractor "github.com/datreeio/datree/pkg/extractor"
+	"github.com/datreeio/datree/pkg/yamlValidator"
+	"github.com/spf13/cobra"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+type MockFileReader struct {
+	mock.Mock
+}
+
+func (fr *MockFileReader) FilterFiles(paths []string) ([]string, error) {
+	fr.Called(paths)
+	return paths, nil
+}
+
+type MockPrinter struct {
+	mock.Mock
+}
+
+func (p *MockPrinter) PrintFilename(title string) {
+	p.Called(title)
+}
+
+func (p *MockPrinter) PrintYamlValidationErrors(validationErrors []error) {
+	p.Called(validationErrors)
+}
+
+func (p *MockPrinter) PrintYamlValidationSummary(passedFiles int, allFiles int) {
+	p.Called(passedFiles, allFiles)
+}
+
+func (p *MockPrinter) PrintMessage(messageText string, messageColor string) {
+	p.Called(messageText, messageColor)
+}
+
+type MockExtractor struct {
+	mock.Mock
+}
+
+func (e *MockExtractor) ExtractConfigurationsFromYamlFile(path string) (*[]pkgExtractor.Configuration, string, *pkgExtractor.InvalidFile) {
+	args := e.Called(path)
+	var configurations *[]pkgExtractor.Configuration
+	var invalidFile *pkgExtractor.InvalidFile
+
+	if args.Get(0) == nil {
+		configurations = nil
+	} else {
+		configurations = args.Get(0).(*[]pkgExtractor.Configuration)
+	}
+
+	if args.Get(2) == nil {
+		invalidFile = nil
+	} else {
+		invalidFile = args.Get(2).(*pkgExtractor.InvalidFile)
+	}
+
+	return configurations, args.String(1), invalidFile
+}
+
+func createMocks() (*MockFileReader, *MockPrinter, *MockExtractor) {
+	mockedFileReader := &MockFileReader{}
+	mockedFileReader.On("FilterFiles", mock.Anything).Return([]string{"."}, nil)
+
+	mockedPrinter := &MockPrinter{}
+	mockedPrinter.On("PrintFilename", mock.Anything).Return()
+	mockedPrinter.On("PrintYamlValidationErrors", mock.Anything).Return()
+	mockedPrinter.On("PrintYamlValidationSummary", mock.Anything, mock.Anything).Return()
+	mockedPrinter.On("PrintMessage", mock.Anything, mock.Anything).Return()
+
+	mockedExtractor := &MockExtractor{}
+	mockedExtractor.On("ExtractConfigurationsFromYamlFile", mock.Anything).Return(nil, "", nil)
+
+	return mockedFileReader, mockedPrinter, mockedExtractor
+}
+
+func createCommand(reader IReader, printer IPrinter, extractor yamlValidator.IExtractor) *cobra.Command {
+	cmd := New(&ValidateYamlCommandContext{
+		Reader:    reader,
+		Printer:   printer,
+		Extractor: extractor,
+	})
+	return cmd
+}
+
+func TestNoFilesSelected(t *testing.T) {
+	mockedFileReader, mockedPrinter, mockedExtractor := createMocks()
+	cmd := createCommand(mockedFileReader, mockedPrinter, mockedExtractor)
+
+	cmd.SetArgs([]string{})
+	output := cmd.Execute()
+
+	assert.Equal(t, errors.New("No files detected"), output, "expected No files detected error")
+}
+
+func TestInvalidYaml(t *testing.T) {
+	mockedExtractor := &MockExtractor{}
+	mockedExtractor.On("ExtractConfigurationsFromYamlFile", mock.Anything).Return(nil, "", &pkgExtractor.InvalidFile{
+		Path: ".gitignore",
+		ValidationErrors: []error{
+			&pkgExtractor.InvalidYamlError{
+				ErrorMessage: "file content is not valid yaml",
+			},
+		},
+	})
+
+	mockedFileReader, mockedPrinter, _ := createMocks()
+	cmd := createCommand(mockedFileReader, mockedPrinter, mockedExtractor)
+
+	actual := new(bytes.Buffer)
+	cmd.SetOut(actual)
+	cmd.SetErr(actual)
+	cmd.SetArgs([]string{"."})
+	output := cmd.Execute()
+
+	assert.Equal(t, YamlNotValidError, output, "expected YamlNotValidError error")
+}
+
+func TestValidYaml(t *testing.T) {
+	mockedFileReader, mockedPrinter, mockedExtractor := createMocks()
+	cmd := createCommand(mockedFileReader, mockedPrinter, mockedExtractor)
+
+	actual := new(bytes.Buffer)
+	cmd.SetOut(actual)
+	cmd.SetErr(actual)
+	cmd.SetArgs([]string{"."})
+	output := cmd.Execute()
+
+	assert.Equal(t, nil, output, "expected nil")
+}
diff --git a/pkg/printer/printer_test.go b/pkg/printer/printer_test.go
index 83196a6..fe35ee9 100644
--- a/pkg/printer/printer_test.go
+++ b/pkg/printer/printer_test.go
@@ -12,7 +12,7 @@ func TestPrintWarnings(t *testing.T) {
 	printer := CreateNewPrinter()
 
 	warnings := []Warning{{
-		Title: "Failed with Occurrences",
+		Title: "~/.datree/k8-demo.yaml",
 		FailedRules: []FailedRule{
 			{
 				Name:               "Caption",
@@ -23,17 +23,17 @@ func TestPrintWarnings(t *testing.T) {
 		},
 	},
 		{
-			Title:           "Failed with yaml validation",
+			Title:           "/datree/datree/internal/fixtures/kube/yaml-validation-error.yaml\n",
 			FailedRules:     []FailedRule{},
 			InvalidYamlInfo: InvalidYamlInfo{ValidationErrors: []error{fmt.Errorf("yaml validation error")}},
 		},
 		{
-			Title:          "Failed with k8s validation",
+			Title:          "/datree/datree/internal/fixtures/kube/k8s-validation-error.yaml\n",
 			FailedRules:    []FailedRule{},
 			InvalidK8sInfo: InvalidK8sInfo{ValidationErrors: []error{fmt.Errorf("K8S validation error")}, K8sVersion: "1.18.0"},
 		},
 		{
-			Title:          ">>  File: /datree/datree/internal/fixtures/kube/Chart.yaml\n",
+			Title:          "/datree/datree/internal/fixtures/kube/Chart.yaml\n",
 			FailedRules:    []FailedRule{},
 			InvalidK8sInfo: InvalidK8sInfo{ValidationErrors: []error{fmt.Errorf("K8S validation error")}, K8sVersion: "1.18.0"},
 			ExtraMessages: []ExtraMessage{{Text: "Are you trying to test a raw helm file? To run Datree with Helm - check out the helm plugin README:\nhttps://github.com/datreeio/helm-datree",
@@ -50,7 +50,8 @@ func TestPrintWarnings(t *testing.T) {
 		got := out.(*bytes.Buffer).Bytes()
 
 		expected := []byte(
-			`Failed with Occurrences
+			`>>  File: ~/.datree/k8-demo.yaml
+
 [V] YAML validation
 [V] Kubernetes schema validation
 
@@ -60,7 +61,9 @@ func TestPrintWarnings(t *testing.T) {
     — metadata.name: yishay (kind: Pod)
 💡  Suggestion
 
-Failed with yaml validation
+>>  File: /datree/datree/internal/fixtures/kube/yaml-validation-error.yaml
+
+
 [X] YAML validation
 
 ❌  yaml validation error
@@ -68,7 +71,9 @@ Failed with yaml validation
 [?] Kubernetes schema validation didn't run for this file
 [?] Policy check didn't run for this file
 
-Failed with k8s validation
+>>  File: /datree/datree/internal/fixtures/kube/k8s-validation-error.yaml
+
+
 [V] YAML validation
 [X] Kubernetes schema validation
 
@@ -78,6 +83,7 @@ Failed with k8s validation
 
 >>  File: /datree/datree/internal/fixtures/kube/Chart.yaml
 
+
 [V] YAML validation
 [X] Kubernetes schema validation
 
@@ -102,7 +108,8 @@ https://github.com/datreeio/helm-datree
 		got := out.(*bytes.Buffer).Bytes()
 
 		expected := []byte(
-			`Failed with Occurrences
+			`>>  File: ~/.datree/k8-demo.yaml
+
 [V] YAML validation
 [V] Kubernetes schema validation
 
@@ -112,7 +119,9 @@ https://github.com/datreeio/helm-datree
     — metadata.name: yishay (kind: Pod)
 [*]  Suggestion
 
-Failed with yaml validation
+>>  File: /datree/datree/internal/fixtures/kube/yaml-validation-error.yaml
+
+
 [X] YAML validation
 
 [X]  yaml validation error
@@ -120,7 +129,9 @@ Failed with yaml validation
 [?] Kubernetes schema validation didn't run for this file
 [?] Policy check didn't run for this file
 
-Failed with k8s validation
+>>  File: /datree/datree/internal/fixtures/kube/k8s-validation-error.yaml
+
+
 [V] YAML validation
 [X] Kubernetes schema validation
 
@@ -130,6 +141,7 @@ Failed with k8s validation
 
 >>  File: /datree/datree/internal/fixtures/kube/Chart.yaml
 
+
 [V] YAML validation
 [X] Kubernetes schema validation
 
diff --git a/pkg/yamlValidator/yamlValidator_test.go b/pkg/yamlValidator/yamlValidator_test.go
new file mode 100644
index 0000000..7b23c27
--- /dev/null
+++ b/pkg/yamlValidator/yamlValidator_test.go
@@ -0,0 +1,55 @@
+package yamlValidator
+
+import (
+	"testing"
+
+	pkgExtractor "github.com/datreeio/datree/pkg/extractor"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+type MockExtractor struct {
+	mock.Mock
+}
+
+func (e *MockExtractor) ExtractConfigurationsFromYamlFile(path string) (*[]pkgExtractor.Configuration, string, *pkgExtractor.InvalidFile) {
+	args := e.Called(path)
+	var configurations *[]pkgExtractor.Configuration
+	var invalidFile *pkgExtractor.InvalidFile
+
+	if args.Get(0) == nil {
+		configurations = nil
+	} else {
+		configurations = args.Get(0).(*[]pkgExtractor.Configuration)
+	}
+
+	if args.Get(2) == nil {
+		invalidFile = nil
+	} else {
+		invalidFile = args.Get(2).(*pkgExtractor.InvalidFile)
+	}
+
+	return configurations, args.String(1), invalidFile
+}
+
+func TestValidateFiles(t *testing.T) {
+	extractor := &MockExtractor{}
+
+	var yamlValidator = New(&YamlValidatorOptions{
+		Extractor: extractor,
+	})
+
+	t.Run("invalid yaml files", func(t *testing.T) {
+		invalidFilePath := "invalid.not-yaml"
+		extractor.On("ExtractConfigurationsFromYamlFile", mock.Anything).Once().Return(nil, "", &pkgExtractor.InvalidFile{Path: invalidFilePath})
+		invalidFiles := yamlValidator.ValidateFiles([]string{invalidFilePath})
+		assert.Equal(t, invalidFilePath, invalidFiles[0].Path, "invalid file path should be returned")
+	})
+
+	t.Run("valid yaml files", func(t *testing.T) {
+		validFilePath := "valid.yaml"
+		extractor.On("ExtractConfigurationsFromYamlFile", mock.Anything).Once().Return(nil, "", nil)
+		invalidFiles := yamlValidator.ValidateFiles([]string{validFilePath})
+		assert.Equal(t, 0, len(invalidFiles), "invalid files should be empty")
+	})
+}