Skip to content

Commit

Permalink
feat: named api test scenario runs
Browse files Browse the repository at this point in the history
  • Loading branch information
pnmcosta committed Jun 28, 2023
1 parent 48b6ca9 commit d0154c1
Showing 1 changed file with 100 additions and 98 deletions.
198 changes: 100 additions & 98 deletions tests/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,124 +43,126 @@ type ApiScenario struct {

// Test executes the test case/scenario.
func (scenario *ApiScenario) Test(t *testing.T) {
var testApp *TestApp
var testAppErr error
if scenario.TestAppFactory != nil {
testApp, testAppErr = scenario.TestAppFactory()
} else {
testApp, testAppErr = NewTestApp()
}
if testAppErr != nil {
t.Fatalf("Failed to initialize the test app instance: %v", testAppErr)
}
defer testApp.Cleanup()

e, err := apis.InitApi(testApp)
if err != nil {
t.Fatal(err)
}

if scenario.BeforeTestFunc != nil {
scenario.BeforeTestFunc(t, testApp, e)
}

recorder := httptest.NewRecorder()
req := httptest.NewRequest(scenario.Method, scenario.Url, scenario.Body)

// add middleware to timeout long-running requests (eg. keep-alive routes)
e.Pre(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctx, cancelFunc := context.WithTimeout(c.Request().Context(), 100*time.Millisecond)
defer cancelFunc()
c.SetRequest(c.Request().Clone(ctx))
return next(c)
t.Run(scenario.Name, func(t *testing.T) {
var testApp *TestApp
var testAppErr error
if scenario.TestAppFactory != nil {
testApp, testAppErr = scenario.TestAppFactory()
} else {
testApp, testAppErr = NewTestApp()
}
})
if testAppErr != nil {
t.Fatalf("Failed to initialize the test app instance: %v", testAppErr)
}
defer testApp.Cleanup()

// set default header
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
e, err := apis.InitApi(testApp)
if err != nil {
t.Fatal(err)
}

if scenario.BeforeTestFunc != nil {
scenario.BeforeTestFunc(t, testApp, e)
}

// set scenario headers
for k, v := range scenario.RequestHeaders {
req.Header.Set(k, v)
}
recorder := httptest.NewRecorder()
req := httptest.NewRequest(scenario.Method, scenario.Url, scenario.Body)

// execute request
e.ServeHTTP(recorder, req)
// add middleware to timeout long-running requests (eg. keep-alive routes)
e.Pre(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctx, cancelFunc := context.WithTimeout(c.Request().Context(), 100*time.Millisecond)
defer cancelFunc()
c.SetRequest(c.Request().Clone(ctx))
return next(c)
}
})

res := recorder.Result()
// set default header
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)

var prefix = scenario.Name
if prefix == "" {
prefix = fmt.Sprintf("%s:%s", scenario.Method, scenario.Url)
}
// set scenario headers
for k, v := range scenario.RequestHeaders {
req.Header.Set(k, v)
}

if res.StatusCode != scenario.ExpectedStatus {
t.Errorf("[%s] Expected status code %d, got %d", prefix, scenario.ExpectedStatus, res.StatusCode)
}
// execute request
e.ServeHTTP(recorder, req)

if scenario.Delay > 0 {
time.Sleep(scenario.Delay)
}
res := recorder.Result()

if len(scenario.ExpectedContent) == 0 && len(scenario.NotExpectedContent) == 0 {
if len(recorder.Body.Bytes()) != 0 {
t.Errorf("[%s] Expected empty body, got \n%v", prefix, recorder.Body.String())
var prefix = scenario.Name
if prefix == "" {
prefix = fmt.Sprintf("%s:%s", scenario.Method, scenario.Url)
}
} else {
// normalize json response format
buffer := new(bytes.Buffer)
err := json.Compact(buffer, recorder.Body.Bytes())
var normalizedBody string
if err != nil {
// not a json...
normalizedBody = recorder.Body.String()
} else {
normalizedBody = buffer.String()

if res.StatusCode != scenario.ExpectedStatus {
t.Errorf("[%s] Expected status code %d, got %d", prefix, scenario.ExpectedStatus, res.StatusCode)
}

for _, item := range scenario.ExpectedContent {
if !strings.Contains(normalizedBody, item) {
t.Errorf("[%s] Cannot find %v in response body \n%v", prefix, item, normalizedBody)
break
}
if scenario.Delay > 0 {
time.Sleep(scenario.Delay)
}

for _, item := range scenario.NotExpectedContent {
if strings.Contains(normalizedBody, item) {
t.Errorf("[%s] Didn't expect %v in response body \n%v", prefix, item, normalizedBody)
break
if len(scenario.ExpectedContent) == 0 && len(scenario.NotExpectedContent) == 0 {
if len(recorder.Body.Bytes()) != 0 {
t.Errorf("[%s] Expected empty body, got \n%v", prefix, recorder.Body.String())
}
} else {
// normalize json response format
buffer := new(bytes.Buffer)
err := json.Compact(buffer, recorder.Body.Bytes())
var normalizedBody string
if err != nil {
// not a json...
normalizedBody = recorder.Body.String()
} else {
normalizedBody = buffer.String()
}
}
}

// to minimize the breaking changes we always expect the error
// events to be called on API error
if res.StatusCode >= 400 {
if scenario.ExpectedEvents == nil {
scenario.ExpectedEvents = map[string]int{}
}
if _, ok := scenario.ExpectedEvents["OnBeforeApiError"]; !ok {
scenario.ExpectedEvents["OnBeforeApiError"] = 1
for _, item := range scenario.ExpectedContent {
if !strings.Contains(normalizedBody, item) {
t.Errorf("[%s] Cannot find %v in response body \n%v", prefix, item, normalizedBody)
break
}
}

for _, item := range scenario.NotExpectedContent {
if strings.Contains(normalizedBody, item) {
t.Errorf("[%s] Didn't expect %v in response body \n%v", prefix, item, normalizedBody)
break
}
}
}
if _, ok := scenario.ExpectedEvents["OnAfterApiError"]; !ok {
scenario.ExpectedEvents["OnAfterApiError"] = 1

// to minimize the breaking changes we always expect the error
// events to be called on API error
if res.StatusCode >= 400 {
if scenario.ExpectedEvents == nil {
scenario.ExpectedEvents = map[string]int{}
}
if _, ok := scenario.ExpectedEvents["OnBeforeApiError"]; !ok {
scenario.ExpectedEvents["OnBeforeApiError"] = 1
}
if _, ok := scenario.ExpectedEvents["OnAfterApiError"]; !ok {
scenario.ExpectedEvents["OnAfterApiError"] = 1
}
}
}

if len(testApp.EventCalls) > len(scenario.ExpectedEvents) {
t.Errorf("[%s] Expected events %v, got %v", prefix, scenario.ExpectedEvents, testApp.EventCalls)
}
if len(testApp.EventCalls) > len(scenario.ExpectedEvents) {
t.Errorf("[%s] Expected events %v, got %v", prefix, scenario.ExpectedEvents, testApp.EventCalls)
}

for event, expectedCalls := range scenario.ExpectedEvents {
actualCalls := testApp.EventCalls[event]
if actualCalls != expectedCalls {
t.Errorf("[%s] Expected event %s to be called %d, got %d", prefix, event, expectedCalls, actualCalls)
for event, expectedCalls := range scenario.ExpectedEvents {
actualCalls := testApp.EventCalls[event]
if actualCalls != expectedCalls {
t.Errorf("[%s] Expected event %s to be called %d, got %d", prefix, event, expectedCalls, actualCalls)
}
}
}

// @todo consider adding the response body to the AfterTestFunc args
if scenario.AfterTestFunc != nil {
scenario.AfterTestFunc(t, testApp, e)
}
// @todo consider adding the response body to the AfterTestFunc args
if scenario.AfterTestFunc != nil {
scenario.AfterTestFunc(t, testApp, e)
}
})
}

0 comments on commit d0154c1

Please sign in to comment.