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

Refactor #22

Merged
merged 9 commits into from
May 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 1 addition & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
go:
- "1.10"
env:
- PATH=$HOME/gopath/bin:$PATH APPPATH=$HOME/gopath/src/github.com/bluehoodie/smoke/ ENVTOKEN=token235 COVERALLS_TOKEN=PPZJ56Ts2op5e2dlIRIhO82p6FPP95mZ1
- PATH=$HOME/gopath/bin:$PATH APPPATH=$HOME/gopath/src/github.com/bluehoodie/smoke/ ENVTOKEN=token235
before_install:
# mock web service
- docker pull bluehoodie/httpbin
Expand All @@ -18,8 +18,6 @@ before_script:
# install dependencies
- go get -u github.com/golang/dep/cmd/dep
- go get -u golang.org/x/lint/golint
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- dep ensure
- go test -race -v `go list ./... | grep -v -e /vendor/ -e /mock/`
- go list ./... | grep -v /vendor/ | xargs -L1 golint -set_exit_status
Expand All @@ -32,9 +30,6 @@ script:
after_script:
- docker stop httpbin
after_success:
- go get github.com/goreleaser/goreleaser
- go test ./... -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN
- cd $APPPATH
- if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then docker login -u="$DOCKERHUB_LOGIN" -p="$DOCKERHUB_PASSWORD"; fi
- if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then make publish; fi
Expand Down
28 changes: 27 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

[![Build Status](https://travis-ci.org/bluehoodie/smoke.svg?branch=master)](https://travis-ci.org/bluehoodie/smoke)
[![Go Report Card](https://goreportcard.com/badge/github.com/bluehoodie/smoke)](https://goreportcard.com/report/github.com/bluehoodie/smoke)
[![Coverage Status](https://coveralls.io/repos/github/Tkanos/smoke/badge.svg?branch=master)](https://coveralls.io/github/Tkanos/smoke?branch=master)


A simple application to write and run smoke tests for RESTful APIs.

Expand Down
69 changes: 59 additions & 10 deletions tester/tester.go → internal/tester/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package tester

import (
"bytes"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"strings"

Expand Down Expand Up @@ -39,16 +43,45 @@ type Contract struct {

ExpectedHTTPCode int `json:"http_code_is" yaml:"http_code_is"`
ExpectedResponseBody string `json:"response_body_contains" yaml:"response_body_contains"`
ExpectedResponses []string `json:"response_contains" yaml:"response_contains"`
ExpectedResponses []string `json:"response_contains" yaml:"response_contains"`
ExpectedHeaders map[string]string `json:"response_headers_is" yaml:"response_headers_is"`
}


// Test represents the data for a full test suite
type Test struct {
Globals map[string]string `json:"globals" yaml:"globals"`
Globals map[string]string `json:"globals" yaml:"globals"`
Contracts []Contract `json:"contracts" yaml:"contracts"`
}

// NewTest returns an initialized *Test and any error encountered along the way
func NewTest(inputFile string) (*Test, error) {
data, err := ioutil.ReadFile(inputFile)
if err != nil {
return nil, errors.Wrapf(err, "could not read test file %v", inputFile)
}

t := Test{}
if err := unmarshal(inputFile, data, &t); err != nil {
return nil, errors.Wrap(err, "could not unmarshal test data")
}

t.init()

Contracts []Contract `json:"contracts" yaml:"contracts"`
return &t, nil
}

func (t *Test) init() {
if t == nil || len(t.Contracts) == 0 {
return
}

for _, c := range t.Contracts {
if c.ExpectedResponseBody == "" {
continue
}

c.ExpectedResponses = append(c.ExpectedResponses, c.ExpectedResponseBody)
}
}

// Runner is the primary struct of this package and is responsible for running the test suite
Expand All @@ -58,7 +91,7 @@ type Runner struct {

client *http.Client

test Test
test *Test
url string
}

Expand All @@ -83,7 +116,7 @@ func WithHTTPClient(client *http.Client) Option {
}

// NewRunner returns a *Runner for a given url and Test.
func NewRunner(url string, test Test, opts ...Option) *Runner {
func NewRunner(url string, test *Test, opts ...Option) *Runner {
runner := &Runner{
url: url,
test: test,
Expand Down Expand Up @@ -172,8 +205,8 @@ func failure(out io.Writer, name, format string, args ...interface{}) {

func createAndSendRequest(contract Contract, url string, client *http.Client) (*http.Response, error) {
// create request
path := strings.Join([]string{url, contract.Path}, "")
req, err := http.NewRequest(strings.ToUpper(contract.Method), path, strings.NewReader(contract.Body))
uri := strings.Join([]string{url, contract.Path}, "")
req, err := http.NewRequest(strings.ToUpper(contract.Method), uri, strings.NewReader(contract.Body))
if err != nil {
return nil, fmt.Errorf("could not create http request: %v", err)
}
Expand Down Expand Up @@ -206,7 +239,7 @@ func validateResponseBody(contract Contract, body []byte) error {
if len(contract.ExpectedResponses) == 0 {
return nil
}

for _, r := range contract.ExpectedResponses {
// check if it is a regexp
if strings.HasPrefix(r, "r/") {
Expand All @@ -221,7 +254,7 @@ func validateResponseBody(contract Contract, body []byte) error {
return fmt.Errorf("expected response not found in the body")
}
}

return nil
}

Expand All @@ -239,3 +272,19 @@ func validateHeaders(contract Contract, resp *http.Response) error {

return nil
}

func unmarshal(filename string, in []byte, out interface{}) error {
var unmarshalError error

ext := strings.Trim(path.Ext(filename), ".")
switch ext {
case "yaml":
fallthrough
case "yml":
unmarshalError = yaml.Unmarshal(in, out)
default:
unmarshalError = json.Unmarshal(in, out)
}

return unmarshalError
}
35 changes: 17 additions & 18 deletions tester/variables.go → internal/tester/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ func parseOutputs(runner *Runner, contract *Contract, body []byte) (err error) {
s := strings.Split(value, ".")
var result string
if len(s) > 1 {
switch strings.ToUpper(s[0]) {
case "JSON":
result, err = jsonParser(value, s[1:], body)
switch strings.ToLower(s[0]) {
case "json":
result, err = parseJSON(value, s[1:], body)
if err != nil {
return
}
Expand All @@ -109,23 +109,23 @@ func parseOutputs(runner *Runner, contract *Contract, body []byte) (err error) {
return nil
}

func jsonParser(format string, field []string, body []byte) (value string, err error) {
if body == nil || field == nil || len(field) == 0 {
return "", fmt.Errorf("Bad Parameter")
func parseJSON(format string, fields []string, body []byte) (value string, err error) {
if body == nil || fields == nil || len(fields) == 0 {
return "", fmt.Errorf("bad parameter")
}

begin := 0
var next interface{}

// If it begins by a [], we should extract the info from an array
if string(field[0][0]) == "[" {
if string(fields[0][0]) == "[" {
var arr []interface{}
err = json.Unmarshal(body, &arr)
if err != nil {
return
}
v := extractValueFromJSONArray(field[begin], arr)
if begin == len(field)-1 { // if it is the value expected
v := extractValueFromJSONArray(fields[begin], arr)
if begin == len(fields)-1 { // if it is the value expected
value = fmt.Sprint(v)
return
}
Expand All @@ -147,17 +147,17 @@ func jsonParser(format string, field []string, body []byte) (value string, err e
}

tmp := jsonMap
for i := begin; i < len(field); i++ {
if i == len(field)-1 {
if string(field[i][len(field[i])-1]) == "]" { // It's an array and field[i] is in the format "param[number]"
v := extractValueFromJSONMap(field[i], tmp)
for i := begin; i < len(fields); i++ {
if i == len(fields)-1 {
if string(fields[i][len(fields[i])-1]) == "]" { // It's an array and fields[i] is in the format "param[number]"
v := extractValueFromJSONMap(fields[i], tmp)
if v != nil {
value = fmt.Sprint(v)
return
}
}
// value
if val, ok := tmp[field[i]]; ok {
if val, ok := tmp[fields[i]]; ok {
value = fmt.Sprint(val)
return
}
Expand All @@ -167,18 +167,17 @@ func jsonParser(format string, field []string, body []byte) (value string, err e

var o interface{}

if string(field[i][len(field[i])-1]) == "]" { // It's an array and field[i] is in the format "param[number]"
o = extractValueFromJSONMap(field[i], tmp)
if string(fields[i][len(fields[i])-1]) == "]" { // It's an array and fields[i] is in the format "param[number]"
o = extractValueFromJSONMap(fields[i], tmp)
} else {
if val, ok := tmp[field[i]]; ok {
if val, ok := tmp[fields[i]]; ok {
o = val
} else {
return "", fmt.Errorf("value not present in the json object %s", format)
}
}

tmp = o.(map[string]interface{})

}

return
Expand Down
Loading