mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Add annotations to routes and apps (#866)
Adds 'annotations' attribute to Routes and Apps
This commit is contained in:
@@ -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?
|
||||
|
||||
84
test/fn-api-tests/annotation_cases.go
Normal file
84
test/fn-api-tests/annotation_cases.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
29
test/fn-api-tests/config_cases.go
Normal file
29
test/fn-api-tests/config_cases.go
Normal 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)
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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, "😀 👍 🎗")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user