Add annotations to routes and apps (#866)

Adds 'annotations' attribute to Routes and Apps
This commit is contained in:
Owen Cliffe
2018-03-20 18:02:49 +00:00
committed by GitHub
parent 845f40ee86
commit d25b5af59d
31 changed files with 1838 additions and 669 deletions

View File

@@ -2,12 +2,15 @@ FN integration API tests
======================================
These are tests that can either run locally against the current codebase (e.g. in an IDE) or remotely against a running Fn instance.
Test dependencies
-----------------
```bash
DOCKER_HOST - for building images
FN_API_URL - Fn API endpoint
FN_API_URL - Fn API endpoint - leave this unset to test using the local codebase
```
How to run tests?

View File

@@ -0,0 +1,84 @@
package tests
import (
"fmt"
"reflect"
"strings"
)
// common test cases around annotations (shared by any objects that support it)
const (
maxAnnotationKeys = 100
maxAnnotationValueSize = 512
maxAnnotationKeySize = 128
)
var emptyAnnMap = map[string]interface{}{}
func makeAnnMap(size int) map[string]interface{} {
md := make(map[string]interface{}, size)
for i := 0; i < size; i++ {
md[fmt.Sprintf("k-%d", i)] = "val"
}
return md
}
var createAnnotationsValidCases = []struct {
name string
annotations map[string]interface{}
}{
{"valid_string", map[string]interface{}{"key": "value"}},
{"valid_array", map[string]interface{}{"key": []interface{}{"value1", "value2"}}},
{"valid_object", map[string]interface{}{"key": map[string]interface{}{"foo": "bar"}}},
{"max_value_size", map[string]interface{}{"key": strings.Repeat("a", maxAnnotationValueSize-2)}},
{"max_key_size", map[string]interface{}{strings.Repeat("a", maxAnnotationKeySize): "val"}},
{"max_map_size", makeAnnMap(maxAnnotationKeys)},
}
var createAnnotationsErrorCases = []struct {
name string
annotations map[string]interface{}
}{
{"value_too_long", map[string]interface{}{"key": strings.Repeat("a", maxAnnotationValueSize-1)}},
{"key_too_long", map[string]interface{}{strings.Repeat("a", maxAnnotationKeySize+1): "value"}},
{"whitespace_in_key", map[string]interface{}{" bad key ": "value"}},
{"too_many_keys", makeAnnMap(maxAnnotationKeys + 1)},
}
var updateAnnotationsValidCases = []struct {
name string
initial map[string]interface{}
change map[string]interface{}
expected map[string]interface{}
}{
{"overwrite_existing_annotation_keys", map[string]interface{}{"key": "value1"}, map[string]interface{}{"key": "value2"}, map[string]interface{}{"key": "value2"}},
{"delete_annotation_key", map[string]interface{}{"key": "value1"}, map[string]interface{}{"key": ""}, map[string]interface{}{}},
{"set_to_max_size_with_deletes", map[string]interface{}{"key": "value1"}, func() map[string]interface{} {
md := makeAnnMap(100)
md["key"] = ""
return md
}(), makeAnnMap(100)},
{"noop_with_max_keys", makeAnnMap(maxAnnotationKeys), emptyAnnMap, makeAnnMap(maxAnnotationKeys)},
}
var updateAnnotationsErrorCases = []struct {
name string
initial map[string]interface{}
change map[string]interface{}
}{
{"too_many_key_after_update", makeAnnMap(100), map[string]interface{}{"key": "value1"}},
{"value_too_long", map[string]interface{}{}, map[string]interface{}{"key": strings.Repeat("a", maxAnnotationValueSize-1)}},
{"key_too_long", map[string]interface{}{}, map[string]interface{}{strings.Repeat("a", maxAnnotationKeySize+1): "value"}},
{"whitespace_in_key", map[string]interface{}{}, map[string]interface{}{" bad key ": "value"}},
{"too_many_keys_in_update", map[string]interface{}{}, makeAnnMap(maxAnnotationKeys + 1)},
}
//AnnotationsEquivalent checks if two annotations maps are semantically equivalent, including nil == empty map
func AnnotationsEquivalent(md1, md2 map[string]interface{}) bool {
if len(md1) == 0 && len(md2) == 0 {
return true
}
return reflect.DeepEqual(md1, md2)
}

View File

@@ -2,6 +2,7 @@ package tests
import (
"context"
"log"
"strings"
"testing"
"time"
@@ -11,117 +12,57 @@ import (
"github.com/fnproject/fn_go/models"
)
func CheckAppResponseError(t *testing.T, e error) {
if e != nil {
switch err := e.(type) {
case *apps.DeleteAppsAppNotFound:
t.Errorf("Unexpected error occurred: %v Original Location: %s", err.Payload.Error.Message, MyCaller())
t.FailNow()
case *apps.DeleteAppsAppDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v Orig Location: %s", err.Payload.Error.Message, err.Code(), MyCaller())
t.FailNow()
case *apps.PostAppsDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v Orig Location: %s", err.Payload.Error.Message, err.Code(), MyCaller())
t.FailNow()
case *apps.GetAppsAppNotFound:
if !strings.Contains("App not found", err.Payload.Error.Message) {
t.Errorf("Unexpected error occurred: %v Original Location: %s", err.Payload.Error.Message, MyCaller())
t.FailNow()
}
case *apps.GetAppsAppDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v Orig Location: %s", err.Payload.Error.Message, err.Code(), MyCaller())
t.FailNow()
case *apps.PatchAppsAppDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v Orig Location: %s", err.Payload.Error.Message, err.Code(), MyCaller())
t.FailNow()
case *apps.PatchAppsAppNotFound:
t.Errorf("Unexpected error occurred: %v. Original Location: %s", err.Payload.Error.Message, MyCaller())
t.FailNow()
case *apps.PatchAppsAppBadRequest:
t.Errorf("Unexpected error occurred: %v. Original Location: %s", err.Payload.Error.Message, MyCaller())
t.FailNow()
default:
t.Errorf("Unable to determine type of error: %s Original Location: %s", err, MyCaller())
t.FailNow()
}
}
}
func CreateAppNoAssert(ctx context.Context, fnclient *client.Fn, appName string, config map[string]string) (*apps.PostAppsOK, error) {
// PostApp creates an app and esures it is deleted on teardown if it was created
func (s *TestHarness) PostApp(app *models.App) (*apps.PostAppsOK, error) {
cfg := &apps.PostAppsParams{
Body: &models.AppWrapper{
App: &models.App{
Config: config,
Name: appName,
},
App: app,
},
Context: ctx,
Context: s.Context,
}
ok, err := fnclient.Apps.PostApps(cfg)
ok, err := s.Client.Apps.PostApps(cfg)
if err == nil {
approutesLock.Lock()
_, got := appsandroutes[appName]
if !got {
appsandroutes[appName] = []string{}
}
approutesLock.Unlock()
s.createdApps[ok.Payload.App.Name] = true
}
return ok, err
}
func CreateApp(t *testing.T, ctx context.Context, fnclient *client.Fn, appName string, config map[string]string) {
appPayload, err := CreateAppNoAssert(ctx, fnclient, appName, config)
CheckAppResponseError(t, err)
if !strings.Contains(appName, appPayload.Payload.App.Name) {
t.Errorf("App name mismatch.\nExpected: %v\nActual: %v",
appName, appPayload.Payload.App.Name)
// GivenAppExists creates an app and ensures it is deleted on teardown, this fatals if the app is not created
func (s *TestHarness) GivenAppExists(t *testing.T, app *models.App) {
appPayload, err := s.PostApp(app)
if err != nil {
t.Fatalf("Failed to create app %v", app)
}
if !strings.Contains(app.Name, appPayload.Payload.App.Name) {
t.Fatalf("App name mismatch.\nExpected: %v\nActual: %v",
app.Name, appPayload.Payload.App.Name)
}
}
func CreateUpdateApp(t *testing.T, ctx context.Context, fnclient *client.Fn, appName string, config map[string]string) *apps.PatchAppsAppOK {
CreateApp(t, ctx, fnclient, appName, map[string]string{"A": "a"})
cfg := &apps.PatchAppsAppParams{
App: appName,
Body: &models.AppWrapper{
App: &models.App{
Config: config,
Name: "",
},
},
Context: ctx,
// AppMustExist fails the test if the specified app does not exist
func (s *TestHarness) AppMustExist(t *testing.T, appName string) *models.App {
app, err := s.Client.Apps.GetAppsApp(&apps.GetAppsAppParams{
App: s.AppName,
Context: s.Context,
})
if err != nil {
t.Fatalf("Expected new route to create app got %v", err)
return nil
}
appPayload, err := fnclient.Apps.PatchAppsApp(cfg)
CheckAppResponseError(t, err)
return appPayload
}
func DeleteApp(t *testing.T, ctx context.Context, fnclient *client.Fn, appName string) {
cfg := &apps.DeleteAppsAppParams{
App: appName,
Context: ctx,
}
_, err := fnclient.Apps.DeleteAppsApp(cfg)
CheckAppResponseError(t, err)
}
func GetApp(t *testing.T, ctx context.Context, fnclient *client.Fn, appName string) *models.App {
cfg := &apps.GetAppsAppParams{
App: appName,
Context: ctx,
}
app, err := fnclient.Apps.GetAppsApp(cfg)
CheckAppResponseError(t, err)
return app.Payload.App
}
func DeleteAppNoT(ctx context.Context, fnclient *client.Fn, appName string) {
func safeDeleteApp(ctx context.Context, fnclient *client.Fn, appName string) {
cfg := &apps.DeleteAppsAppParams{
App: appName,
Context: ctx,
}
cfg.WithTimeout(time.Second * 60)
fnclient.Apps.DeleteAppsApp(cfg)
_, err := fnclient.Apps.DeleteAppsApp(cfg)
if _, ok := err.(*apps.DeleteAppsAppNotFound); err != nil && !ok {
log.Printf("Error cleaning up app %s: %v", appName, err)
}
}

View File

@@ -1,126 +1,300 @@
package tests
import (
"github.com/fnproject/fn_go/client/apps"
"github.com/fnproject/fn_go/models"
"reflect"
"strings"
"testing"
"time"
"github.com/fnproject/fn_go/client/apps"
)
func TestAppDeleteNotFound(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
s := SetupHarness()
defer s.Cleanup()
cfg := &apps.DeleteAppsAppParams{
App: "missing-app",
Context: s.Context,
}
cfg.WithTimeout(time.Second * 60)
_, err := s.Client.Apps.DeleteAppsApp(cfg)
if err == nil {
if _, ok := err.(*apps.DeleteAppsAppNotFound); !ok {
t.Errorf("Error during app delete: we should get HTTP 404, but got: %s", err.Error())
}
}
func TestAppGetNotFound(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
s := SetupHarness()
defer s.Cleanup()
cfg := &apps.GetAppsAppParams{
App: "missing-app",
Context: s.Context,
}
cfg.WithTimeout(time.Second * 60)
_, err := s.Client.Apps.GetAppsApp(cfg)
CheckAppResponseError(t, err)
if _, ok := err.(*apps.GetAppsAppNotFound); !ok {
t.Errorf("Error during get: we should get HTTP 404, but got: %s", err.Error())
}
if !strings.Contains(err.(*apps.GetAppsAppNotFound).Payload.Error.Message, "App not found") {
t.Errorf("Error during app delete: unexpeted error `%s`, wanted `App not found`", err.Error())
}
}
func TestAppCreateNoConfigSuccess(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
DeleteApp(t, s.Context, s.Client, s.AppName)
s := SetupHarness()
defer s.Cleanup()
resp, err := s.PostApp(&models.App{
Name: s.AppName,
})
if err != nil {
t.Errorf("Failed to create simple app %v", err)
return
}
if resp.Payload.App.Name != s.AppName {
t.Errorf("app name in response %s does not match new app %s ", resp.Payload.App.Name, s.AppName)
}
}
func TestSetAppAnnotationsOnCreate(t *testing.T) {
t.Parallel()
for _, tci := range createAnnotationsValidCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("valid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
app, err := s.PostApp(&models.App{
Name: s.AppName,
Annotations: tc.annotations,
})
if err != nil {
t.Fatalf("Failed to create app with valid annotations %v got error %v", tc.annotations, err)
}
gotMd := app.Payload.App.Annotations
if !AnnotationsEquivalent(gotMd, tc.annotations) {
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.annotations)
}
getApp := s.AppMustExist(t, s.AppName)
if !AnnotationsEquivalent(getApp.Annotations, tc.annotations) {
t.Errorf("GET annotations '%v' does not match set annotations %v", getApp.Annotations, tc.annotations)
}
})
}
for _, tci := range createAnnotationsErrorCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("invalid_"+tc.name, func(ti *testing.T) {
ti.Parallel()
s := SetupHarness()
defer s.Cleanup()
_, err := s.PostApp(&models.App{
Name: s.AppName,
Annotations: tc.annotations,
})
if err == nil {
t.Fatalf("Created app with invalid annotations %v but expected error", tc.annotations)
}
if _, ok := err.(*apps.PostAppsBadRequest); !ok {
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
}
})
}
}
func TestUpdateAppAnnotationsOnPatch(t *testing.T) {
t.Parallel()
for _, tci := range updateAnnotationsValidCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("valid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
Annotations: tc.initial,
})
res, err := s.Client.Apps.PatchAppsApp(&apps.PatchAppsAppParams{
App: s.AppName,
Context: s.Context,
Body: &models.AppWrapper{
App: &models.App{
Annotations: tc.change,
},
},
})
if err != nil {
t.Fatalf("Failed to patch annotations with %v on app: %v", tc.change, err)
}
gotMd := res.Payload.App.Annotations
if !AnnotationsEquivalent(gotMd, tc.expected) {
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.expected)
}
getApp := s.AppMustExist(t, s.AppName)
if !AnnotationsEquivalent(getApp.Annotations, tc.expected) {
t.Errorf("GET annotations '%v' does not match set annotations %v", getApp.Annotations, tc.expected)
}
})
}
for _, tci := range updateAnnotationsErrorCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("invalid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
Annotations: tc.initial,
})
_, err := s.Client.Apps.PatchAppsApp(&apps.PatchAppsAppParams{
App: s.AppName,
Context: s.Context,
Body: &models.AppWrapper{
App: &models.App{
Annotations: tc.change,
},
},
})
if err == nil {
t.Errorf("patched app with invalid annotations %v but expected error", tc.change)
}
if _, ok := err.(*apps.PatchAppsAppBadRequest); !ok {
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
}
})
}
}
func TestAppCreateWithConfigSuccess(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
DeleteApp(t, s.Context, s.Client, s.AppName)
s := SetupHarness()
defer s.Cleanup()
validConfig := map[string]string{"A": "a"}
appPayload, err := s.PostApp(&models.App{
Name: s.AppName,
Config: validConfig,
})
if err != nil {
t.Fatalf("Failed to create app with valid config got %v", err)
}
if !reflect.DeepEqual(validConfig, appPayload.Payload.App.Config) {
t.Errorf("Expecting config %v but got %v in response", validConfig, appPayload.Payload.App.Config)
}
}
func TestAppInsect(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
app := GetApp(t, s.Context, s.Client, s.AppName)
val, ok := app.Config["A"]
if !ok {
t.Error("Error during app config inspect: config map misses required entity `A` with value `a`.")
s := SetupHarness()
defer s.Cleanup()
validConfig := map[string]string{"A": "a"}
s.GivenAppExists(t, &models.App{Name: s.AppName,
Config: validConfig})
appOk, err := s.Client.Apps.GetAppsApp(&apps.GetAppsAppParams{
App: s.AppName,
Context: s.Context,
})
if err != nil {
t.Fatalf("Expected valid response to get app, got %v", err)
}
if !strings.Contains("a", val) {
t.Errorf("App config value is different. Expected: `a`. Actual %v", val)
if !reflect.DeepEqual(validConfig, appOk.Payload.App.Config) {
t.Errorf("Returned config %v does not match requested config %v", appOk.Payload.App.Config, validConfig)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestAppPatchSameConfig(t *testing.T) {
func TestAppPatchConfig(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
config := map[string]string{
"A": "a",
for _, tci := range updateConfigCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
Config: tc.intialConfig,
})
patch, err := s.Client.Apps.PatchAppsApp(&apps.PatchAppsAppParams{
App: s.AppName,
Body: &models.AppWrapper{
App: &models.App{
Config: tc.change,
},
},
Context: s.Context,
})
if err != nil {
t.Fatalf("Failed to patch app with valid value %v, %v", tc.change, err)
}
if !ConfigEquivalent(patch.Payload.App.Config, tc.expected) {
t.Errorf("Expected returned app config to be %v, but was %v", tc.expected, patch.Payload.App.Config)
}
})
}
appUpdatePayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
_, ok := appUpdatePayload.Payload.App.Config["A"]
if !ok {
t.Error("Error during app update: config map misses required entity `A` with value `a`.")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestAppPatchOverwriteConfig(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
config := map[string]string{
"A": "b",
}
appPayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
val, ok := appPayload.Payload.App.Config["A"]
if !ok {
t.Error("Error during app config inspect: config map misses required entity `A` with value `a`.")
}
if !strings.Contains("b", val) {
t.Errorf("App config value is different. Expected: `b`. Actual %v", val)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestAppsPatchConfigAddValue(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
config := map[string]string{
"B": "b",
}
appPayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
val, ok := appPayload.Payload.App.Config["B"]
if !ok {
t.Error("Error during app config inspect: config map misses required entity `B` with value `b`.")
}
if !strings.Contains("b", val) {
t.Errorf("App config value is different. Expected: `b`. Actual %v", val)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestAppDuplicate(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
_, err := CreateAppNoAssert(s.Context, s.Client, s.AppName, map[string]string{})
if reflect.TypeOf(err) != reflect.TypeOf(apps.NewPostAppsConflict()) {
CheckAppResponseError(t, err)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PostApp(&models.App{Name: s.AppName})
if _, ok := err.(*apps.PostAppsConflict); !ok {
t.Errorf("Expecting conflict response on duplicate app, got %v", err)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}

View File

@@ -8,11 +8,12 @@ import (
"time"
"github.com/fnproject/fn_go/client/call"
"github.com/fnproject/fn_go/models"
)
func TestCallsMissingApp(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
s := SetupHarness()
cfg := &call.GetAppsAppCallsParams{
App: s.AppName,
Path: &s.RoutePath,
@@ -26,10 +27,11 @@ func TestCallsMissingApp(t *testing.T) {
func TestCallsDummy(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
cfg := &call.GetAppsAppCallsCallParams{
Call: "dummy",
@@ -42,15 +44,15 @@ func TestCallsDummy(t *testing.T) {
t.Error("Must fail because `dummy` call does not exist.")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestGetExactCall(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
u := url.URL{
Scheme: "http",
@@ -76,5 +78,4 @@ func TestGetExactCall(t *testing.T) {
t.Error(retryErr.Error())
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}

View File

@@ -0,0 +1,29 @@
package tests
import "reflect"
// common test cases around config for apps/routes
var updateConfigCases = []struct {
name string
intialConfig map[string]string
change map[string]string
expected map[string]string
}{
{"preserve existing config keys with nop", map[string]string{"key": "value1"}, map[string]string{}, map[string]string{"key": "value1"}},
{"preserve existing config keys with change", map[string]string{"key": "value1"}, map[string]string{"key": "value1"}, map[string]string{"key": "value1"}},
{"overwrite existing config keys", map[string]string{"key": "value1"}, map[string]string{"key": "value2"}, map[string]string{"key": "value2"}},
{"delete config key", map[string]string{"key": "value1"}, map[string]string{"key": ""}, map[string]string{}},
}
//ConfigEquivalent checks if two config objects are semantically equivalent (including nils)
func ConfigEquivalent(a map[string]string, b map[string]string) bool {
if len(a) == 0 && len(b) == 0 {
return true
}
return reflect.DeepEqual(a, b)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/fnproject/fn_go/client/call"
"github.com/fnproject/fn_go/client/operations"
"github.com/fnproject/fn_go/models"
)
func CallAsync(t *testing.T, u url.URL, content io.Reader) string {
@@ -58,10 +59,13 @@ func CallSync(t *testing.T, u url.URL, content io.Reader) string {
func TestCanCallfunction(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
@@ -79,15 +83,15 @@ func TestCanCallfunction(t *testing.T) {
if !strings.Contains(expectedOutput, output.String()) {
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCallOutputMatch(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
@@ -108,16 +112,17 @@ func TestCallOutputMatch(t *testing.T) {
if !strings.Contains(expectedOutput, output.String()) {
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCanCallAsync(t *testing.T) {
newRouteType := "async"
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
@@ -125,25 +130,22 @@ func TestCanCallAsync(t *testing.T) {
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
_, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType, s.Format,
s.Memory, s.RouteConfig, s.RouteHeaders, "")
CheckRouteResponseError(t, err)
s.GivenRoutePatched(t, s.AppName, s.RoutePath, &models.Route{
Type: newRouteType,
})
CallAsync(t, u, &bytes.Buffer{})
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCanGetAsyncState(t *testing.T) {
newRouteType := "async"
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
@@ -151,13 +153,9 @@ func TestCanGetAsyncState(t *testing.T) {
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
_, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType, s.Format,
s.Memory, s.RouteConfig, s.RouteHeaders, "")
CheckRouteResponseError(t, err)
s.GivenRoutePatched(t, s.AppName, rt.Path, &models.Route{
Type: newRouteType,
})
callID := CallAsync(t, u, &bytes.Buffer{})
cfg := &call.GetAppsAppCallsCallParams{
@@ -198,26 +196,27 @@ func TestCanGetAsyncState(t *testing.T) {
t.Errorf("Call object status mismatch.\n\tExpected: %v\n\tActual:%v", "success", callObject.Status)
}
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCanCauseTimeout(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
routePath := "/" + RandStringBytes(10)
image := "funcy/timeout:0.0.1"
routeType := "sync"
s := SetupHarness()
defer s.Cleanup()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.Format, int32(10), s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
timeout := int32(10)
rt.Timeout = &timeout
rt.Type = "sync"
rt.Image = "funcy/timeout:0.0.1"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
Host: Host(),
}
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
content := &bytes.Buffer{}
json.NewEncoder(content).Encode(struct {
@@ -254,24 +253,24 @@ func TestCanCauseTimeout(t *testing.T) {
"output", "callObj.Payload.Call.Status")
}
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCallResponseHeadersMatch(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
routePath := "/os.environ"
image := "denismakogon/os.environ"
routeType := "sync"
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Image = "denismakogon/os.environ"
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
Host: Host(),
}
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
content := &bytes.Buffer{}
output := &bytes.Buffer{}
CallFN(u.String(), content, output, "POST",
@@ -284,26 +283,26 @@ func TestCallResponseHeadersMatch(t *testing.T) {
t.Errorf("HEADER_ACCEPT='application/xml, application/json; q=0.2' "+
"should be in output, have:%s\n", res)
}
DeleteRoute(t, s.Context, s.Client, s.AppName, routePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCanWriteLogs(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
routePath := "/log"
image := "funcy/log:0.0.1"
routeType := "sync"
s := SetupHarness()
defer s.Cleanup()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
rt := s.BasicRoute()
rt.Path = "/log"
rt.Image = "funcy/log:0.0.1"
rt.Type = "sync"
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
Host: Host(),
}
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
content := &bytes.Buffer{}
json.NewEncoder(content).Encode(struct {
Size int
@@ -323,26 +322,27 @@ func TestCanWriteLogs(t *testing.T) {
t.Error(err.Error())
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestOversizedLog(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
routePath := "/log"
image := "funcy/log:0.0.1"
routeType := "sync"
s := SetupHarness()
defer s.Cleanup()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
rt := s.BasicRoute()
rt.Path = "/log"
rt.Image = "funcy/log:0.0.1"
rt.Type = "sync"
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, rt)
size := 1 * 1024 * 1024 * 1024
u := url.URL{
Scheme: "http",
Host: Host(),
}
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
content := &bytes.Buffer{}
json.NewEncoder(content).Encode(struct {
Size int
@@ -366,5 +366,4 @@ func TestOversizedLog(t *testing.T) {
size/1024, len(log))
}
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}

View File

@@ -3,6 +3,7 @@ package tests
import (
"bytes"
"encoding/json"
"github.com/fnproject/fn_go/models"
"net/url"
"path"
"strconv"
@@ -16,16 +17,16 @@ type JSONResponse struct {
func TestFnJSONFormats(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
s := SetupHarness()
defer s.Cleanup()
// TODO(treeder): put image in fnproject @ dockerhub
image := "denismakogon/test-hot-json-go:0.0.1"
format := "json"
route := "/test-hot-json-go"
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, route, image, "sync",
format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Image = "denismakogon/test-hot-json-go:0.0.1"
rt.Format = "json"
s.GivenRouteExists(t, s.AppName, rt)
u := url.URL{
Scheme: "http",
@@ -63,5 +64,4 @@ func TestFnJSONFormats(t *testing.T) {
}
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}

View File

@@ -8,10 +8,8 @@ import (
func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
s := SetupDefaultSuite()
result := m.Run()
Cleanup()
s.Cancel()
if result == 0 {
fmt.Fprintln(os.Stdout, "😀 👍 🎗")
}

View File

@@ -1,228 +1,97 @@
package tests
import (
"context"
"testing"
"github.com/fnproject/fn_go/client"
"github.com/fnproject/fn_go/client/routes"
"github.com/fnproject/fn_go/models"
)
func CheckRouteResponseError(t *testing.T, e error) {
if e != nil {
switch err := e.(type) {
case *routes.PostAppsAppRoutesDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v", err.Payload.Error.Message, err.Code())
t.FailNow()
case *routes.PostAppsAppRoutesBadRequest:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.PostAppsAppRoutesConflict:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.GetAppsAppRoutesRouteNotFound:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.GetAppsAppRoutesRouteDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v", err.Payload.Error.Message, err.Code())
t.FailNow()
case *routes.DeleteAppsAppRoutesRouteNotFound:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.DeleteAppsAppRoutesRouteDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v", err.Payload.Error.Message, err.Code())
t.FailNow()
case *routes.GetAppsAppRoutesNotFound:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.GetAppsAppRoutesDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v", err.Payload.Error.Message, err.Code())
t.FailNow()
case *routes.PatchAppsAppRoutesRouteBadRequest:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.PatchAppsAppRoutesRouteNotFound:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
t.FailNow()
case *routes.PatchAppsAppRoutesRouteDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v", err.Payload.Error.Message, err.Code())
case *routes.PutAppsAppRoutesRouteBadRequest:
t.Errorf("Unexpected error occurred: %v.", err.Payload.Error.Message)
case *routes.PutAppsAppRoutesRouteDefault:
t.Errorf("Unexpected error occurred: %v. Status code: %v", err.Payload.Error.Message, err.Code())
t.FailNow()
default:
t.Errorf("Unable to determine type of error: %s", err)
t.FailNow()
}
}
}
func AssertRouteMatches(t *testing.T, expected *models.Route, got *models.Route) {
func assertRouteFields(t *testing.T, routeObject *models.Route, path, image, routeType, routeFormat string) {
rPath := routeObject.Path
rImage := routeObject.Image
rType := routeObject.Type
rTimeout := *routeObject.Timeout
rIdleTimeout := *routeObject.IDLETimeout
rFormat := routeObject.Format
if rPath != path {
t.Errorf("Route path mismatch. Expected: %v. Actual: %v", path, rPath)
if expected.Path != got.Path {
t.Errorf("Route path mismatch. Expected: %v. Actual: %v", expected.Path, got.Path)
}
if rImage != image {
t.Errorf("Route image mismatch. Expected: %v. Actual: %v", image, rImage)
if expected.Image != got.Image {
t.Errorf("Route image mismatch. Expected: %v. Actual: %v", expected.Image, got.Image)
}
if rType != routeType {
t.Errorf("Route type mismatch. Expected: %v. Actual: %v", routeType, rType)
if expected.Image != got.Image {
t.Errorf("Route type mismatch. Expected: %v. Actual: %v", expected.Image, got.Image)
}
if rTimeout == 0 {
t.Error("Route timeout should have default value of 30 seconds, but got 0 seconds")
}
if rIdleTimeout == 0 {
t.Error("Route idle timeout should have default value of 30 seconds, but got 0 seconds")
}
if rFormat != routeFormat {
t.Errorf("Route format mismatch. Expected: %v. Actual: %v", routeFormat, rFormat)
if expected.Format != got.Format {
t.Errorf("Route format mismatch. Expected: %v. Actual: %v", expected.Format, got.Format)
}
}
func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, routePath, routeType, routeFormat string, timeout, idleTimeout int32, routeConfig map[string]string, headers map[string][]string) (*routes.PostAppsAppRoutesOK, error) {
// PostRoute Creates a route and deletes the corresponding app (if created) on teardown
func (s *TestHarness) PostRoute(appName string, route *models.Route) (*routes.PostAppsAppRoutesOK, error) {
cfg := &routes.PostAppsAppRoutesParams{
App: appName,
Body: &models.RouteWrapper{
Route: &models.Route{
Config: routeConfig,
Headers: headers,
Image: image,
Path: routePath,
Type: routeType,
Format: routeFormat,
Timeout: &timeout,
IDLETimeout: &idleTimeout,
},
Route: route,
},
Context: ctx,
Context: s.Context,
}
ok, err := fnclient.Routes.PostAppsAppRoutes(cfg)
ok, err := s.Client.Routes.PostAppsAppRoutes(cfg)
if err == nil {
approutesLock.Lock()
r, got := appsandroutes[appName]
if got {
appsandroutes[appName] = append(r, routePath)
} else {
appsandroutes[appName] = []string{routePath}
}
approutesLock.Unlock()
s.createdApps[appName] = true
}
return ok, err
}
func CreateRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath, image, routeType, routeFormat string, timeout, idleTimeout int32, routeConfig map[string]string, headers map[string][]string) {
routeResponse, err := createRoute(ctx, fnclient, appName, image, routePath, routeType, routeFormat, timeout, idleTimeout, routeConfig, headers)
CheckRouteResponseError(t, err)
assertRouteFields(t, routeResponse.Payload.Route, routePath, image, routeType, routeFormat)
func (s *TestHarness) BasicRoute() *models.Route {
return &models.Route{
Format: s.Format,
Path: s.RoutePath,
Image: s.Image,
Type: s.RouteType,
Timeout: &s.Timeout,
IDLETimeout: &s.IdleTimeout,
}
}
func deleteRoute(ctx context.Context, fnclient *client.Fn, appName, routePath string) (*routes.DeleteAppsAppRoutesRouteOK, error) {
cfg := &routes.DeleteAppsAppRoutesRouteParams{
App: appName,
Route: routePath,
Context: ctx,
//GivenRouteExists creates a route using the specified arguments, failing the test if the creation fails, this tears down any apps that are created when the test is complete
func (s *TestHarness) GivenRouteExists(t *testing.T, appName string, route *models.Route) {
_, err := s.PostRoute(appName, route)
if err != nil {
t.Fatalf("Expected route to be created, got %v", err)
}
return fnclient.Routes.DeleteAppsAppRoutesRoute(cfg)
}
func DeleteRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath string) {
_, err := deleteRoute(ctx, fnclient, appName, routePath)
CheckRouteResponseError(t, err)
}
func ListRoutes(t *testing.T, ctx context.Context, fnclient *client.Fn, appName string) []*models.Route {
cfg := &routes.GetAppsAppRoutesParams{
App: appName,
Context: ctx,
}
routesResponse, err := fnclient.Routes.GetAppsAppRoutes(cfg)
CheckRouteResponseError(t, err)
return routesResponse.Payload.Routes
}
func GetRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath string) *models.Route {
//RouteMustExist checks that a route exists, failing the test if it doesn't, returns the route
func (s *TestHarness) RouteMustExist(t *testing.T, appName string, routePath string) *models.Route {
cfg := &routes.GetAppsAppRoutesRouteParams{
App: appName,
Route: routePath[1:],
Context: ctx,
Context: s.Context,
}
routeResponse, err := fnclient.Routes.GetAppsAppRoutesRoute(cfg)
CheckRouteResponseError(t, err)
routeResponse, err := s.Client.Routes.GetAppsAppRoutesRoute(cfg)
if err != nil {
t.Fatalf("Expected route %s %s to exist but got %v", appName, routePath, err)
}
return routeResponse.Payload.Route
}
func UpdateRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath, image, routeType, format string, memory uint64, routeConfig map[string]string, headers map[string][]string, newRoutePath string) (*routes.PatchAppsAppRoutesRouteOK, error) {
//GivenRoutePatched applies a patch to a route, failing the test if this fails.
func (s *TestHarness) GivenRoutePatched(t *testing.T, appName, routeName string, rt *models.Route) {
routeObject := GetRoute(t, ctx, fnclient, appName, routePath)
if routeObject.Config == nil {
routeObject.Config = map[string]string{}
}
if routeObject.Headers == nil {
routeObject.Headers = map[string][]string{}
}
routeObject.Path = ""
if newRoutePath != "" {
routeObject.Path = newRoutePath
}
if routeConfig != nil {
for k, v := range routeConfig {
if string(k[0]) == "-" {
delete(routeObject.Config, string(k[1:]))
continue
}
routeObject.Config[k] = v
}
}
if headers != nil {
for k, v := range headers {
if string(k[0]) == "-" {
delete(routeObject.Headers, k)
continue
}
routeObject.Headers[k] = v
}
}
if image != "" {
routeObject.Image = image
}
if format != "" {
routeObject.Format = format
}
if routeType != "" {
routeObject.Type = routeType
}
if memory > 0 {
routeObject.Memory = memory
}
cfg := &routes.PatchAppsAppRoutesRouteParams{
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
App: appName,
Context: ctx,
Route: routeName,
Context: s.Context,
Body: &models.RouteWrapper{
Route: routeObject,
Route: rt,
},
Route: routePath,
}
})
return fnclient.Routes.PatchAppsAppRoutesRoute(cfg)
if err != nil {
t.Fatalf("Failed to patch route %s %s : %v", appName, routeName, err)
}
}
func assertContainsRoute(routeModels []*models.Route, expectedRoute string) bool {
@@ -234,24 +103,22 @@ func assertContainsRoute(routeModels []*models.Route, expectedRoute string) bool
return false
}
func DeployRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath, image, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) *models.Route {
//PutRoute creates a route via PUT, tearing down any apps that are created when the test is complete
func (s *TestHarness) PutRoute(appName string, routePath string, route *models.Route) (*routes.PutAppsAppRoutesRouteOK, error) {
cfg := &routes.PutAppsAppRoutesRouteParams{
App: appName,
Context: ctx,
Context: s.Context,
Route: routePath,
Body: &models.RouteWrapper{
Route: &models.Route{
Config: routeConfig,
Headers: headers,
Image: image,
Path: routePath,
Type: routeType,
Format: routeFormat,
},
Route: route,
},
}
route, err := fnclient.Routes.PutAppsAppRoutesRoute(cfg)
CheckRouteResponseError(t, err)
return route.Payload.Route
resp, err := s.Client.Routes.PutAppsAppRoutesRoute(cfg)
if err == nil {
s.createdApps[appName] = true
}
return resp, err
}

View File

@@ -6,206 +6,497 @@ import (
"reflect"
"github.com/fnproject/fn/api/id"
"github.com/fnproject/fn_go/client/apps"
"github.com/fnproject/fn_go/client/routes"
"github.com/fnproject/fn_go/models"
)
func TestCreateRouteEmptyType(t *testing.T) {
func TestShouldRejectEmptyRouteType(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
_, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PostRoute(s.AppName, &models.Route{
Path: s.RoutePath,
Image: s.Image,
Type: "v",
Format: s.Format,
})
if err == nil {
t.Errorf("Should fail with Invalid route Type.")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCanCreateRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
DeleteApp(t, s.Context, s.Client, s.AppName)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PostRoute(s.AppName, &models.Route{
Path: s.RoutePath,
Image: s.Image,
Format: s.Format,
})
if err != nil {
t.Errorf("expected route success, got %v", err)
}
// TODO validate route returned matches request
}
func TestListRoutes(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
if !assertContainsRoute(ListRoutes(t, s.Context, s.Client, s.AppName), s.RoutePath) {
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
cfg := &routes.GetAppsAppRoutesParams{
App: s.AppName,
Context: s.Context,
}
routesResponse, err := s.Client.Routes.GetAppsAppRoutes(cfg)
if err != nil {
t.Fatalf("Expecting list routes to be successful, got %v", err)
}
if !assertContainsRoute(routesResponse.Payload.Routes, s.RoutePath) {
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestInspectRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
rObjects := []*models.Route{GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)}
if !assertContainsRoute(rObjects, s.RoutePath) {
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
s.GivenAppExists(t, &models.App{Name: s.AppName})
newRt := s.BasicRoute()
s.GivenRouteExists(t, s.AppName, newRt)
resp, err := s.Client.Routes.GetAppsAppRoutesRoute(&routes.GetAppsAppRoutesRouteParams{
App: s.AppName,
Route: newRt.Path[1:],
Context: s.Context,
})
if err != nil {
t.Fatalf("Failed to get route %s, %v", s.RoutePath, err)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
gotRt := resp.Payload.Route
AssertRouteMatches(t, newRt, gotRt)
}
func TestCanUpdateRouteType(t *testing.T) {
newRouteType := "sync"
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
routeResp, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType, s.Format,
s.Memory, s.RouteConfig, s.RouteHeaders, "")
CheckRouteResponseError(t, err)
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, newRouteType, s.Format)
DeleteApp(t, s.Context, s.Client, s.AppName)
var validRouteUpdates = []struct {
name string
update *models.Route
extract func(*models.Route) interface{}
}{
{"route type (sync)", &models.Route{Type: "sync"}, func(m *models.Route) interface{} { return m.Type }},
{"route type (async)", &models.Route{Type: "async"}, func(m *models.Route) interface{} { return m.Type }},
{"format (json)", &models.Route{Format: "json"}, func(m *models.Route) interface{} { return m.Format }},
{"format (default)", &models.Route{Format: "default"}, func(m *models.Route) interface{} { return m.Format }},
// ...
}
func TestCanUpdateRouteConfig(t *testing.T) {
func TestCanUpdateRouteAttributes(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
newRouteConf := map[string]string{
"A": "a",
for _, tci := range validRouteUpdates {
tc := tci
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
routeResp, err := s.Client.Routes.PatchAppsAppRoutesRoute(
&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Context: s.Context,
Route: s.RoutePath,
Body: &models.RouteWrapper{
Route: tc.update,
},
},
)
if err != nil {
t.Fatalf("Failed to patch route, got %v", err)
}
got := tc.extract(routeResp.Payload.Route)
change := tc.extract(tc.update)
if !reflect.DeepEqual(got, change) {
t.Errorf("Expected value in response tobe %v but was %v", change, got)
}
})
}
routeResp, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, s.RouteType, s.Format,
s.Memory, newRouteConf, s.RouteHeaders, "")
}
CheckRouteResponseError(t, err)
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, s.RouteType, s.Format)
func TestRoutePatchConfig(t *testing.T) {
t.Parallel()
for _, tci := range updateConfigCases {
tc := tci
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
route := s.BasicRoute()
route.Config = tc.intialConfig
DeleteApp(t, s.Context, s.Client, s.AppName)
s.GivenRouteExists(t, s.AppName, route)
routeResp, err := s.Client.Routes.PatchAppsAppRoutesRoute(
&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Body: &models.RouteWrapper{
Route: &models.Route{
Config: tc.change,
},
},
Context: s.Context,
},
)
if err != nil {
t.Fatalf("Failed to patch route, got %v", err)
}
actual := routeResp.Payload.Route.Config
if !ConfigEquivalent(actual, tc.expected) {
t.Errorf("Expected config : %v after update, got %v", tc.expected, actual)
}
})
}
}
func TestSetRouteAnnotationsOnCreate(t *testing.T) {
t.Parallel()
for _, tci := range createAnnotationsValidCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("valid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
})
rt := s.BasicRoute()
rt.Annotations = tc.annotations
route, err := s.Client.Routes.PostAppsAppRoutes(&routes.PostAppsAppRoutesParams{
App: s.AppName,
Context: s.Context,
Body: &models.RouteWrapper{
Route: rt,
},
})
if err != nil {
t.Fatalf("Failed to create route with valid annotations %v got error %v", tc.annotations, err)
}
gotMd := route.Payload.Route.Annotations
if !AnnotationsEquivalent(gotMd, tc.annotations) {
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.annotations)
}
getRoute := s.RouteMustExist(t, s.AppName, s.RoutePath)
if !AnnotationsEquivalent(getRoute.Annotations, tc.annotations) {
t.Errorf("GET annotations '%v' does not match set annotations %v", getRoute.Annotations, tc.annotations)
}
})
}
for _, tci := range createAnnotationsErrorCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("invalid_"+tc.name, func(ti *testing.T) {
ti.Parallel()
s := SetupHarness()
defer s.Cleanup()
_, err := s.PostApp(&models.App{
Name: s.AppName,
Annotations: tc.annotations,
})
if err == nil {
t.Fatalf("Created app with invalid annotations %v but expected error", tc.annotations)
}
if _, ok := err.(*apps.PostAppsBadRequest); !ok {
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
}
})
}
}
func TestSetRouteMetadataOnPatch(t *testing.T) {
t.Parallel()
for _, tci := range updateAnnotationsValidCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("valid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Annotations = tc.initial
s.GivenRouteExists(t, s.AppName, rt)
res, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath[1:],
Context: s.Context,
Body: &models.RouteWrapper{
Route: &models.Route{
Annotations: tc.change,
},
},
})
if err != nil {
t.Fatalf("Failed to patch annotations with %v on route: %v", tc.change, err)
}
gotMd := res.Payload.Route.Annotations
if !AnnotationsEquivalent(gotMd, tc.expected) {
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.expected)
}
getRoute := s.RouteMustExist(t, s.AppName, s.RoutePath)
if !AnnotationsEquivalent(getRoute.Annotations, tc.expected) {
t.Errorf("GET annotations '%v' does not match set annotations %v", getRoute.Annotations, tc.expected)
}
})
}
for _, tci := range updateAnnotationsErrorCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("invalid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
})
rt := s.BasicRoute()
rt.Annotations = tc.initial
s.GivenRouteExists(t, s.AppName, rt)
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath[1:],
Context: s.Context,
Body: &models.RouteWrapper{
Route: &models.Route{
Annotations: tc.change,
},
},
})
if err == nil {
t.Errorf("patched route with invalid annotations %v but expected error", tc.change)
}
if _, ok := err.(*routes.PatchAppsAppRoutesRouteBadRequest); !ok {
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
}
})
}
}
func TestCantUpdateRoutePath(t *testing.T) {
t.Parallel()
newRoutePath := id.New().String()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
_, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, s.RouteType, s.Format,
s.Memory, s.RouteConfig, s.RouteHeaders, newRoutePath)
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(
&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Body: &models.RouteWrapper{
Route: &models.Route{
Path: id.New().String(),
},
},
})
if err == nil {
t.Errorf("Route path suppose to be immutable, but it's not.")
t.Fatalf("Expected error when patching route")
}
if _, ok := err.(*routes.PatchAppsAppRoutesRouteBadRequest); ok {
t.Errorf("Error should be bad request when updating route path ")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestRouteDuplicate(t *testing.T) {
func TestRoutePreventsDuplicate(t *testing.T) {
t.Parallel()
newRouteType := "async"
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
_, err := s.PostRoute(s.AppName, s.BasicRoute())
_, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath,
newRouteType, s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
if err == nil {
t.Errorf("Route duplicate error should appear, but it didn't")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
if _, ok := err.(*routes.PostAppsAppRoutesConflict); !ok {
t.Errorf("Error should be a conflict when creating a new route, got %v", err)
}
}
func TestCanDeleteRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
}
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
func TestCantDeleteRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
_, err := s.Client.Routes.DeleteAppsAppRoutesRoute(&routes.DeleteAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Context: s.Context,
})
_, err := deleteRoute(s.Context, s.Client, s.AppName, "dummy-route")
if err == nil {
t.Error("Delete from missing route must fail.")
if err != nil {
t.Errorf("Expected success when deleting existing route, got %v", err)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestDeployNewApp(t *testing.T) {
func TestCantDeleteMissingRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
GetApp(t, s.Context, s.Client, s.AppName)
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.Client.Routes.DeleteAppsAppRoutesRoute(&routes.DeleteAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Context: s.Context,
})
if err == nil {
t.Fatalf("Expected error when deleting non-existing route, got none")
}
if _, ok := err.(*routes.DeleteAppsAppRoutesRouteNotFound); !ok {
t.Fatalf("Expected not-found when deleting non-existing route, got %v", err)
}
}
func TestDeployExistingApp(t *testing.T) {
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
GetApp(t, s.Context, s.Client, s.AppName)
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
func TestPutRouteCreatesNewApp(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
_, err := s.PutRoute(s.AppName, s.RoutePath, s.BasicRoute())
if err != nil {
t.Fatalf("Expected new route to be created, got %v", err)
}
s.AppMustExist(t, s.AppName)
s.RouteMustExist(t, s.AppName, s.RoutePath)
}
func TestDeployUpdate(t *testing.T) {
func TestPutRouteToExistingApp(t *testing.T) {
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PutRoute(s.AppName, s.RoutePath, s.BasicRoute())
if err != nil {
t.Fatalf("Failed to create route, got error %v", err)
}
s.AppMustExist(t, s.AppName)
s.RouteMustExist(t, s.AppName, s.RoutePath)
}
func TestPutRouteUpdatesRoute(t *testing.T) {
newRouteType := "sync"
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
updatedRoute := DeployRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType,
s.Format, s.RouteConfig, s.RouteHeaders)
assertRouteFields(t, updatedRoute, s.RoutePath, s.Image, newRouteType, s.Format)
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
DeleteApp(t, s.Context, s.Client, s.AppName)
changed := s.BasicRoute()
changed.Type = newRouteType
updatedRoute, err := s.PutRoute(s.AppName, s.RoutePath, changed)
if err != nil {
t.Fatalf("Failed to update route, got %v", err)
}
got := updatedRoute.Payload.Route.Type
if got != newRouteType {
t.Errorf("expected type to be %v after update, got %v", newRouteType, got)
}
}
func TestMulpileDeployExistingApp(t *testing.T) {
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
func TestPutIsIdempotentForHeaders(t *testing.T) {
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
routeHeaders := map[string][]string{}
routeHeaders["A"] = []string{"a"}
routeHeaders["B"] = []string{"b"}
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
sameRoute := DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
if ok := reflect.DeepEqual(sameRoute.Headers, routeHeaders); !ok {
r1 := s.BasicRoute()
r1.Headers = routeHeaders
updatedRoute1, err := s.PutRoute(s.AppName, s.RoutePath, r1)
if err != nil {
t.Fatalf("Failed to update route, got %v", err)
}
if firstMatches := reflect.DeepEqual(routeHeaders, updatedRoute1.Payload.Route.Headers); !firstMatches {
t.Errorf("Route headers should remain the same after multiple deploys with exact the same parameters '%v' != '%v'", routeHeaders, updatedRoute1.Payload.Route.Headers)
}
updatedRoute2, err := s.PutRoute(s.AppName, s.RoutePath, r1)
if bothmatch := reflect.DeepEqual(updatedRoute1.Payload.Route.Headers, updatedRoute2.Payload.Route.Headers); !bothmatch {
t.Error("Route headers should remain the same after multiple deploys with exact the same parameters")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}

View File

@@ -9,7 +9,6 @@ import (
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"testing"
@@ -48,11 +47,9 @@ func APIClient() *client.Fn {
}
var (
getServer sync.Once
cancel2 context.CancelFunc
s *server.Server
appsandroutes = make(map[string][]string)
approutesLock sync.Mutex
getServer sync.Once
cancel2 context.CancelFunc
s *server.Server
)
func getServerWithCancel() (*server.Server, context.CancelFunc) {
@@ -95,7 +92,9 @@ func getServerWithCancel() (*server.Server, context.CancelFunc) {
return s, cancel2
}
type SuiteSetup struct {
// TestHarness provides context and pre-configured clients to an individual test, it has some helper functions to create Apps and Routes that mirror the underlying client operations and clean them up after the test is complete
// This is not goroutine safe and each test case should use its own harness.
type TestHarness struct {
Context context.Context
Client *client.Fn
AppName string
@@ -109,6 +108,8 @@ type SuiteSetup struct {
RouteConfig map[string]string
RouteHeaders map[string][]string
Cancel context.CancelFunc
createdApps map[string]bool
}
func RandStringBytes(n int) string {
@@ -119,9 +120,10 @@ func RandStringBytes(n int) string {
return strings.ToLower(string(b))
}
func SetupDefaultSuite() *SuiteSetup {
// SetupHarness creates a test harness for a test case - this picks up external options and
func SetupHarness() *TestHarness {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
ss := &SuiteSetup{
ss := &TestHarness{
Context: ctx,
Client: APIClient(),
AppName: "fnintegrationtestapp" + RandStringBytes(10),
@@ -135,6 +137,7 @@ func SetupDefaultSuite() *SuiteSetup {
Memory: uint64(256),
Timeout: int32(30),
IdleTimeout: int32(30),
createdApps: make(map[string]bool),
}
if Host() != "localhost:8080" {
@@ -153,18 +156,16 @@ func SetupDefaultSuite() *SuiteSetup {
return ss
}
func Cleanup() {
func (s *TestHarness) Cleanup() {
ctx := context.Background()
c := APIClient()
approutesLock.Lock()
defer approutesLock.Unlock()
for appName, rs := range appsandroutes {
for _, routePath := range rs {
deleteRoute(ctx, c, appName, routePath)
}
DeleteAppNoT(ctx, c, appName)
//for _,ar := range s.createdRoutes {
// deleteRoute(ctx, s.Client, ar.appName, ar.routeName)
//}
for app, _ := range s.createdApps {
safeDeleteApp(ctx, s.Client, app)
}
appsandroutes = make(map[string][]string)
}
func EnvAsHeader(req *http.Request, selectedEnv []string) {
@@ -214,20 +215,6 @@ func init() {
rand.Seed(time.Now().UnixNano())
}
func MyCaller() string {
fpcs := make([]uintptr, 1)
n := runtime.Callers(3, fpcs)
if n == 0 {
return "n/a"
}
fun := runtime.FuncForPC(fpcs[0] - 1)
if fun == nil {
return "n/a"
}
f, l := fun.FileLine(fpcs[0] - 1)
return fmt.Sprintf("%s:%d", f, l)
}
func APICallWithRetry(t *testing.T, attempts int, sleep time.Duration, callback func() error) (err error) {
for i := 0; i < attempts; i++ {
err = callback()

View File

@@ -10,6 +10,7 @@ import (
"testing"
apiutils "github.com/fnproject/fn/test/fn-api-tests"
"github.com/fnproject/fn_go/models"
)
func LB() (string, error) {
@@ -23,10 +24,14 @@ func LB() (string, error) {
}
func TestCanExecuteFunction(t *testing.T) {
s := apiutils.SetupDefaultSuite()
apiutils.CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
apiutils.CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := apiutils.SetupHarness()
s.GivenAppExists(t, &models.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
@@ -48,14 +53,18 @@ func TestCanExecuteFunction(t *testing.T) {
if !strings.Contains(expectedOutput, output.String()) {
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
}
apiutils.DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestBasicConcurrentExecution(t *testing.T) {
s := apiutils.SetupDefaultSuite()
apiutils.CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
apiutils.CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := apiutils.SetupHarness()
s.GivenAppExists(t, &models.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
@@ -93,5 +102,4 @@ func TestBasicConcurrentExecution(t *testing.T) {
}
}
apiutils.DeleteApp(t, s.Context, s.Client, s.AppName)
}