/**
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package session
import (
"testing"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strings"
"github.com/jarcoal/httpmock"
"github.com/softlayer/softlayer-go/sl"
"github.com/softlayer/xmlrpc"
)
// structure of a single testcase
type xmltestcase struct {
description string
service string
method string
args []interface{}
options sl.Options
responder httpmock.Responder
expected interface{}
expectError bool
}
const xmlrpcEndpoint = "https://api.softlayer.com/xmlrpc/v3"
var xmlClient *xmlrpc.Client
var err error
var xmltestcases = []xmltestcase{
// Positive tests
{
description: "empty array return",
service: "SoftLayer_Account",
method: "getVirtualGuests",
args: nil,
options: sl.Options{},
responder: httpmock.NewStringResponder(200, ``),
expected: make([]struct{}, 0),
expectError: false,
},
{
description: "Values in array",
service: "SoftLayer_Account",
method: "getVirtualGuests",
args: nil,
options: sl.Options{},
responder: httpmock.NewStringResponder(200, `15`),
expected: []int{1, 5},
expectError: false,
},
{
description: "Single Value",
service: "SoftLayer_Account",
method: "getOneThing",
args: nil,
options: sl.Options{},
responder: httpmock.NewStringResponder(200, `100`),
expected: 100,
expectError: false,
},
{
description: "Test Object Mask mask[]",
service: "SoftLayer_Account",
method: "getVirtualGuests",
args: nil,
options: sl.Options{Mask: "mask[id,hostname]"},
responder: responderCheckObjectMask,
expected: "OK",
expectError: false,
},
{
description: "Test Object Mask no mask[]",
service: "SoftLayer_Account",
method: "getVirtualGuests",
args: nil,
options: sl.Options{Mask: "id,hostname"},
responder: responderCheckObjectMask,
expected: "OK",
expectError: false,
},
}
func TestXmlRpc(t *testing.T) {
// The structure for this test is inspired heavily from here:
// https://github.com/softlayer/xmlrpc/blob/master/decoder_test.go
// setup session and mock environment
s = New()
s.Endpoint = xmlrpcEndpoint
//s.Debug = true
httpmock.Activate()
defer httpmock.Deactivate()
// For each test case:
// 1. Setup the mock environment
// 2. Allocate an empty variable for the response, based on the 'expected' response
// 3. Call DoRequest
// 4. Check result matches expected
// 5. Reset the mock
for _, tc := range xmltestcases {
setupxml(tc)
// Do some reflecting to make comparisions of type and value easier
pResult := reflect.New(reflect.TypeOf(tc.expected))
expected := reflect.ValueOf(tc.expected)
fmt.Printf("Test [%s]: ", tc.description)
// Actually make the request
err := s.DoRequest(tc.service, tc.method, tc.args, &tc.options, pResult.Interface())
// Removes the pointer bits I think.
pResult = pResult.Elem()
// Report results
switch {
// Positive tests - no error expected
case !tc.expectError && err != nil:
fmt.Println("Unexpected error:", err.Error())
t.Fail()
case !tc.expectError && err == nil:
// Slices comparisons gave me a lot of trouble with DeepEqual, check each element instead
if expected.Kind() == reflect.Slice {
if pResult.Len() != expected.Len() {
fmt.Println("FAIL")
t.Errorf("Slice length mismatch. Expcected %v, got %v", expected.Len(), pResult.Len())
}
for i := 0; i < pResult.Len(); i++ {
if expected.Index(i).Interface() != pResult.Index(i).Interface() {
fmt.Println("FAIL")
t.Errorf("Expected %#v, got %#v", expected.Index(i), pResult.Index(i))
}
}
// If we haven't failed until now, assume its ok.
fmt.Println("OK")
} else if !reflect.DeepEqual(expected.Interface(), pResult.Interface()) {
fmt.Println("FAIL")
t.Errorf("Expected %#v, got %#v", expected, pResult)
} else {
fmt.Println("OK")
}
// Negative tests - error expected
case tc.expectError && err == nil:
fmt.Println("FAIL")
t.Errorf("Expected error not received")
case tc.expectError && err != nil:
fmt.Println("OK")
}
teardownxml()
}
}
func responderCheckObjectMask(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)
string_body := string(body)
// fmt.Printf("Body: %v\n", string_body)
message := "FAILED"
if strings.Contains(string_body, "maskmask[id,hostname]") {
message = "OK"
}
resp := httpmock.NewStringResponse(200, ``+message+``)
return resp, nil
}
func setupxml(tc xmltestcase) {
httpmock.RegisterResponder(
"POST",
fmt.Sprintf("%s/%s", xmlrpcEndpoint, tc.service),
tc.responder)
}
// remove any existig mocks (e.g., between tests)
func teardownxml() {
httpmock.Reset()
}