Skip to content
This repository has been archived by the owner on Feb 16, 2024. It is now read-only.

YAML validation of a TestStep file #29

Closed
76 changes: 76 additions & 0 deletions pkg/test/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"

"gopkg.in/yaml.v2"

k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -561,6 +564,11 @@ func (s *Step) LoadYAML(file string) error {
for _, apply := range s.Apply {
if apply.object.GetObjectKind().GroupVersionKind().Kind == "TestStep" {
if testStep, ok := apply.object.(*harness.TestStep); ok {
// validate yaml files and teststep fields for TestStep objects
err := testStepYamlValidate(testStep, s.Dir)
if err != nil {
return fmt.Errorf("failed to validate TestStep object from %s, error: %v", file, err)
}
if s.Step != nil {
return fmt.Errorf("more than 1 TestStep not allowed in step %q", s.Name)
}
Expand Down Expand Up @@ -688,3 +696,71 @@ func hasTimeoutErr(err []error) bool {
}
return false
}

func testStepYamlValidate(ts *harness.TestStep, dir string) error {
// If apply field is not present, then either command or delete needs to be present,
// if both are not specified it should throw an error
if len(ts.Apply) == 0 && len(ts.Commands) == 0 && len(ts.Delete) == 0 {
return fmt.Errorf("if apply field is not specified either command or delete is expected")
}

// Assert and Error fields require Apply field
if (len(ts.Assert) != 0 || len(ts.Error) != 0) && len(ts.Apply) == 0 {
return fmt.Errorf("cannot have assert or error field when missing apply field")
}

// Validation for referenced field files
for _, Apply := range ts.Apply {
err := validateFieldFile(Apply.File, dir)
if err != nil {
return errors.Join(err, fmt.Errorf("error in apply field"))
}
}
for _, Assert := range ts.Assert {
err := validateFieldFile(Assert, dir)
if err != nil {
return errors.Join(err, fmt.Errorf("error in assert field"))
}
}
for _, Error := range ts.Error {
err := validateFieldFile(Error, dir)
if err != nil {
return errors.Join(err, fmt.Errorf("error in error field"))
}
}

return nil
}

// validateFieldFile checks if the reference file exists
func validateFieldFile(file string, dir string) error {
completeFile := filepath.Join(dir, file)
_, err := os.Stat(completeFile)
if os.IsNotExist(err) {
return fmt.Errorf("the file %s specified in the field does not exist", completeFile)
}

// Check if the file is in yaml format
err = validateYAMLFormat(completeFile)
if err != nil {
return fmt.Errorf("unable to validate yaml format and indentation for file %s", completeFile)
}

return nil
}

// validateYAMLFormat checks if the yaml file is in correct format and indentation
func validateYAMLFormat(file string) error {
data, err := os.ReadFile(file)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", file, err)
}

var temp []client.Object
err = yaml.Unmarshal(data, &temp)
if err != nil {
return fmt.Errorf("failed to parse yaml file %s: %w", file, err)
}

return nil
}