Files
apitester/main.go

297 lines
7.0 KiB
Go

package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"strconv"
"github.com/iancoleman/orderedmap"
)
type TestDefinitions struct {
Name string `json:"name"`
TestCases []TestCase `json:"testCases"`
BaseUrl string `json:"url"`
Headers map[string]string // Need to initialise with make
//Header HeaderOptions `json:"header"`
}
type TestCase struct {
Name string
Path string `json:"path"`
Method string `json:"method"`
Description string `json:"description"`
Expect ExpectOptions `json:"expect"`
Header map[string]string
Body map[string]string
}
type ExpectOptions struct {
Header HeaderOptions `json:"header"`
Body BodyOptions `json:"body"`
}
type HeaderOptions struct {
Contains ContainsTest `json:"contains"`
Equals EqualsTest `json:"equals"`
}
type BodyOptions struct {
Contains ContainsTest `json:"contains"`
Equals EqualsTest `json:"equals"`
HasKeys HasKeysTest `json:"hasKeys"`
PathEq PathEqTest `json:"pathEq"`
PathContains PathContainsTest `json:"pathContains"`
}
// Test types
type ContainsTest struct {
// dynamically specified key value pairs
}
type EqualsTest struct {
// dynamically specified key value pairs
}
type HasKeysTest struct {
// dynamically specified key value pairs
}
type PathEqTest struct {
// dynamically specified key value pairs
}
type PathContainsTest struct {
// dynamically specified key value pairs
}
// fileExists returns true if the specified file exists and is not a directory
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func PerformGet() {
// Sample code from https://golangnote.com/request/sending-post-request-in-golang-with-header
url := "https://www.example.com/api/v1/create"
contentType := "application/json"
data := []byte(`{"name": "Test User", "email": "test@example.com"}`)
client := &http.Client{}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", contentType)
req.Header.Add("Authorization", "Bearer YOUR_ACCESS_TOKEN")
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
func PerformPost() {
}
func TestOutput() {
}
func main() {
var s []byte
var err error
var ok bool
// Prepare struct to store settings common to all tests
testDefinitions := new(TestDefinitions)
testDefinitions.Headers = make(map[string]string)
//testCaseDefinition := new(InputData)
// Command line arguments
var inputJson string
// Process command line arguments
flag.StringVar(&inputJson, "input", "./tests.json", "Full path to input json test definition file")
// Read the json input file
if fileExists(inputJson) {
s, err = os.ReadFile(inputJson)
if err != nil {
panic(err)
}
} else {
fmt.Printf("Input JSON file '%s' does not exist.\n", inputJson)
os.Exit(1)
}
// Unmarshal the json into an orderedmap to preserve the ordering of json structure
o := orderedmap.New()
err = json.Unmarshal([]byte(s), &o)
if err != nil {
error := fmt.Sprintf("JSON Unmarshal error %s\n", err)
panic(error)
}
topLevel := o.Keys()
fmt.Printf("Found %d top-level keys in json data\n", len(topLevel))
/*
for i, key := range topLevel {
fmt.Printf("[%d] : %s\n", i, key)
}
*/
// TODO : Check required top level keys are present
if len(topLevel) <= 1 {
error := "Missing required keys in input json"
panic(error)
}
// Get the name of the group of tests
if data, ok := o.Get("name"); ok {
testDefinitions.Name = data.(string)
fmt.Printf("test name: '%s'\n", testDefinitions.Name)
}
// Get a reference to the node containing each of the test cases
testsInterface, ok := o.Get("testCases")
if !ok {
fmt.Printf("No key defining test cases found\n")
}
/*
// Test Cases is an array, need to go one level down before we start looking at key/values
vs := testsInterface.([]interface{})
fmt.Printf("Listing %d test definitions\n", len(vs))
for i, vInterface := range vs {
v := vInterface.(orderedmap.OrderedMap)
keys := v.Keys()
fmt.Printf("Test %d\n", i)
for j := range keys {
fmt.Printf("[%d] : %s\n", j, keys[j])
}
}
*/
/*
vslice := testsInterface.([]interface{})
vmap := vslice[2].(orderedmap.OrderedMap)
k := vmap.Keys()
for i := range k {
fmt.Printf("[%d] : %s\n", i, k[i])
}
return
*/
// Get the keys for the first test so we know what config options have been specified
testCasesMap := testsInterface.(orderedmap.OrderedMap)
testCasesKeys := testCasesMap.Keys()
// Parse json into our testCaseDefinition
// Parse each key into our config struct
fmt.Printf("Listing %d test cases\n", len(testCasesKeys))
for i, outerKey := range testCasesKeys {
fmt.Printf("Test %d : %s\n", i, outerKey)
// Get the name of the test case
thisTestCase := new(TestCase)
thisTestCase.Name = outerKey
if testCase, ok := testCasesMap.Get(outerKey); ok {
// Get the details of the test case
thisTestCaseMap := testCase.(orderedmap.OrderedMap)
// Path
if val, ok := thisTestCaseMap.Get("path"); ok {
thisTestCase.Path = val.(string)
}
// Method
if val, ok := thisTestCaseMap.Get("method"); ok {
thisTestCase.Method = val.(string)
}
// Description
if val, ok := thisTestCaseMap.Get("description"); ok {
thisTestCase.Description = val.(string)
}
// Body
if val, ok := thisTestCaseMap.Get("body"); ok {
// Create a normal string map for all the body key-value pairs
thisTestCase.Body = OrderedToStringMap(val.(orderedmap.OrderedMap))
/*
thisTestCase.Body = make(map[string]string)
bodyMap := val.(orderedmap.OrderedMap)
bodyMapKeys := bodyMap.Keys()
for _, bodyKey := range bodyMapKeys {
if bodyVal, ok := bodyMap.Get(bodyKey); ok {
switch bodyVal.(type) {
case string:
thisTestCase.Body[bodyKey] = bodyVal.(string)
case bool:
thisTestCase.Body[bodyKey] = strconv.FormatBool(bodyVal.(bool))
}
}
}
*/
}
// Expect - this is more tricky since it is yet another json fragment
/*
for j, key := range thisTestCaseMap.Keys() {
val, _ := thisTestCaseMap.Get(key)
fmt.Printf("[%d] %s : %s\n", j, key, val)
}
*/
}
testDefinitions.TestCases = append(testDefinitions.TestCases, *thisTestCase)
}
// For debugging, just dump the output
fmt.Printf("%+v\n", testDefinitions)
}
func OrderedToStringMap(input orderedmap.OrderedMap) map[string]string {
result := make(map[string]string)
mapKeys := input.Keys()
for _, bodyKey := range mapKeys {
if bodyVal, ok := input.Get(bodyKey); ok {
switch bodyVal.(type) {
case string:
result[bodyKey] = bodyVal.(string)
case bool:
result[bodyKey] = strconv.FormatBool(bodyVal.(bool))
}
}
}
return result
}