Repository: datreeio/datree
Pull request #58 (2021-06-03 09:48:00)

staging to main


Patch:
diff --git a/bl/evaluation/evaluator.go b/bl/evaluation/evaluator.go
index d9eb96a..24ce8a7 100644
--- a/bl/evaluation/evaluator.go
+++ b/bl/evaluation/evaluator.go
@@ -9,13 +9,13 @@ import (
 type CLIClient interface {
 	RequestEvaluation(request *cliClient.EvaluationRequest) (*cliClient.EvaluationResponse, error)
 	CreateEvaluation(request *cliClient.CreateEvaluationRequest) (*cliClient.CreateEvaluationResponse, error)
-	UpdateEvaluationValidation(request *cliClient.UpdateEvaluationValidationRequest) error
+	SendFailedYamlValidation(request *cliClient.UpdateEvaluationValidationRequest) error
+	SendFailedK8sValidation(request *cliClient.UpdateEvaluationValidationRequest) error
 }
 
 type Evaluator struct {
-	cliClient             CLIClient
-	osInfo                *OSInfo
-	extractConfigurations func(path string) (*extractor.FileConfiguration, *extractor.Error)
+	cliClient CLIClient
+	osInfo    *OSInfo
 }
 
 func New(c CLIClient) *Evaluator {
@@ -35,11 +35,6 @@ type EvaluationResults struct {
 	}
 }
 
-type Error struct {
-	Message  string
-	Filename string
-}
-
 func (e *Evaluator) CreateEvaluation(cliId string, cliVersion string, k8sVersion string) (*cliClient.CreateEvaluationResponse, error) {
 	createEvaluationResponse, err := e.cliClient.CreateEvaluation(&cliClient.CreateEvaluationRequest{
 		K8sVersion: &k8sVersion,
@@ -55,83 +50,48 @@ func (e *Evaluator) CreateEvaluation(cliId string, cliVersion string, k8sVersion
 	return createEvaluationResponse, err
 }
 
-func (e *Evaluator) Evaluate(validFilesPathsChan chan string, invalidFilesPathsChan chan *validation.InvalidFile, evaluationId int) (*EvaluationResults, []*validation.InvalidFile, []*extractor.FileConfiguration, []*Error, error) {
-	filesConfigurations, invalidFiles, extractionErrors := e.extractFilesConfigurations(validFilesPathsChan, invalidFilesPathsChan)
-
+func (e *Evaluator) UpdateFailedYamlValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
 	invalidFilesPaths := []*string{}
 	for _, file := range invalidFiles {
 		invalidFilesPaths = append(invalidFilesPaths, &file.Path)
 	}
+	err := e.cliClient.SendFailedYamlValidation(&cliClient.UpdateEvaluationValidationRequest{
+		EvaluationId:   evaluationId,
+		InvalidFiles:   invalidFilesPaths,
+		StopEvaluation: stopEvaluation,
+	})
+	return err
+}
 
-	if len(invalidFiles) > 0 {
-		stopEvaluation := len(filesConfigurations) == 0 // NOTICE: validFilesPathsChan surely closed and empty
-		err := e.cliClient.UpdateEvaluationValidation(&cliClient.UpdateEvaluationValidationRequest{
-			EvaluationId:   evaluationId,
-			InvalidFiles:   invalidFilesPaths,
-			StopEvaluation: stopEvaluation,
-		})
-
-		if stopEvaluation {
-			return nil, invalidFiles, filesConfigurations, extractionErrors, err
-		}
+func (e *Evaluator) UpdateFailedK8sValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
+	invalidFilesPaths := []*string{}
+	for _, file := range invalidFiles {
+		invalidFilesPaths = append(invalidFilesPaths, &file.Path)
 	}
+	err := e.cliClient.SendFailedK8sValidation(&cliClient.UpdateEvaluationValidationRequest{
+		EvaluationId:   evaluationId,
+		InvalidFiles:   invalidFilesPaths,
+		StopEvaluation: stopEvaluation,
+	})
+	return err
+}
 
-	if len(filesConfigurations) > 0 {
-		res, err := e.cliClient.RequestEvaluation(&cliClient.EvaluationRequest{
-			EvaluationId: evaluationId,
-			Files:        filesConfigurations,
-		})
-		if err != nil {
-			return nil, invalidFiles, filesConfigurations, extractionErrors, err
-		}
+func (e *Evaluator) Evaluate(filesConfigurations []*extractor.FileConfigurations, evaluationId int) (*EvaluationResults, error) {
 
-		results := e.formatEvaluationResults(res.Results, len(filesConfigurations))
-		return results, invalidFiles, filesConfigurations, extractionErrors, nil
+	if len(filesConfigurations) == 0 {
+		return &EvaluationResults{}, nil
 	}
 
-	return nil, invalidFiles, filesConfigurations, extractionErrors, nil
-}
-
-func (e *Evaluator) extractFilesConfigurations(validFilesPathsChan chan string, invalidFilesPathsChan chan *validation.InvalidFile) ([]*extractor.FileConfiguration, []*validation.InvalidFile, []*Error) {
-	invalidFiles := []*validation.InvalidFile{}
-	var files []*extractor.FileConfiguration
-	var errors []*Error
-
-	readFromValidDone := false
-	readFromInvalidDone := false
-	for {
-		select {
-		case path, ok := <-validFilesPathsChan:
-			if !ok {
-				readFromValidDone = true
-			} else {
-				file, err := extractor.ExtractConfiguration(path)
-				if file != nil {
-					files = append(files, &extractor.FileConfiguration{
-						FileName:       file.FileName,
-						Configurations: file.Configurations,
-					})
-				}
-				if err != nil {
-					errors = append(errors, &Error{
-						Message:  err.Message,
-						Filename: err.Filename,
-					})
-				}
-			}
-		case file, ok := <-invalidFilesPathsChan:
-			if !ok {
-				readFromInvalidDone = true
-			} else {
-				invalidFiles = append(invalidFiles, file)
-			}
-		}
-		if readFromValidDone && readFromInvalidDone {
-			break
-		}
+	res, err := e.cliClient.RequestEvaluation(&cliClient.EvaluationRequest{
+		EvaluationId: evaluationId,
+		Files:        filesConfigurations,
+	})
+	if err != nil {
+		return nil, err
 	}
 
-	return files, invalidFiles, errors
+	results := e.formatEvaluationResults(res.Results, len(filesConfigurations))
+	return results, nil
 }
 
 func (e *Evaluator) formatEvaluationResults(evaluationResults []*cliClient.EvaluationResult, filesCount int) *EvaluationResults {
diff --git a/bl/evaluation/printer.go b/bl/evaluation/printer.go
index 860f562..11b493d 100644
--- a/bl/evaluation/printer.go
+++ b/bl/evaluation/printer.go
@@ -88,6 +88,7 @@ func parseToPrinterWarnings(results *EvaluationResults, invalidFiles []*validati
 			Details: []printer.WarningInfo{},
 			ValidationInfo: printer.ValidationInfo{
 				IsValid:          false,
+				ValidationStatus: invalidFile.ValidationStatus,
 				ValidationErrors: invalidFile.ValidationErrors,
 				K8sVersion:       k8sVersion,
 			},
diff --git a/bl/files/files.go b/bl/files/files.go
index 056c86f..822e572 100644
--- a/bl/files/files.go
+++ b/bl/files/files.go
@@ -4,9 +4,12 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+
+	"github.com/datreeio/datree/bl/validation"
+	"github.com/datreeio/datree/pkg/extractor"
 )
 
-func toAbsolutePath(path string) (string, error) {
+func ToAbsolutePath(path string) (string, error) {
 	absolutePath, err := filepath.Abs(path)
 	if err != nil {
 		return "", err
@@ -20,18 +23,39 @@ func toAbsolutePath(path string) (string, error) {
 	return "", fmt.Errorf("failed parsing absolute path %s", path)
 }
 
-func ToAbsolutePaths(paths []string) chan string {
-	pathsChan := make(chan string)
+func ExtractFilesConfigurations(paths []string, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidFile) {
+	filesConfigurationsChan := make(chan *extractor.FileConfigurations, concurrency)
+	invalidFilesChan := make(chan *validation.InvalidFile, concurrency)
 
 	go func() {
-		for _, p := range paths {
-			absolutePath, err := toAbsolutePath(p)
-			if err == nil {
-				pathsChan <- absolutePath
+		defer func() {
+			close(filesConfigurationsChan)
+			close(invalidFilesChan)
+		}()
+
+		for _, path := range paths {
+
+			absolutePath, err := ToAbsolutePath(path)
+			if err != nil {
+				invalidFilesChan <- &validation.InvalidFile{Path: path, ValidationStatus: validation.InvalidYamlFile, ValidationErrors: []error{err}}
+				continue
+			}
+
+			content, err := extractor.ReadFileContent(absolutePath)
+			if err != nil {
+				invalidFilesChan <- &validation.InvalidFile{Path: absolutePath, ValidationStatus: validation.InvalidYamlFile, ValidationErrors: []error{err}}
+				continue
+			}
+
+			configurations, err := extractor.ParseYaml(content)
+			if err != nil {
+				invalidFilesChan <- &validation.InvalidFile{Path: absolutePath, ValidationStatus: validation.InvalidYamlFile, ValidationErrors: []error{err}}
+				continue
 			}
+
+			filesConfigurationsChan <- &extractor.FileConfigurations{FileName: absolutePath, Configurations: *configurations}
 		}
-		close(pathsChan)
 	}()
 
-	return pathsChan
+	return filesConfigurationsChan, invalidFilesChan
 }
diff --git a/bl/validation/k8sValidator.go b/bl/validation/k8sValidator.go
index 8a01a04..deafaa9 100644
--- a/bl/validation/k8sValidator.go
+++ b/bl/validation/k8sValidator.go
@@ -5,7 +5,7 @@ import (
 	"io"
 	"os"
 
-	"github.com/datreeio/datree/bl/files"
+	"github.com/datreeio/datree/pkg/extractor"
 	kubeconformValidator "github.com/yannh/kubeconform/pkg/validator"
 )
 
@@ -21,8 +21,20 @@ func New() *K8sValidator {
 	return &K8sValidator{}
 }
 
+type ValidationStatus int
+
+const (
+	InvalidYamlFile ValidationStatus = iota + 1
+	InvalidK8sFile
+)
+
+func (validationStatus ValidationStatus) String() string {
+	return [...]string{"InvalidYamlFile", "InvalidK8sFile"}[validationStatus]
+}
+
 type InvalidFile struct {
 	Path             string
+	ValidationStatus ValidationStatus
 	ValidationErrors []error
 }
 
@@ -30,35 +42,39 @@ func (val *K8sValidator) InitClient(k8sVersion string) {
 	val.validationClient = newKubconformValidator(k8sVersion)
 }
 
-func (val *K8sValidator) ValidateResources(paths []string) (chan string, chan *InvalidFile, chan error) {
-	pathsChan := files.ToAbsolutePaths(paths)
-
-	errorChan := make(chan error)
-	validFilesPathChan := make(chan string)
-	invalidFilesPathsChan := make(chan *InvalidFile)
+func (val *K8sValidator) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *InvalidFile) {
+	validK8sFilesConfigurationsChan := make(chan *extractor.FileConfigurations, concurrency)
+	invalidK8sFilesChan := make(chan *InvalidFile, concurrency)
 
 	go func() {
-		for path := range pathsChan {
-			isValid, validationErrors, err := val.validateResource(path)
+		defer func() {
+			close(invalidK8sFilesChan)
+			close(validK8sFilesConfigurationsChan)
+		}()
+
+		for fileConfigurations := range filesConfigurationsChan {
+
+			isValid, validationErrors, err := val.validateResource(fileConfigurations.FileName)
+			if err != nil {
+				invalidK8sFilesChan <- &InvalidFile{
+					Path:             fileConfigurations.FileName,
+					ValidationStatus: InvalidK8sFile,
+					ValidationErrors: []error{err},
+				}
+				continue
+			}
 			if isValid {
-				validFilesPathChan <- path
+				validK8sFilesConfigurationsChan <- fileConfigurations
 			} else {
-				invalidFilesPathsChan <- &InvalidFile{
-					Path:             path,
+				invalidK8sFilesChan <- &InvalidFile{
+					Path:             fileConfigurations.FileName,
+					ValidationStatus: InvalidK8sFile,
 					ValidationErrors: validationErrors,
 				}
 			}
-			if err != nil {
-				errorChan <- err
-			}
 		}
-
-		close(invalidFilesPathsChan)
-		close(validFilesPathChan)
-		close(errorChan)
 	}()
-
-	return validFilesPathChan, invalidFilesPathsChan, errorChan
+	return validK8sFilesConfigurationsChan, invalidK8sFilesChan
 }
 
 func (val *K8sValidator) validateResource(filepath string) (bool, []error, error) {
diff --git a/internal/fixtures/kube/invalidK8sSchema.yaml b/internal/fixtures/kube/invalidK8sSchema.yaml
new file mode 100644
index 0000000..632a8c9
--- /dev/null
+++ b/internal/fixtures/kube/invalidK8sSchema.yaml
@@ -0,0 +1,11 @@
+apiversion: v1
+kind: Service
+metadata:
+  name: my-service
+spec:
+  selector:
+    app: MyApp
+  ports:
+    - protocol: TCP
+      port: 80
+      targetPort: 9376
diff --git a/pkg/cliClient/evaluation.go b/pkg/cliClient/evaluation.go
index 785984a..3e55344 100644
--- a/pkg/cliClient/evaluation.go
+++ b/pkg/cliClient/evaluation.go
@@ -66,8 +66,8 @@ type EvaluationResponse struct {
 }
 
 type EvaluationRequest struct {
-	EvaluationId int                            `json:"evaluationId"`
-	Files        []*extractor.FileConfiguration `json:"files"`
+	EvaluationId int                             `json:"evaluationId"`
+	Files        []*extractor.FileConfigurations `json:"files"`
 }
 
 func (c *CliClient) RequestEvaluation(request *EvaluationRequest) (*EvaluationResponse, error) {
@@ -91,7 +91,16 @@ type UpdateEvaluationValidationRequest struct {
 	StopEvaluation bool      `json:"stopEvaluation"`
 }
 
-func (c *CliClient) UpdateEvaluationValidation(request *UpdateEvaluationValidationRequest) error {
+func (c *CliClient) SendFailedYamlValidation(request *UpdateEvaluationValidationRequest) error {
+	_, err := c.httpClient.Request(http.MethodPost, "/cli/evaluation/validation/yaml", request, nil)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (c *CliClient) SendFailedK8sValidation(request *UpdateEvaluationValidationRequest) error {
 	_, err := c.httpClient.Request(http.MethodPost, "/cli/evaluation/validation/k8s", request, nil)
 	if err != nil {
 		return err
diff --git a/pkg/extractor/extractor.go b/pkg/extractor/extractor.go
index 8978587..ceebeca 100644
--- a/pkg/extractor/extractor.go
+++ b/pkg/extractor/extractor.go
@@ -12,38 +12,25 @@ type FileReader interface {
 	ReadFileContent(filepath string) (string, error)
 }
 
-type K8sConfiguration map[string]interface{}
+type Configuration map[string]interface{}
 
-type FileConfiguration struct {
-	FileName       string             `json:"fileName"`
-	Configurations []K8sConfiguration `json:"configurations"`
+type FileConfigurations struct {
+	FileName       string          `json:"fileName"`
+	Configurations []Configuration `json:"configurations"`
 }
 
-type Error struct {
-	Message  string
-	Filename string
-}
-
-func ExtractConfiguration(path string) (*FileConfiguration, *Error) {
-	content, err := readFileContent(path)
+func ParseYaml(content string) (*[]Configuration, error) {
+	configurations, err := extractYamlConfigurations(content)
 	if err != nil {
-		return nil, &Error{Filename: path, Message: err.Error()}
-	}
-
-	configurations, err := yamlToK8sConfigurations(content)
-	if err != nil {
-		return nil, &Error{Filename: path, Message: err.Error()}
+		return nil, err
 	} else {
-		f := &FileConfiguration{
-			Configurations: *configurations,
-			FileName:       path,
-		}
-		return f, nil
+
+		return configurations, nil
 	}
 }
 
-func yamlToK8sConfigurations(content string) (*[]K8sConfiguration, error) {
-	var configurations []K8sConfiguration
+func extractYamlConfigurations(content string) (*[]Configuration, error) {
+	var configurations []Configuration
 
 	yamlDecoder := yaml.NewDecoder(bytes.NewReader([]byte(content)))
 
@@ -64,7 +51,7 @@ func yamlToK8sConfigurations(content string) (*[]K8sConfiguration, error) {
 	return &configurations, err
 }
 
-func readFileContent(filepath string) (string, error) {
+func ReadFileContent(filepath string) (string, error) {
 	dat, err := ioutil.ReadFile(filepath)
 	if err != nil {
 		return "", err
diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go
index 6cab9b6..5e9c759 100644
--- a/pkg/printer/printer.go
+++ b/pkg/printer/printer.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 
+	"github.com/datreeio/datree/bl/validation"
 	"github.com/fatih/color"
 	"github.com/olekukonko/tablewriter"
 )
@@ -27,6 +28,7 @@ type WarningInfo struct {
 
 type ValidationInfo struct {
 	IsValid          bool
+	ValidationStatus validation.ValidationStatus
 	ValidationErrors []error
 	K8sVersion       string
 }
@@ -41,23 +43,38 @@ func (p *Printer) PrintWarnings(warnings []Warning) {
 		p.printInColor(warning.Title, p.theme.Colors.Yellow)
 		fmt.Println()
 
-		p.printInColor("[V] YAML validation\n", p.theme.Colors.Green)
+		if !warning.ValidationInfo.IsValid {
+			if warning.ValidationInfo.ValidationStatus == validation.InvalidYamlFile {
+				p.printInColor("[X] YAML validation\n", p.theme.Colors.White)
+				fmt.Println()
+				for _, validationError := range warning.ValidationInfo.ValidationErrors {
+					validationError := p.theme.Colors.Red.Sprint(validationError.Error())
+					fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
+				}
+				fmt.Println()
 
-		if warning.ValidationInfo.IsValid != true {
-			p.printInColor("[X] Kubernetes schema validation\n", p.theme.Colors.White)
-			fmt.Println()
+				p.printInColor("[?] Kubernetes schema validation\n", p.theme.Colors.White)
+				p.printInColor("[?] Policy check didn’t run for this file\n", p.theme.Colors.White)
 
-			for _, validationError := range warning.ValidationInfo.ValidationErrors {
-				validationError := p.theme.Colors.Red.Sprint(validationError.Error())
-				fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
-			}
-			fmt.Println()
-			p.printInColor("[?] Policy check didn’t run for this file\n", p.theme.Colors.White)
-			fmt.Println()
-			fmt.Println()
+				fmt.Println()
+
+			} else if warning.ValidationInfo.ValidationStatus == validation.InvalidK8sFile {
+				p.printInColor("[V] YAML validation\n", p.theme.Colors.Green)
+				p.printInColor("[X] Kubernetes schema validation\n", p.theme.Colors.White)
+				fmt.Println()
 
+				for _, validationError := range warning.ValidationInfo.ValidationErrors {
+					validationError := p.theme.Colors.Red.Sprint(validationError.Error())
+					fmt.Printf("%v %v\n", p.theme.Emoji.Error, validationError)
+				}
+				fmt.Println()
+				p.printInColor("[?] Policy check didn’t run for this file\n", p.theme.Colors.White)
+				fmt.Println()
+			}
 		} else {
+			p.printInColor("[V] YAML validation\n", p.theme.Colors.Green)
 			p.printInColor("[V] Kubernetes schema validation\n", p.theme.Colors.Green)
+
 			fmt.Println()
 			p.printInColor("[X] Policy check\n", p.theme.Colors.White)
 			fmt.Println()
@@ -127,11 +144,11 @@ func (p *Printer) PrintSummaryTable(summary Summary) {
 	}
 
 	errorRow := []string{summary.ErrorRow.LeftCol, summary.ErrorRow.RightCol}
-	summaryTable.Rich(errorRow, []tablewriter.Colors{tablewriter.Colors{tablewriter.FgHiRedColor}, tablewriter.Colors{tablewriter.FgHiRedColor}})
+	summaryTable.Rich(errorRow, []tablewriter.Colors{{tablewriter.FgHiRedColor}, {tablewriter.FgHiRedColor}})
 	rowIndex++
 
 	successRow := []string{summary.SuccessRow.LeftCol, summary.SuccessRow.RightCol}
-	summaryTable.Rich(successRow, []tablewriter.Colors{tablewriter.Colors{tablewriter.Normal, tablewriter.FgGreenColor}, tablewriter.Colors{tablewriter.Normal, tablewriter.FgGreenColor}})
+	summaryTable.Rich(successRow, []tablewriter.Colors{{tablewriter.Normal, tablewriter.FgGreenColor}, {tablewriter.Normal, tablewriter.FgGreenColor}})
 	rowIndex++
 
 	for plainRowsIndex < len(summary.PlainRows) && summary.PlainRows[plainRowsIndex].RowIndex >= rowIndex {


Test patch:
diff --git a/bl/evaluation/evaluator_test.go b/bl/evaluation/evaluator_test.go
index 1d7e01c..e70efa5 100644
--- a/bl/evaluation/evaluator_test.go
+++ b/bl/evaluation/evaluator_test.go
@@ -4,9 +4,8 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/datreeio/datree/bl/validation"
-
 	"github.com/datreeio/datree/pkg/cliClient"
+	"github.com/datreeio/datree/pkg/extractor"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
@@ -25,7 +24,12 @@ func (m *mockCliClient) RequestEvaluation(evaluationRequest *cliClient.Evaluatio
 	return args.Get(0).(*cliClient.EvaluationResponse), args.Error(1)
 }
 
-func (m *mockCliClient) UpdateEvaluationValidation(request *cliClient.UpdateEvaluationValidationRequest) error {
+func (m *mockCliClient) SendFailedYamlValidation(request *cliClient.UpdateEvaluationValidationRequest) error {
+	args := m.Called(request)
+	return args.Error(0)
+}
+
+func (m *mockCliClient) SendFailedK8sValidation(request *cliClient.UpdateEvaluationValidationRequest) error {
 	args := m.Called(request)
 	return args.Error(0)
 }
@@ -57,48 +61,44 @@ type evaluatorMock struct {
 	cliClient *cliClientMockTestCase
 }
 
-func TestEvaluate(t *testing.T) {
-	// TODO: add actual tests
-	tests := []*evaluateTestCase{}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			mockedCliClient := &mockCliClient{}
-
-			mockedCliClient.On("CreateEvaluation", mock.Anything).Return(tt.mock.cliClient.createEvaluation, tt.mock.cliClient.createEvaluation.err)
-			mockedCliClient.On("UpdateEvaluationValidation", mock.Anything).Return(nil)
+// TODO: add actual tests
+func TestCreateEvaluation(t *testing.T) {
+	t.Run("CreateEvaluation should succedd", func(t *testing.T) {
+		mockedCliClient := &mockCliClient{}
+		evaluator := &Evaluator{
+			cliClient: mockedCliClient,
+			osInfo: &OSInfo{
+				OS:              "darwin",
+				PlatformVersion: "1.2.3",
+				KernelVersion:   "4.5.6",
+			},
+		}
 
-			evaluator := &Evaluator{
-				cliClient: mockedCliClient,
-				osInfo:    tt.args.osInfo,
-			}
+		cliId := "test_token"
+		cliVersion := "0.0.7"
+		k8sVersion := "1.18.1"
 
-			results, _, _, _, _ := evaluator.Evaluate(tt.args.validFilesChan, tt.args.invalidFilesChan, tt.args.evaluationId)
+		mockedCliClient.On("CreateEvaluation", mock.Anything).Return(&cliClient.CreateEvaluationResponse{EvaluationId: 1, K8sVersion: k8sVersion}, nil)
 
-			if tt.expected.isRequestEvaluationCalled {
-				mockedCliClient.AssertCalled(t, "RequestEvaluation", mock.Anything)
-				assert.Equal(t, tt.expected.response.Summary, results.Summary)
-				assert.Equal(t, tt.expected.response.FileNameRuleMapper, results.FileNameRuleMapper)
-			}
+		expectedCreateEvaluationResponse := &cliClient.CreateEvaluationResponse{EvaluationId: 1, K8sVersion: k8sVersion}
+		createEvaluationResponse, _ := evaluator.CreateEvaluation(cliId, cliVersion, k8sVersion)
 
-			if tt.expected.isUpdateEvaluationValidationCalled {
-				mockedCliClient.AssertCalled(t, "UpdateEvaluationValidation", mock.Anything)
-			}
+		mockedCliClient.AssertCalled(t, "CreateEvaluation", mock.Anything)
+		assert.Equal(t, expectedCreateEvaluationResponse, createEvaluationResponse)
 
-		})
-	}
+	})
 }
 
-func TestCreateEvaluation(t *testing.T) {
+func TestEvaluate(t *testing.T) {
 	tests := []*evaluateTestCase{
-		request_evaluation_all_invalid(),
 		request_evaluation_all_valid(),
+		request_evaluation_all_invalid(),
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			mockedCliClient := &mockCliClient{}
 
 			mockedCliClient.On("RequestEvaluation", mock.Anything).Return(tt.mock.cliClient.requestEvaluation.response, tt.mock.cliClient.requestEvaluation.err)
-			mockedCliClient.On("UpdateEvaluationValidation", mock.Anything).Return(nil)
 
 			evaluator := &Evaluator{
 				cliClient: mockedCliClient,
@@ -106,37 +106,31 @@ func TestCreateEvaluation(t *testing.T) {
 			}
 
 			// TODO: define and check the rest of the values
-			results, _, _, _, _ := evaluator.Evaluate(tt.args.validFilesChan, tt.args.invalidFilesChan, tt.args.evaluationId)
+			results, _ := evaluator.Evaluate(tt.args.validFilesConfigurations, tt.args.evaluationId)
 
 			if tt.expected.isRequestEvaluationCalled {
 				mockedCliClient.AssertCalled(t, "RequestEvaluation", mock.Anything)
 				assert.Equal(t, tt.expected.response.Summary, results.Summary)
 				assert.Equal(t, tt.expected.response.FileNameRuleMapper, results.FileNameRuleMapper)
+			} else {
+				mockedCliClient.AssertNotCalled(t, "RequestEvaluation")
 			}
-
-			if tt.expected.isUpdateEvaluationValidationCalled {
-				mockedCliClient.AssertCalled(t, "UpdateEvaluationValidation", mock.Anything)
-			}
-
 		})
 	}
 }
 
 type evaluateArgs struct {
-	validFilesChan   chan string
-	invalidFilesChan chan *validation.InvalidFile
-	evaluationId     int
-	osInfo           *OSInfo
+	validFilesConfigurations []*extractor.FileConfigurations
+	evaluationId             int
+	osInfo                   *OSInfo
 }
 
 type evaluateExpected struct {
-	response                           *EvaluationResults
-	errors                             []*Error
-	err                                error
-	isRequestEvaluationCalled          bool
-	isCreateEvaluationCalled           bool
-	isUpdateEvaluationValidationCalled bool
-	isGetVersionMessageCalled          bool
+	response                  *EvaluationResults
+	err                       error
+	isRequestEvaluationCalled bool
+	isCreateEvaluationCalled  bool
+	isGetVersionMessageCalled bool
 }
 
 type evaluateTestCase struct {
@@ -147,14 +141,13 @@ type evaluateTestCase struct {
 }
 
 func request_evaluation_all_valid() *evaluateTestCase {
-	invalidPath := "path/path1/service.yaml"
+	validFilePath := "../../internal/fixtures/kube/pass-all.yaml"
 
 	return &evaluateTestCase{
 		name: "should request validation without invalid files",
 		args: &evaluateArgs{
-			validFilesChan:   newFilesChan(),
-			invalidFilesChan: newInvalidFilesChan(invalidPath),
-			evaluationId:     1,
+			validFilesConfigurations: newFilesConfigurations(validFilePath),
+			evaluationId:             1,
 			osInfo: &OSInfo{
 				OS:              "darwin",
 				PlatformVersion: "1.2.3",
@@ -207,22 +200,18 @@ func request_evaluation_all_valid() *evaluateTestCase {
 					TotalPassedCount: 1,
 				},
 			},
-			errors:                             []*Error{},
-			err:                                nil,
-			isRequestEvaluationCalled:          true,
-			isUpdateEvaluationValidationCalled: false,
+			err:                       nil,
+			isRequestEvaluationCalled: true,
 		},
 	}
 }
 
 func request_evaluation_all_invalid() *evaluateTestCase {
-	invalidPath := "path/path1/service.yaml"
 	return &evaluateTestCase{
-		name: "should request validation all files are invalid",
+		name: "should not request validation if there are no valid files",
 		args: &evaluateArgs{
-			validFilesChan:   newFilesChan(),
-			invalidFilesChan: newInvalidFilesChan(invalidPath),
-			evaluationId:     1,
+			validFilesConfigurations: []*extractor.FileConfigurations{},
+			evaluationId:             1,
 			osInfo: &OSInfo{
 				OS:              "darwin",
 				PlatformVersion: "1.2.3",
@@ -272,32 +261,20 @@ func request_evaluation_all_invalid() *evaluateTestCase {
 					RulesCount:       0,
 					TotalFailedRules: 0,
 					FilesCount:       1,
-					TotalPassedCount: 1,
+					TotalPassedCount: 0,
 				},
 			},
-			errors:                             []*Error{},
-			err:                                nil,
-			isRequestEvaluationCalled:          false,
-			isUpdateEvaluationValidationCalled: true,
+			err:                       nil,
+			isRequestEvaluationCalled: false,
 		},
 	}
 }
 
-func newFilesChan() chan string {
-	files := make(chan string, 1)
-	p, _ := filepath.Abs("../../internal/fixtures/kube/pass-all.yaml")
-	files <- p
-	close(files)
-	return files
-}
-
-func newInvalidFilesChan(invalidPath string) chan *validation.InvalidFile {
-	invalidFilesChan := make(chan *validation.InvalidFile, 1)
-
-	invalidFilesChan <- &validation.InvalidFile{
-		Path:             invalidPath,
-		ValidationErrors: []error{},
-	}
-	close(invalidFilesChan)
-	return invalidFilesChan
+func newFilesConfigurations(path string) []*extractor.FileConfigurations {
+	var filesConfigurations []*extractor.FileConfigurations
+	absolutePath, _ := filepath.Abs(path)
+	filesConfigurations = append(filesConfigurations, &extractor.FileConfigurations{
+		FileName: absolutePath,
+	})
+	return filesConfigurations
 }
diff --git a/bl/files/files_test.go b/bl/files/files_test.go
index 665920b..1247e8d 100644
--- a/bl/files/files_test.go
+++ b/bl/files/files_test.go
@@ -10,14 +10,14 @@ import (
 type toAbsolutePathsTestCase struct {
 	name string
 	args struct {
-		paths []string
+		path string
 	}
 	expected struct {
 		path string
 	}
 }
 
-func TestToAbsolutePaths(t *testing.T) {
+func TestToAbsolutePath(t *testing.T) {
 	tests := []*toAbsolutePathsTestCase{
 		test_existed_file(),
 		test_directory_file(),
@@ -25,8 +25,8 @@ func TestToAbsolutePaths(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			pathsChan := ToAbsolutePaths(tt.args.paths)
-			assert.Equal(t, tt.expected.path, <-pathsChan)
+			absolutePath, _ := ToAbsolutePath(tt.args.path)
+			assert.Equal(t, tt.expected.path, absolutePath)
 		})
 	}
 
@@ -36,8 +36,8 @@ func test_existed_file() *toAbsolutePathsTestCase {
 	p, _ := filepath.Abs("../../internal/fixtures/kube/pass-all.yaml")
 	return &toAbsolutePathsTestCase{
 		name: "existed file, should return abs path with no errors",
-		args: struct{ paths []string }{
-			paths: []string{"../../internal/fixtures/kube/pass-all.yaml"},
+		args: struct{ path string }{
+			path: "../../internal/fixtures/kube/pass-all.yaml",
 		},
 		expected: struct {
 			path string
@@ -50,8 +50,8 @@ func test_existed_file() *toAbsolutePathsTestCase {
 func test_not_existed_file() *toAbsolutePathsTestCase {
 	return &toAbsolutePathsTestCase{
 		name: "test not existed file, should return an error",
-		args: struct{ paths []string }{
-			paths: []string{"../../internal/fixtures/kube/bla.yaml"},
+		args: struct{ path string }{
+			path: "../../internal/fixtures/kube/bla.yaml",
 		},
 		expected: struct {
 			path string
@@ -63,8 +63,8 @@ func test_not_existed_file() *toAbsolutePathsTestCase {
 
 func test_directory_file() *toAbsolutePathsTestCase {
 	return &toAbsolutePathsTestCase{
-		args: struct{ paths []string }{
-			paths: []string{"../../internal/fixtures/kube"},
+		args: struct{ path string }{
+			path: "../../internal/fixtures/kube",
 		},
 		expected: struct {
 			path string
diff --git a/bl/validation/k8sValidator_test.go b/bl/validation/k8sValidator_test.go
index cf0ac93..5a80dba 100644
--- a/bl/validation/k8sValidator_test.go
+++ b/bl/validation/k8sValidator_test.go
@@ -2,9 +2,9 @@ package validation
 
 import (
 	"io"
-	"path/filepath"
 	"testing"
 
+	"github.com/datreeio/datree/pkg/extractor"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	kubeconformValidator "github.com/yannh/kubeconform/pkg/validator"
@@ -20,10 +20,11 @@ func (m *mockValidationClient) Validate(filename string, r io.ReadCloser) []kube
 }
 
 func TestValidateResources(t *testing.T) {
-	test_valid_multiple_resources(t)
+	test_valid_multiple_configurations(t)
+	test_invalid_file(t)
 }
 
-func test_valid_multiple_resources(t *testing.T) {
+func test_valid_multiple_configurations(t *testing.T) {
 	validationClient := &mockValidationClient{}
 	validationClient.On("Validate", mock.Anything, mock.Anything).Return([]kubeconformValidator.Result{
 		{Status: kubeconformValidator.Valid},
@@ -34,12 +35,39 @@ func test_valid_multiple_resources(t *testing.T) {
 
 	path := "../../internal/fixtures/kube/pass-all.yaml"
 
-	valid, _, _ := k8sValidator.ValidateResources([]string{path})
-	expectedPath, _ := filepath.Abs(path)
+	filesConfigurationsChan := make(chan *extractor.FileConfigurations, 1)
+	filesConfigurationsChan <- &extractor.FileConfigurations{
+		FileName:       path,
+		Configurations: []extractor.Configuration{},
+	}
+	close(filesConfigurationsChan)
+	validConfigurationsChan, _ := k8sValidator.ValidateResources(filesConfigurationsChan, 1)
 
-	for p := range valid {
-		assert.Equal(t, expectedPath, p)
+	for p := range validConfigurationsChan {
+		assert.Equal(t, path, p.FileName)
 	}
+}
 
-	// assert.Equal(t, nil)
+func test_invalid_file(t *testing.T) {
+	validationClient := &mockValidationClient{}
+	validationClient.On("Validate", mock.Anything, mock.Anything).Return([]kubeconformValidator.Result{
+		{Status: kubeconformValidator.Invalid},
+	})
+	k8sValidator := K8sValidator{
+		validationClient: validationClient,
+	}
+
+	path := "../../internal/fixtures/kube/invalidK8sSchema.yaml"
+
+	filesConfigurationsChan := make(chan *extractor.FileConfigurations, 1)
+	filesConfigurationsChan <- &extractor.FileConfigurations{
+		FileName:       path,
+		Configurations: []extractor.Configuration{},
+	}
+	close(filesConfigurationsChan)
+	_, invalidFilesChan := k8sValidator.ValidateResources(filesConfigurationsChan, 1)
+
+	for p := range invalidFilesChan {
+		assert.Equal(t, path, p.Path)
+	}
 }
diff --git a/cmd/test/main.go b/cmd/test/main.go
index 249356c..43212c7 100644
--- a/cmd/test/main.go
+++ b/cmd/test/main.go
@@ -2,13 +2,13 @@ package test
 
 import (
 	"fmt"
-	"time"
 
+	"github.com/briandowns/spinner"
 	"github.com/datreeio/datree/pkg/cliClient"
 	"github.com/datreeio/datree/pkg/extractor"
 
-	"github.com/briandowns/spinner"
 	"github.com/datreeio/datree/bl/evaluation"
+	"github.com/datreeio/datree/bl/files"
 	"github.com/datreeio/datree/bl/messager"
 	"github.com/datreeio/datree/bl/validation"
 	"github.com/datreeio/datree/pkg/localConfig"
@@ -17,8 +17,10 @@ import (
 )
 
 type Evaluator interface {
-	Evaluate(validFilesPathsChan chan string, invalidFilesPaths chan *validation.InvalidFile, evaluationId int) (*evaluation.EvaluationResults, []*validation.InvalidFile, []*extractor.FileConfiguration, []*evaluation.Error, error)
+	Evaluate(filesConfigurationsChan []*extractor.FileConfigurations, evaluationId int) (*evaluation.EvaluationResults, error)
 	CreateEvaluation(cliId string, cliVersion string, k8sVersion string) (*cliClient.CreateEvaluationResponse, error)
+	UpdateFailedYamlValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error
+	UpdateFailedK8sValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error
 }
 
 type Messager interface {
@@ -26,7 +28,7 @@ type Messager interface {
 }
 
 type K8sValidator interface {
-	ValidateResources(paths []string) (chan string, chan *validation.InvalidFile, chan error)
+	ValidateResources(filesConfigurations chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidFile)
 	InitClient(k8sVersion string)
 }
 
@@ -100,12 +102,13 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 		}()
 	}
 
-	filePaths, err := ctx.Reader.FilterFiles(paths)
+	filesPaths, err := ctx.Reader.FilterFiles(paths)
 	if err != nil {
 		fmt.Println(err.Error())
 		return err
 	}
-	if len(filePaths) == 0 {
+	filesPathsLen := len(filesPaths)
+	if filesPathsLen == 0 {
 		noFilesErr := fmt.Errorf("No files detected")
 		fmt.Println(noFilesErr.Error())
 		return noFilesErr
@@ -117,6 +120,10 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 		_spinner.Start()
 	}
 
+	concurrency := 100
+
+	validYamlFilesConfigurationsChan, invalidYamlFilesChan := files.ExtractFilesConfigurations(filesPaths, concurrency)
+
 	createEvaluationResponse, err := ctx.Evaluator.CreateEvaluation(ctx.LocalConfig.CliId, ctx.CliVersion, flags.K8sVersion)
 	if err != nil {
 		fmt.Println(err.Error())
@@ -124,14 +131,38 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 	}
 
 	ctx.K8sValidator.InitClient(createEvaluationResponse.K8sVersion)
-	validFilesPathsChan, invalidFilesPathsChan, errorsChan := ctx.K8sValidator.ValidateResources(paths)
-	go func() {
-		for err := range errorsChan {
+	validK8sFilesConfigurationsChan, invalidK8sFilesChan := ctx.K8sValidator.ValidateResources(validYamlFilesConfigurationsChan, concurrency)
+
+	invalidYamlFiles := aggregateInvalidFiles(invalidYamlFilesChan)
+
+	invalidYamlFilesLen := len(invalidYamlFiles)
+
+	stopEvaluation := invalidYamlFilesLen == filesPathsLen
+	err = ctx.Evaluator.UpdateFailedYamlValidation(invalidYamlFiles, createEvaluationResponse.EvaluationId, stopEvaluation)
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+
+	invalidK8sFiles := aggregateInvalidFiles(invalidK8sFilesChan)
+
+	invalidK8sFilesLen := len(invalidK8sFiles)
+	stopEvaluation = invalidYamlFilesLen+invalidK8sFilesLen == filesPathsLen
+
+	if len(invalidK8sFiles) > 0 {
+		err = ctx.Evaluator.UpdateFailedK8sValidation(invalidK8sFiles, createEvaluationResponse.EvaluationId, stopEvaluation)
+		if err != nil {
 			fmt.Println(err.Error())
+			return err
 		}
-	}()
+	}
 
-	results, invalidFiles, filesConfigurations, errors, err := ctx.Evaluator.Evaluate(validFilesPathsChan, invalidFilesPathsChan, createEvaluationResponse.EvaluationId)
+	var validK8sFilesConfigurations []*extractor.FileConfigurations
+	for fileConfigurations := range validK8sFilesConfigurationsChan {
+		validK8sFilesConfigurations = append(validK8sFilesConfigurations, fileConfigurations)
+	}
+
+	results, err := ctx.Evaluator.Evaluate(validK8sFilesConfigurations, createEvaluationResponse.EvaluationId)
 
 	if _spinner != nil && isInteractiveMode == true {
 		_spinner.Stop()
@@ -142,15 +173,21 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 		passedPolicyCheckCount = results.Summary.TotalPassedCount
 	}
 
-	configsCount := countConfigurations(filesConfigurations)
+	passedYamlValidationCount := filesPathsLen - invalidYamlFilesLen
+	passedK8sValidationCount := passedYamlValidationCount - invalidK8sFilesLen
+
+	configsCount := countConfigurations(validK8sFilesConfigurations)
+
 	evaluationSummary := printer.EvaluationSummary{
+		FilesCount:                filesPathsLen,
+		PassedYamlValidationCount: passedYamlValidationCount,
+		PassedK8sValidationCount:  passedK8sValidationCount,
 		ConfigsCount:              configsCount,
-		FilesCount:                len(paths),
-		PassedYamlValidationCount: len(paths),
-		PassedK8sValidationCount:  len(filesConfigurations),
 		PassedPolicyCheckCount:    passedPolicyCheckCount,
 	}
 
+	invalidFiles := append(invalidYamlFiles, invalidK8sFiles...)
+
 	err = evaluation.PrintResults(results, invalidFiles, evaluationSummary, fmt.Sprintf("https://app.datree.io/login?cliId=%s", ctx.LocalConfig.CliId), flags.Output, ctx.Printer, createEvaluationResponse.K8sVersion)
 
 	var invocationFailedErr error = nil
@@ -158,36 +195,9 @@ func test(ctx *TestCommandContext, paths []string, flags TestCommandFlags) error
 	if err != nil {
 		fmt.Println(err.Error())
 		invocationFailedErr = err
-	} else if len(errors) > 0 {
-		printEvaluationErrors(errors)
-		invocationFailedErr = fmt.Errorf("Invocation failed")
 	} else if len(invalidFiles) > 0 || results.Summary.TotalFailedRules > 0 {
-		invocationFailedErr = fmt.Errorf("Invocation failed")
+		invocationFailedErr = fmt.Errorf("Evaluation failed")
 	}
 
 	return invocationFailedErr
 }
-
-func countConfigurations(filesConfigurations []*extractor.FileConfiguration) int {
-	totalConfigs := 0
-
-	for _, fileConfiguration := range filesConfigurations {
-		totalConfigs += len(fileConfiguration.Configurations)
-	}
-
-	return totalConfigs
-}
-
-func createSpinner(text string, color string) *spinner.Spinner {
-	s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
-	s.Suffix = text
-	s.Color(color)
-	return s
-}
-
-func printEvaluationErrors(errors []*evaluation.Error) {
-	fmt.Println("The following files failed:")
-	for _, fileError := range errors {
-		fmt.Printf("\n\tFilename: %s\n\tError: %s\n\t---------------------", fileError.Filename, fileError.Message)
-	}
-}
diff --git a/cmd/test/main_test.go b/cmd/test/main_test.go
index 889772b..c7e4c25 100644
--- a/cmd/test/main_test.go
+++ b/cmd/test/main_test.go
@@ -18,9 +18,9 @@ type mockEvaluator struct {
 	mock.Mock
 }
 
-func (m *mockEvaluator) Evaluate(validFilesPathsChan chan string, invalidFilesPaths chan *validation.InvalidFile, evaluationId int) (*evaluation.EvaluationResults, []*validation.InvalidFile, []*extractor.FileConfiguration, []*evaluation.Error, error) {
-	args := m.Called(validFilesPathsChan, invalidFilesPaths, evaluationId)
-	return args.Get(0).(*evaluation.EvaluationResults), args.Get(1).([]*validation.InvalidFile), args.Get(2).([]*extractor.FileConfiguration), args.Get(3).([]*evaluation.Error), args.Error(4)
+func (m *mockEvaluator) Evaluate(filesConfigurationsChan []*extractor.FileConfigurations, evaluationId int) (*evaluation.EvaluationResults, error) {
+	args := m.Called(filesConfigurationsChan, evaluationId)
+	return args.Get(0).(*evaluation.EvaluationResults), args.Error(1)
 }
 
 func (m *mockEvaluator) CreateEvaluation(cliId string, cliVersion string, k8sVersion string) (*cliClient.CreateEvaluationResponse, error) {
@@ -28,6 +28,16 @@ func (m *mockEvaluator) CreateEvaluation(cliId string, cliVersion string, k8sVer
 	return args.Get(0).(*cliClient.CreateEvaluationResponse), args.Error(1)
 }
 
+func (m *mockEvaluator) UpdateFailedYamlValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
+	args := m.Called(invalidFiles, evaluationId, stopEvaluation)
+	return args.Error(0)
+}
+
+func (m *mockEvaluator) UpdateFailedK8sValidation(invalidFiles []*validation.InvalidFile, evaluationId int, stopEvaluation bool) error {
+	args := m.Called(invalidFiles, evaluationId, stopEvaluation)
+	return args.Error(0)
+}
+
 type mockMessager struct {
 	mock.Mock
 }
@@ -52,9 +62,9 @@ type K8sValidatorMock struct {
 	mock.Mock
 }
 
-func (kv *K8sValidatorMock) ValidateResources(paths []string) (chan string, chan *validation.InvalidFile, chan error) {
-	args := kv.Called(paths)
-	return args.Get(0).(chan string), args.Get(1).(chan *validation.InvalidFile), args.Get(2).(chan error)
+func (kv *K8sValidatorMock) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *validation.InvalidFile) {
+	args := kv.Called(filesConfigurationsChan, concurrency)
+	return args.Get(0).(chan *extractor.FileConfigurations), args.Get(1).(chan *validation.InvalidFile)
 }
 
 func (kv *K8sValidatorMock) InitClient(k8sVersion string) {
@@ -101,22 +111,23 @@ func TestTestCommand(t *testing.T) {
 		}{RulesCount: 1, TotalFailedRules: 0, FilesCount: 0, TotalPassedCount: 1},
 	}
 
-	invalidFiles := []*validation.InvalidFile{}
-	var fileConfigurations []*extractor.FileConfiguration
-	var evaluationErrors []*evaluation.Error
-
 	mockedEvaluator := &mockEvaluator{}
-	mockedEvaluator.On("Evaluate", mock.Anything, mock.Anything, mock.Anything).Return(evaluationResults, invalidFiles, fileConfigurations, evaluationErrors, nil)
+	mockedEvaluator.On("Evaluate", mock.Anything, mock.Anything, mock.Anything).Return(evaluationResults, nil)
 	mockedEvaluator.On("CreateEvaluation", mock.Anything, mock.Anything, mock.Anything).Return(&cliClient.CreateEvaluationResponse{EvaluationId: evaluationId, K8sVersion: "1.18.0"}, nil)
+	mockedEvaluator.On("UpdateFailedYamlValidation", mock.Anything, mock.Anything, mock.Anything).Return(nil)
+	mockedEvaluator.On("UpdateFailedK8sValidation", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	messager := &mockMessager{}
 	messager.On("LoadVersionMessages", mock.Anything, mock.Anything)
 
 	k8sValidatorMock := &K8sValidatorMock{}
 
-	validFilesPathChan := newValidFilesPathsChan()
+	path := "valid/path"
+	filesConfigurationsChan := newFilesConfigurationsChan(path)
+	filesConfigurations := newFilesConfigurations(path)
+
 	invalidFilesChan := newInvalidFilesChan()
 
-	k8sValidatorMock.On("ValidateResources", mock.Anything).Return(validFilesPathChan, invalidFilesChan, newErrorsChan())
+	k8sValidatorMock.On("ValidateResources", mock.Anything, mock.Anything).Return(filesConfigurationsChan, invalidFilesChan, newErrorsChan())
 	k8sValidatorMock.On("InitClient", mock.Anything).Return()
 
 	printerMock := &PrinterMock{}
@@ -138,41 +149,48 @@ func TestTestCommand(t *testing.T) {
 		Reader:       readerMock,
 	}
 
-	test_testCommand_no_flags(t, mockedEvaluator, validFilesPathChan, invalidFilesChan, evaluationId, ctx)
-	test_testCommand_json_output(t, mockedEvaluator, validFilesPathChan, invalidFilesChan, evaluationId, ctx)
-	test_testCommand_yaml_output(t, mockedEvaluator, validFilesPathChan, invalidFilesChan, evaluationId, ctx)
+	test_testCommand_no_flags(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
+	test_testCommand_json_output(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
+	test_testCommand_yaml_output(t, mockedEvaluator, filesConfigurations, evaluationId, ctx)
 }
 
-func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, validFilesPathChan chan string, invalidFilesChan chan *validation.InvalidFile, evaluationId int, ctx *TestCommandContext) {
+func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) {
 	test(ctx, []string{"8/*"}, TestCommandFlags{K8sVersion: "", Output: ""})
 
-	evaluator.AssertCalled(t, "Evaluate", validFilesPathChan, invalidFilesChan, evaluationId)
-	evaluator.AssertNotCalled(t, "PrintFileParsingErrors")
+	evaluator.AssertCalled(t, "Evaluate", filesConfigurations, evaluationId)
 }
 
-func test_testCommand_json_output(t *testing.T, evaluator *mockEvaluator, validFilesPathChan chan string, invalidFilesChan chan *validation.InvalidFile, evaluationId int, ctx *TestCommandContext) {
+func test_testCommand_json_output(t *testing.T, evaluator *mockEvaluator, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) {
 	test(ctx, []string{"8/*"}, TestCommandFlags{Output: "json"})
 
-	evaluator.AssertCalled(t, "Evaluate", validFilesPathChan, invalidFilesChan, evaluationId)
-	evaluator.AssertNotCalled(t, "PrintFileParsingErrors")
+	evaluator.AssertCalled(t, "Evaluate", filesConfigurations, evaluationId)
 }
 
-func test_testCommand_yaml_output(t *testing.T, evaluator *mockEvaluator, validFilesPathChan chan string, invalidFilesChan chan *validation.InvalidFile, evaluationId int, ctx *TestCommandContext) {
+func test_testCommand_yaml_output(t *testing.T, evaluator *mockEvaluator, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) {
 	test(ctx, []string{"8/*"}, TestCommandFlags{Output: "yaml"})
 
-	evaluator.AssertCalled(t, "Evaluate", validFilesPathChan, invalidFilesChan, evaluationId)
-	evaluator.AssertNotCalled(t, "PrintFileParsingErrors")
+	evaluator.AssertCalled(t, "Evaluate", filesConfigurations, evaluationId)
 }
 
-func newValidFilesPathsChan() chan string {
-	validFilesChan := make(chan string, 1)
+func newFilesConfigurationsChan(path string) chan *extractor.FileConfigurations {
+	filesConfigurationsChan := make(chan *extractor.FileConfigurations, 1)
 
 	go func() {
-		validFilesChan <- "valid/path"
-		close(validFilesChan)
+		filesConfigurationsChan <- &extractor.FileConfigurations{
+			FileName: path,
+		}
+		close(filesConfigurationsChan)
 	}()
 
-	return validFilesChan
+	return filesConfigurationsChan
+}
+
+func newFilesConfigurations(path string) []*extractor.FileConfigurations {
+	var filesConfigurations []*extractor.FileConfigurations
+	filesConfigurations = append(filesConfigurations, &extractor.FileConfigurations{
+		FileName: path,
+	})
+	return filesConfigurations
 }
 
 func newInvalidFilesChan() chan *validation.InvalidFile {
diff --git a/cmd/test/testCommandHelper.go b/cmd/test/testCommandHelper.go
new file mode 100644
index 0000000..ddcd045
--- /dev/null
+++ b/cmd/test/testCommandHelper.go
@@ -0,0 +1,34 @@
+package test
+
+import (
+	"time"
+
+	"github.com/briandowns/spinner"
+	"github.com/datreeio/datree/bl/validation"
+	"github.com/datreeio/datree/pkg/extractor"
+)
+
+func createSpinner(text string, color string) *spinner.Spinner {
+	s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
+	s.Suffix = text
+	s.Color(color)
+	return s
+}
+
+func aggregateInvalidFiles(invalidFilesChan chan *validation.InvalidFile) []*validation.InvalidFile {
+	var invalidFiles []*validation.InvalidFile
+	for invalidFile := range invalidFilesChan {
+		invalidFiles = append(invalidFiles, invalidFile)
+	}
+	return invalidFiles
+}
+
+func countConfigurations(filesConfigurations []*extractor.FileConfigurations) int {
+	totalConfigs := 0
+
+	for _, fileConfiguration := range filesConfigurations {
+		totalConfigs += len(fileConfiguration.Configurations)
+	}
+
+	return totalConfigs
+}
diff --git a/pkg/cliClient/cliClient_test.go b/pkg/cliClient/cliClient_test.go
index 0ee3a97..c654b24 100644
--- a/pkg/cliClient/cliClient_test.go
+++ b/pkg/cliClient/cliClient_test.go
@@ -174,14 +174,14 @@ func TestGetVersionMessage(t *testing.T) {
 	}
 }
 
-func readMock(path string) ([]extractor.K8sConfiguration, error) {
-	var configurations []extractor.K8sConfiguration
+func readMock(path string) ([]extractor.Configuration, error) {
+	var configurations []extractor.Configuration
 
 	absPath, _ := filepath.Abs(path)
 	content, err := ioutil.ReadFile(absPath)
 
 	if err != nil {
-		return []extractor.K8sConfiguration{}, err
+		return []extractor.Configuration{}, err
 	}
 
 	yamlDecoder := yaml.NewDecoder(bytes.NewReader(content))
@@ -198,10 +198,10 @@ func readMock(path string) ([]extractor.K8sConfiguration, error) {
 	return configurations, nil
 }
 
-func castPropertiesMock(fileName string, path string) []*extractor.FileConfiguration {
+func castPropertiesMock(fileName string, path string) []*extractor.FileConfigurations {
 	configurations, _ := readMock(path)
 
-	properties := []*extractor.FileConfiguration{
+	properties := []*extractor.FileConfigurations{
 		{
 			FileName:       fileName,
 			Configurations: configurations,