Skip to content

Commit

Permalink
Refactored generator to avoid duplication
Browse files Browse the repository at this point in the history
* added more unit tests for the executor
  • Loading branch information
PhilibertDugas committed Jan 18, 2016
1 parent 61b8bec commit a2514cb
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 46 deletions.
17 changes: 8 additions & 9 deletions http_executor.go → executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ type GoltExecutor struct {
SendingChannel chan []byte
}


func (e *GoltExecutor) executeHttpRequests() {
for i := 1; i <= e.ThreadGroup.Repetitions; i++ {
e.executeRequestsSequence(e.ThreadGroup.Requests)
Expand All @@ -23,11 +22,12 @@ func (e *GoltExecutor) executeHttpRequests() {

func (e *GoltExecutor) executeRequestsSequence(httpRequests []GoltRequest) {
// TODO: By defining the map here, it's local to the thread, maybe we want something else
extractorMap := make(map[string]string)
regexMap := make(map[string]string)
generator := &GoltGenerator{RegexMap: regexMap}
extractionWasDone := false

for _, request := range httpRequests {
req := BuildRequest(extractionWasDone, request, extractorMap)
req := generator.buildRequest(extractionWasDone, request)
notifyWatcher(e.SendingChannel)

start := time.Now()
Expand All @@ -42,11 +42,10 @@ func (e *GoltExecutor) executeRequestsSequence(httpRequests []GoltRequest) {
e.logResult(request, resp, err, elapsed)

// Handle all the regular expression extraction
extractionWasDone = handleExtraction(request, resp, extractorMap)
extractionWasDone = handleExtraction(request.Extract, resp, regexMap)
}
}

// TODO: Too many parameters on this method, to refactor
func (e *GoltExecutor) logResult(request GoltRequest, resp *http.Response, err error, elapsed time.Duration) {
var msg LogMessage
if err != nil {
Expand Down Expand Up @@ -87,13 +86,13 @@ func isCallSuccessful(assert GoltAssert, response *http.Response) bool {
return isCallSuccessful
}

func handleExtraction(request GoltRequest, resp *http.Response, extractorMap map[string]string) bool{
func handleExtraction(extractor GoltExtractor, resp *http.Response, regexMap map[string]string) bool{
// Check if we are extracting anything and store it in a Map
regexIsDefined := request.Extract.Field != "" && request.Extract.Regex != "" && request.Extract.Var != ""
regexIsDefined := extractor.Field != "" && extractor.Regex != "" && extractor.Var != ""
if regexIsDefined {
value := executeExtraction(request.Extract, resp)
value := executeExtraction(extractor, resp)
if value != "" {
extractorMap[request.Extract.Var] = value
regexMap[extractor.Var] = value
return true
}
}
Expand Down
70 changes: 49 additions & 21 deletions http_executor_test.go → executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@ import (
"log"
)

const testKey1, testValue1 = "TEST_VALUE_1", "test-string-1"
const testKey2, testValue2 = "TEST_VALUE_2", "test-string-2"
var testingMap = map[string]string{testKey1: testValue1, testKey2: testKey2}

var countedRequest = 0

var jsonHeaders = http.Header{"Content-Type":[]string{"application/json"}}
var htmlHeaders = http.Header{"Content-Type":[]string{"text/html"}}

var assertionTestingTable = []struct{
expectedSuccess bool
response *http.Response
Expand All @@ -25,6 +22,46 @@ var assertionTestingTable = []struct{
{expectedSuccess: false, response: &http.Response{StatusCode: 404, Header: htmlHeaders,}},
}

var requestTestingTable = []struct {
extractor GoltExtractor
response *http.Response
expectedValue string
extractionWasExecuted bool
} {
{
extractor: GoltExtractor{Field: "headers", Var: "CONTENT_TYPE", Regex: "text/html(.*)"},
response: &http.Response{Header: htmlHeaders},
expectedValue: "text/html",
extractionWasExecuted: true,
},
{
extractor: GoltExtractor{Field: "headers", Var: "CONTENT_TYPE", Regex: "text/html(.*)"},
response: &http.Response{Header: jsonHeaders},
expectedValue: "",
extractionWasExecuted: false,
},
{
extractor: GoltExtractor{Field: "headers", Var: "", Regex: "text/html(.*)"},
response: &http.Response{Header: jsonHeaders},
expectedValue: "",
extractionWasExecuted: false,
},
}

type MockReader struct {}
func (reader MockReader) Read(p []byte) (int, error) {return 0, nil}
type MockWriter struct {}
func (writer MockWriter) Close() error {return nil}
type MockReadCloser struct {
MockReader
MockWriter
}
type MockSender struct {}
func (sender MockSender) Send(req *http.Request) (*http.Response, error) {
countedRequest += 1
return &http.Response{Body: MockReadCloser{}}, nil
}

func TestIsCallSuccessful(t *testing.T) {
assert := GoltAssert{
Type: "application/json",
Expand All @@ -50,21 +87,12 @@ func TestExecuteRequestsSequence(t *testing.T) {
}
}


type MockReader struct {}
func (reader MockReader) Read(p []byte) (int, error) {return 0, nil}

type MockWriter struct {}
func (writer MockWriter) Close() error {return nil}

type MockReadCloser struct {
MockReader
MockWriter
}

type MockSender struct {}

func (sender MockSender) Send(req *http.Request) (*http.Response, error) {
countedRequest += 1
return &http.Response{Body: MockReadCloser{}}, nil
func TestHandleExtraction(t *testing.T) {
for _, entry := range requestTestingTable {
regexMap := make(map[string]string)
extractionExecuted := handleExtraction(entry.extractor, entry.response, regexMap)
if extractionExecuted != entry.extractionWasExecuted || regexMap["CONTENT_TYPE"] != entry.expectedValue {
t.Error("Result returned by the method is not what was expected")
}
}
}
30 changes: 17 additions & 13 deletions http_request_generator.go → generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,32 @@ import (
const regexForVariable = "\\$\\(.*?\\)"
var r, _ = regexp.Compile(regexForVariable)

func BuildRequest(shouldRegex bool, request GoltRequest, regexMap map[string]string) *http.Request{
type GoltGenerator struct {
RegexMap map[string]string
}

func (g *GoltGenerator) buildRequest(shouldRegex bool, request GoltRequest) *http.Request{
if shouldRegex {
return buildRegexRequest(request, regexMap)
return g.buildRegexRequest(request)
} else {
return buildRegularRequest(request)
return g.buildRegularRequest(request)
}
}

func buildRegexRequest(request GoltRequest, extractorMap map[string]string) *http.Request{
payloadString := generatePayload(request, extractorMap)
func (g *GoltGenerator) buildRegexRequest(request GoltRequest) *http.Request{
payloadString := g.generatePayload(request)
payload := []byte(payloadString)

req, _ := http.NewRequest(request.Method, request.URL, bytes.NewBuffer(payload))

headers := generateHeaders(request, extractorMap)
headers := g.generateHeaders(request)
for k, v := range headers {
req.Header.Set(k, *v)
}
return req
}

func buildRegularRequest(request GoltRequest) *http.Request {
func (g *GoltGenerator) buildRegularRequest(request GoltRequest) *http.Request {
payload := []byte(request.Payload)
req, _ := http.NewRequest(request.Method, request.URL, bytes.NewBuffer(payload))
for k, v := range request.Headers {
Expand All @@ -40,29 +44,29 @@ func buildRegularRequest(request GoltRequest) *http.Request {
return req
}

func generatePayload(request GoltRequest, extractorMap map[string]string) (string) {
func (g *GoltGenerator) generatePayload(request GoltRequest) (string) {
// We are passing the pointer of the Payload to modify it's value
replaceRegex(r, &request.Payload, extractorMap)
g.replaceRegex(r, &request.Payload)
return request.Payload
}

func generateHeaders(request GoltRequest, extractorMap map[string]string) map[string]*string {
func (g *GoltGenerator) generateHeaders(request GoltRequest) map[string]*string {
for k := range request.Headers {
// We are passing a pointer of the value in the map to replace it's value
replaceRegex(r, request.Headers[k], extractorMap)
g.replaceRegex(r, request.Headers[k])
}
return request.Headers
}

func replaceRegex(regex *regexp.Regexp, value *string, extractorMap map[string]string) {
func (g *GoltGenerator) replaceRegex(regex *regexp.Regexp, value *string) {
/*
Given a specific regular expression, a pointer to a string and a map of stored variable
This method will have the side effect of changing the value pointer by the string if the regex is matching
*/
if regex.MatchString(*value) {
for _, foundMatch := range regex.FindAllString(*value, -1) {
mapKey := foundMatch[2:len(foundMatch)-1]
extractedValue := extractorMap[mapKey]
extractedValue := g.RegexMap[mapKey]
*value = strings.Replace(*value, foundMatch, extractedValue, -1)
}
}
Expand Down
13 changes: 10 additions & 3 deletions http_request_generator_test.go → generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ import (
"io/ioutil"
)

const testKey1, testValue1 = "TESTING_KEY_1", "TESTING_VALUE_1"
const testKey2, testValue2 = "TESTING_KEY_2", "TESTING_VALUE_2"
var generator *GoltGenerator

func init() {
testingMap = make(map[string]string)
testingMap := make(map[string]string)
testingMap[testKey1] = testValue1
testingMap[testKey2] = testValue2
generator = &GoltGenerator{
RegexMap: testingMap,
}
}

func TestBuildRegexRequest(t *testing.T) {
Expand All @@ -19,7 +26,7 @@ func TestBuildRegexRequest(t *testing.T) {
Headers: map[string]*string{testHeader: &headerValue},
}

httpRequest := buildRegexRequest(request, testingMap)
httpRequest := generator.buildRegexRequest(request)

body, _ := ioutil.ReadAll(httpRequest.Body)
if string(body) != testValue1 {
Expand All @@ -36,7 +43,7 @@ func TestBuildRequest(t *testing.T) {
Payload: testPayload,
Headers: map[string]*string{testHeaderKey: &testHeaderValue},
}
httpRequest := buildRegularRequest(request)
httpRequest := generator.buildRegularRequest(request)
body, _ := ioutil.ReadAll(httpRequest.Body)
if string(body) != testPayload {
t.Fail()
Expand Down

0 comments on commit a2514cb

Please sign in to comment.