Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First stage of new tests which share logic with library tests #348

Merged
merged 2 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Temporary Build Files
build/_output
build/_test
test/v200/schemaTest/tmp
test/**/tmp
test/go/pkg
# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
### Emacs ###
Expand Down
26 changes: 25 additions & 1 deletion test/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# API Tests
# schemsTest
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
yangcao77 marked this conversation as resolved.
Show resolved Hide resolved

The API tests are intended to provide a comprehensive verification of the devfile schemas. This includes:
- Ensuring every possible attribute is valid.
Expand Down Expand Up @@ -44,3 +44,27 @@ The test will read each of the test-xxxxxx.json files and run the tests defined
1. Modify the copied tests as needed for the new version as decsribed above.
1. Add `test/v201/schemaTest/tmp` to the .gitignore file.
1. Run the test


# apiTest

A new test approach, shared with the library repository for testing valid devfiles. Basically the test creates lots of valid devfiles whith different content. The attributes which are set and the values to which they are set are randomized. These tests are a work in progress and the intent is to eventually replace schemaTest.

## Test structure

- `test/v200/apiTest/api-test.go`: The go unit test program
- `test/v200/utils/api/test-utils.go` : utilites, used by the test, which contain functions uniqiue to the api tests.
- `test/v200/utils/common/*-utils.go` : utilites, used by the test, which are also used by the library tests. Mostly contain the code to generate valid devfile content.


## Running tests

from the `test/v200/apiTest/` directory run
- `go test -v`

* The test will generate a set of valid devfile.yaml files in `test/v200/apiTest/tmp/api-test/
* The test will generate a log file: `test/v200/apiTest/tmp/test.log`
* Each run of the test removes the `test/v200/apiTest/tmp` directory from the previous run.



80 changes: 80 additions & 0 deletions test/v200/apiTest/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package apiTest

import (
"testing"

schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
api "github.com/devfile/api/v2/test/v200/utils/api"
common "github.com/devfile/api/v2/test/v200/utils/common"
yangcao77 marked this conversation as resolved.
Show resolved Hide resolved
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
)

func Test_ExecCommand(t *testing.T) {
testContent := common.TestContent{}
testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType}
testContent.EditContent = false
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_ApplyCommand(t *testing.T) {
testContent := common.TestContent{}
testContent.CommandTypes = []schema.CommandType{schema.ApplyCommandType}
testContent.EditContent = false
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_CompositeCommand(t *testing.T) {
testContent := common.TestContent{}
testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType}
testContent.EditContent = false
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_MultiCommand(t *testing.T) {
testContent := common.TestContent{}
testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType,
schema.CompositeCommandType,
schema.ApplyCommandType}
testContent.EditContent = true
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_ContainerComponent(t *testing.T) {
testContent := common.TestContent{}
testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType}
testContent.EditContent = false
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_VolumeComponent(t *testing.T) {
testContent := common.TestContent{}
testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType}
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_MultiComponent(t *testing.T) {
testContent := common.TestContent{}
testContent.ComponentTypes = []schema.ComponentType{
schema.ContainerComponentType,
schema.VolumeComponentType}
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}

func Test_Everything(t *testing.T) {
testContent := common.TestContent{}
testContent.CommandTypes = []schema.CommandType{
schema.ExecCommandType,
schema.CompositeCommandType,
schema.ApplyCommandType}
testContent.ComponentTypes = []schema.ComponentType{
schema.ContainerComponentType,
schema.VolumeComponentType}
testContent.FileName = common.GetDevFileName()
api.RunTest(testContent, t)
}
173 changes: 173 additions & 0 deletions test/v200/utils/api/test_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package api

import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"strconv"
"strings"
"testing"

common "github.com/devfile/api/v2/test/v200/utils/common"
yangcao77 marked this conversation as resolved.
Show resolved Hide resolved
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
"github.com/santhosh-tekuri/jsonschema"
"sigs.k8s.io/yaml"
)

const (
// numDevfiles : the number of devfiles to create for each test
numDevfiles = 5

schemaFileName = "../../../schemas/latest/ide-targeted/devfile.json"
)

var schemas = make(map[string]SchemaFile)

type SchemaFile struct {
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
Schema *jsonschema.Schema
}

type DevfileValidator struct{}
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved

// WriteAndVerify implements Saved.DevfileValidator interface.
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
// writes to disk and validates the specified devfile
func (devfileValidator DevfileValidator) WriteAndValidate(devfile *common.TestDevfile) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps function WriteAndValidate and writeDevfile can be under pkg test/v200/utils/common?
So you do not need to define the empty struct DevfileValidator to avoid import cycle

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method implementations are completely different comparing the api and library implementations so they do not belong in the common. This is the standard way of implementing an interface declared in a different package.

err := writeDevfile(devfile)
if err != nil {
common.LogErrorMessage(fmt.Sprintf("Error writing file : %s : %v", devfile.FileName, err))
} else {
err = validateDevfile(devfile)
if err != nil {
common.LogErrorMessage(fmt.Sprintf("Error vaidating file : %s : %v", devfile.FileName, err))
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
}
}
return err
}

// CheckWithSchema checks the validity of aa devfile against the schema.
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
yangcao77 marked this conversation as resolved.
Show resolved Hide resolved
func (schemaFile *SchemaFile) CheckWithSchema(devfile string, expectedMessage string) error {

// Read the created yaml file, ready for converison to json
devfileData, err := ioutil.ReadFile(devfile)
if err != nil {
common.LogErrorMessage(fmt.Sprintf(" FAIL: schema : unable to read %s: %v", devfile, err))
return err
}

// Convert the yaml file to json
devfileDataAsJSON, err := yaml.YAMLToJSON(devfileData)
if err != nil {
common.LogErrorMessage(fmt.Sprintf(" FAIL : %s : schema : failed to convert to json : %v", devfile, err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplify the message

Suggested change
common.LogErrorMessage(fmt.Sprintf(" FAIL : %s : schema : failed to convert to json : %v", devfile, err))
common.LogErrorMessage(fmt.Sprintf(" FAIL : schema : failed to convert %s to json : %v", devfile, err))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer my format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine. it's up to you

return err
}

validationErr := schemaFile.Schema.Validate(bytes.NewReader(devfileDataAsJSON))
if validationErr != nil {
if len(expectedMessage) > 0 {
if !strings.Contains(validationErr.Error(), expectedMessage) {
err = errors.New(common.LogErrorMessage(fmt.Sprintf(" FAIL : schema : %s : Did not fail as expected : %s got : %v", devfile, expectedMessage, validationErr)))
} else {
common.LogInfoMessage(fmt.Sprintf("PASS: schema : Expected Error received : %s", expectedMessage))
}
} else {
err = errors.New(common.LogErrorMessage(fmt.Sprintf(" FAIL : schema : %s : Did not pass as expected, got : %v", devfile, validationErr)))
}
} else {
if len(expectedMessage) > 0 {
err = errors.New(common.LogErrorMessage(fmt.Sprintf(" FAIL : schema : %s : was valid - Expected Error not found : %v", devfile, validationErr)))
} else {
common.LogInfoMessage(fmt.Sprintf(" PASS : schema : %s : devfile was valid.", devfile))
}
}
return err
}

// GetSchema downloads and saves a schema from the provided url
func GetSchema(schemafile string) (SchemaFile, error) {
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved

var err error
schemaFile, found := schemas[schemafile]
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
if !found {

schemaFile = SchemaFile{}

// Prepare the schema file
compiler := jsonschema.NewCompiler()
compiler.Draft = jsonschema.Draft7
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
schemaFile.Schema, err = compiler.Compile(schemafile)
if err != nil {
//t.Fatalf(" FAIL : Schema compile failed : %s: %v", testJsonContent.SchemaFile, err)
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
yangcao77 marked this conversation as resolved.
Show resolved Hide resolved
common.LogErrorMessage(fmt.Sprintf("FAIL : Failed to compile schema %v", err))
} else {
common.LogInfoMessage(fmt.Sprintf("Schema compiled from file: %s)", schemafile))
schemas[schemafile] = schemaFile
}
}
return schemaFile, err
}

// WriteDevfile creates a devfile on disk for use in a test.
func writeDevfile(devfile *common.TestDevfile) error {
var err error

fileName := devfile.FileName
if !strings.HasSuffix(fileName, ".yaml") {
fileName += ".yaml"
}

common.LogInfoMessage(fmt.Sprintf("Marshall and write devfile %s", devfile.FileName))

c, marshallErr := yaml.Marshal(&(devfile.SchemaDevFile))

if marshallErr != nil {
err = errors.New(common.LogErrorMessage(fmt.Sprintf("Marshall devfile %s : %v", devfile.FileName, marshallErr)))
} else {
err = ioutil.WriteFile(fileName, c, 0644)
if err != nil {
common.LogErrorMessage(fmt.Sprintf("Write devfile %s : %v", devfile.FileName, err))
}
}
return err
}

// validateDevfile check the provided defile against the schema
maysunfaisal marked this conversation as resolved.
Show resolved Hide resolved
mmulholla marked this conversation as resolved.
Show resolved Hide resolved
func validateDevfile(devfile *common.TestDevfile) error {

var err error
var schemaFile SchemaFile

schemaFile, err = GetSchema(schemaFileName)
if err != nil {
common.LogErrorMessage(fmt.Sprintf("Failed to get devfile schema : %v", err))
} else {
err = schemaFile.CheckWithSchema(devfile.FileName, "")
if err != nil {
common.LogErrorMessage(fmt.Sprintf("Verification with devfile schema failed : %v", err))
} else {
common.LogInfoMessage(fmt.Sprintf("Devfile validated using JSONSchema schema : %s", devfile.FileName))
}
}

return err
}

// RunTest : Runs a test to create and verify a devfile based on the content of the specified TestContent
func RunTest(testContent common.TestContent, t *testing.T) {

common.LogMessage(fmt.Sprintf("Start test for %s", testContent.FileName))

validator := DevfileValidator{}

devfileName := testContent.FileName
for i := 1; i <= numDevfiles; i++ {

testContent.FileName = common.AddSuffixToFileName(devfileName, strconv.Itoa(i))

testDevfile, err := common.GetDevfile(testContent.FileName, nil, validator)
if err != nil {
t.Fatalf(common.LogMessage(fmt.Sprintf("Error creating devfile : %v", err)))
}

testDevfile.RunTest(testContent, t)
}
}
Loading