mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Speed up API tests (#624)
* Adjust API tests internal API * Refactor API tests to take less time - sqlite: tests 15s, overall time: 1m - mysql: tests 15s, overall time: 59s * Use retry func to survive in faulty places * Use retry func while trying to ping SQL datastore - implements retry func specifically for SQL datastore ping - fmt fixes - using sqlx.Db.PingContext instead of sqlx.Db.Ping - propogate context to SQL datastore * Simplify TestCanCauseTimeout retry loop * Call retry with sane timeout * Fix TestOversizedLog, use retry func * Increase number of attempts 2 test cases are really faulty in CI, so they need a lot more time to finish. * Increase TestCanCauseTimeout timeout * Use retry at TestMultiLog to speed it up * Use retry at TestCanWriteLogs to speed it up * Use retry at TestGetCallsSuccess to speed it up * Use retry at TestCanGetAsyncState to speed it up * Use retry at TestListCallsSuccess to speed it up * Remove sleep calls * Remove dup test case * Cleaup Calls API test * Build API tests binary once This patch lets CI to build API tests binary once and reuse that whenever it needs it * Swap API tests checks * Build API test binary by default dirty fix for CircleCI * Use retry func to determine if datastore is alive in tests * go install should also reduce build time * Fix rebase issues
This commit is contained in:
committed by
Reed Allman
parent
cafc277325
commit
9d6f0b2a05
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,6 +30,6 @@ fnlb/fnlb
|
|||||||
/fn
|
/fn
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/fnserver
|
/fnserver
|
||||||
.idea/
|
|
||||||
*iml
|
*iml
|
||||||
target/
|
target/
|
||||||
|
fn-api-tests.test
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -31,11 +31,14 @@ test-basic: checkfmt pull-images fn-test-utils
|
|||||||
test: checkfmt pull-images test-basic test-middleware test-extensions
|
test: checkfmt pull-images test-basic test-middleware test-extensions
|
||||||
|
|
||||||
test-api: test-basic
|
test-api: test-basic
|
||||||
./api_test.sh mysql 4
|
|
||||||
./api_test.sh postgres 4
|
|
||||||
./api_test.sh sqlite3 4
|
./api_test.sh sqlite3 4
|
||||||
|
./api_test.sh mysql 4 0
|
||||||
|
./api_test.sh postgres 4 0
|
||||||
|
|
||||||
full-test: test test-api
|
build-static:
|
||||||
|
go install
|
||||||
|
|
||||||
|
full-test: build-static test test-api
|
||||||
|
|
||||||
img-sleeper:
|
img-sleeper:
|
||||||
docker pull fnproject/sleeper
|
docker pull fnproject/sleeper
|
||||||
|
|||||||
28
api_test.sh
28
api_test.sh
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -exuo pipefail
|
set -exo pipefail
|
||||||
|
|
||||||
function host {
|
function host {
|
||||||
case ${DOCKER_LOCATION:-localhost} in
|
case ${DOCKER_LOCATION:-localhost} in
|
||||||
@@ -24,17 +24,16 @@ case "$1" in
|
|||||||
"sqlite3" )
|
"sqlite3" )
|
||||||
rm -fr /tmp/fn_integration_tests.db
|
rm -fr /tmp/fn_integration_tests.db
|
||||||
touch /tmp/fn_integration_tests.db
|
touch /tmp/fn_integration_tests.db
|
||||||
FN_DB_URL="sqlite3:///tmp/fn_integration_tests.db"
|
export FN_DB_URL="sqlite3:///tmp/fn_integration_tests.db"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"mysql" )
|
"mysql" )
|
||||||
DB_CONTAINER="func-mysql-test"
|
DB_CONTAINER="func-mysql-test"
|
||||||
docker rm -fv ${DB_CONTAINER} || echo No prev mysql test db container
|
docker rm -fv ${DB_CONTAINER} || echo No prev mysql test db container
|
||||||
docker run --name ${DB_CONTAINER} -p 3306:3306 -e MYSQL_DATABASE=funcs -e MYSQL_ROOT_PASSWORD=root -d mysql
|
docker run --name ${DB_CONTAINER} -p 3306:3306 -e MYSQL_DATABASE=funcs -e MYSQL_ROOT_PASSWORD=root -d mysql
|
||||||
sleep 15
|
|
||||||
MYSQL_HOST=`host ${DB_CONTAINER}`
|
MYSQL_HOST=`host ${DB_CONTAINER}`
|
||||||
MYSQL_PORT=3306
|
MYSQL_PORT=3306
|
||||||
FN_DB_URL="mysql://root:root@tcp(${MYSQL_HOST}:${MYSQL_PORT})/funcs"
|
export FN_DB_URL="mysql://root:root@tcp(${MYSQL_HOST}:${MYSQL_PORT})/funcs"
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -42,12 +41,29 @@ case "$1" in
|
|||||||
DB_CONTAINER="func-postgres-test"
|
DB_CONTAINER="func-postgres-test"
|
||||||
docker rm -fv ${DB_CONTAINER} || echo No prev test db container
|
docker rm -fv ${DB_CONTAINER} || echo No prev test db container
|
||||||
docker run --name ${DB_CONTAINER} -e "POSTGRES_DB=funcs" -e "POSTGRES_PASSWORD=root" -p 5432:5432 -d postgres
|
docker run --name ${DB_CONTAINER} -e "POSTGRES_DB=funcs" -e "POSTGRES_PASSWORD=root" -p 5432:5432 -d postgres
|
||||||
sleep 15
|
|
||||||
POSTGRES_HOST=`host ${DB_CONTAINER}`
|
POSTGRES_HOST=`host ${DB_CONTAINER}`
|
||||||
POSTGRES_PORT=5432
|
POSTGRES_PORT=5432
|
||||||
FN_DB_URL="postgres://postgres:root@${POSTGRES_HOST}:${POSTGRES_PORT}/funcs?sslmode=disable"
|
export FN_DB_URL="postgres://postgres:root@${POSTGRES_HOST}:${POSTGRES_PORT}/funcs?sslmode=disable"
|
||||||
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
#test test/fn-api-tests/fn-api-tests.test
|
||||||
|
#status=`echo $?`
|
||||||
|
#rebuild="${3:-1}"
|
||||||
|
#circleci=`echo ${CIRCLECI}`
|
||||||
|
#cd test/fn-api-tests
|
||||||
|
#if [[ $status -ne 0 ]] || [[ $rebuild -ne 0 ]] ; then
|
||||||
|
# if [[ $circleci == "true" ]]; then
|
||||||
|
# # dirty things to make CI pass
|
||||||
|
# ls -lah /usr/local/go/pkg/linux_amd64/runtime
|
||||||
|
# sudo chown -R `whoami`:root /usr/local/go
|
||||||
|
# sudo chmod -R 777 /usr/local/go/pkg/linux_amd64
|
||||||
|
# ls -lah /usr/local/go/pkg/linux_amd64/runtime
|
||||||
|
# fi
|
||||||
|
# pwd
|
||||||
|
# go test -i -a -o fn-api-tests.test
|
||||||
|
#fi
|
||||||
|
#pwd
|
||||||
|
#./fn-api-tests.test -test.v -test.parallel ${2:-1} ./...; cd ../../
|
||||||
cd test/fn-api-tests && FN_API_URL="http://localhost:8080" FN_DB_URL=${FN_DB_URL} go test -v -parallel ${2:-1} ./...; cd ../../
|
cd test/fn-api-tests && FN_API_URL="http://localhost:8080" FN_DB_URL=${FN_DB_URL} go test -v -parallel ${2:-1} ./...; cd ../../
|
||||||
|
|||||||
@@ -9,121 +9,118 @@ import (
|
|||||||
"github.com/fnproject/fn_go/client/apps"
|
"github.com/fnproject/fn_go/client/apps"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestApps(t *testing.T) {
|
func TestAppDeleteNotFound(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("delete-app-not-found-test", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
cfg := &apps.DeleteAppsAppParams{
|
||||||
s := SetupDefaultSuite()
|
App: "missing-app",
|
||||||
cfg := &apps.DeleteAppsAppParams{
|
Context: s.Context,
|
||||||
App: "missing-app",
|
}
|
||||||
Context: s.Context,
|
cfg.WithTimeout(time.Second * 60)
|
||||||
}
|
_, err := s.Client.Apps.DeleteAppsApp(cfg)
|
||||||
cfg.WithTimeout(time.Second * 60)
|
if err == nil {
|
||||||
_, err := s.Client.Apps.DeleteAppsApp(cfg)
|
t.Errorf("Error during app delete: we should get HTTP 404, but got: %s", err.Error())
|
||||||
if err == nil {
|
}
|
||||||
t.Errorf("Error during app delete: we should get HTTP 404, but got: %s", err.Error())
|
}
|
||||||
}
|
|
||||||
})
|
func TestAppGetNotFound(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("app-not-found-test", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
cfg := &apps.GetAppsAppParams{
|
||||||
s := SetupDefaultSuite()
|
App: "missing-app",
|
||||||
cfg := &apps.GetAppsAppParams{
|
Context: s.Context,
|
||||||
App: "missing-app",
|
}
|
||||||
Context: s.Context,
|
cfg.WithTimeout(time.Second * 60)
|
||||||
}
|
_, err := s.Client.Apps.GetAppsApp(cfg)
|
||||||
cfg.WithTimeout(time.Second * 60)
|
CheckAppResponseError(t, err)
|
||||||
_, err := s.Client.Apps.GetAppsApp(cfg)
|
}
|
||||||
CheckAppResponseError(t, err)
|
|
||||||
})
|
func TestAppCreateNoConfigSuccess(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("create-app-and-delete-no-config-test", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s := SetupDefaultSuite()
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestAppCreateWithConfigSuccess(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("create-app-with-config-test", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
|
||||||
s := SetupDefaultSuite()
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestAppInsect(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("inspect-app-with-config-test", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
|
||||||
s := SetupDefaultSuite()
|
app := GetApp(t, s.Context, s.Client, s.AppName)
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
|
val, ok := app.Config["A"]
|
||||||
app := GetApp(t, s.Context, s.Client, s.AppName)
|
if !ok {
|
||||||
val, ok := app.Config["A"]
|
t.Error("Error during app config inspect: config map misses required entity `A` with value `a`.")
|
||||||
if !ok {
|
}
|
||||||
t.Error("Error during app config inspect: config map misses required entity `A` with value `a`.")
|
if !strings.Contains("a", val) {
|
||||||
}
|
t.Errorf("App config value is different. Expected: `a`. Actual %v", val)
|
||||||
if !strings.Contains("a", val) {
|
}
|
||||||
t.Errorf("App config value is different. Expected: `a`. Actual %v", val)
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
}
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestAppPatchSameConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("patch-app-with-exact-same-config-data", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
config := map[string]string{
|
||||||
s := SetupDefaultSuite()
|
"A": "a",
|
||||||
config := map[string]string{
|
}
|
||||||
"A": "a",
|
|
||||||
}
|
appUpdatePayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
|
||||||
|
_, ok := appUpdatePayload.Payload.App.Config["A"]
|
||||||
appUpdatePayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
|
if !ok {
|
||||||
_, ok := appUpdatePayload.Payload.App.Config["A"]
|
t.Error("Error during app update: config map misses required entity `A` with value `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)
|
||||||
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestAppPatchOverwriteConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("patch-override-app-config", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
config := map[string]string{
|
||||||
s := SetupDefaultSuite()
|
"A": "b",
|
||||||
config := map[string]string{
|
}
|
||||||
"A": "b",
|
appPayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
|
||||||
}
|
val, ok := appPayload.Payload.App.Config["A"]
|
||||||
appPayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
|
if !ok {
|
||||||
val, ok := appPayload.Payload.App.Config["A"]
|
t.Error("Error during app config inspect: config map misses required entity `A` with value `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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestAppsPatchConfigAddValue(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("patch-add-app-config", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
config := map[string]string{
|
||||||
s := SetupDefaultSuite()
|
"B": "b",
|
||||||
config := map[string]string{
|
}
|
||||||
"B": "b",
|
appPayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
|
||||||
}
|
val, ok := appPayload.Payload.App.Config["B"]
|
||||||
appPayload := CreateUpdateApp(t, s.Context, s.Client, s.AppName, config)
|
if !ok {
|
||||||
val, ok := appPayload.Payload.App.Config["B"]
|
t.Error("Error during app config inspect: config map misses required entity `B` with value `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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestAppDuplicate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("crete-app-duplicate", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s := SetupDefaultSuite()
|
_, err := CreateAppNoAssert(s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
if reflect.TypeOf(err) != reflect.TypeOf(apps.NewPostAppsConflict()) {
|
||||||
_, err := CreateAppNoAssert(s.Context, s.Client, s.AppName, map[string]string{})
|
CheckAppResponseError(t, err)
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(apps.NewPostAppsConflict()) {
|
}
|
||||||
CheckAppResponseError(t, err)
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
}
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,106 +10,71 @@ import (
|
|||||||
"github.com/fnproject/fn_go/client/call"
|
"github.com/fnproject/fn_go/client/call"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCalls(t *testing.T) {
|
func TestCallsMissingApp(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("list-calls-for-missing-app", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
cfg := &call.GetAppsAppCallsParams{
|
||||||
s := SetupDefaultSuite()
|
App: s.AppName,
|
||||||
cfg := &call.GetAppsAppCallsParams{
|
Path: &s.RoutePath,
|
||||||
App: s.AppName,
|
Context: s.Context,
|
||||||
Path: &s.RoutePath,
|
}
|
||||||
Context: s.Context,
|
_, err := s.Client.Call.GetAppsAppCalls(cfg)
|
||||||
}
|
if err == nil {
|
||||||
_, err := s.Client.Call.GetAppsAppCalls(cfg)
|
t.Errorf("Must fail with missing app error, but got %s", err)
|
||||||
if err == nil {
|
}
|
||||||
t.Errorf("Must fail with missing app error, but got %s", err)
|
}
|
||||||
}
|
|
||||||
})
|
func TestCallsDummy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("get-dummy-call", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s := SetupDefaultSuite()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
cfg := &call.GetAppsAppCallsCallParams{
|
||||||
|
Call: "dummy",
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
App: s.AppName,
|
||||||
Call: "dummy",
|
Context: s.Context,
|
||||||
App: s.AppName,
|
}
|
||||||
Context: s.Context,
|
cfg.WithTimeout(time.Second * 60)
|
||||||
}
|
_, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
||||||
cfg.WithTimeout(time.Second * 60)
|
if err == nil {
|
||||||
_, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
t.Error("Must fail because `dummy` call does not exist.")
|
||||||
if err == nil {
|
}
|
||||||
t.Error("Must fail because `dummy` call does not exist.")
|
|
||||||
}
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestGetExactCall(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("get-real-call", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s := SetupDefaultSuite()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
u := url.URL{
|
Host: Host(),
|
||||||
Scheme: "http",
|
}
|
||||||
Host: Host(),
|
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
callID := CallAsync(t, u, &bytes.Buffer{})
|
||||||
|
|
||||||
time.Sleep(time.Second * 5)
|
cfg := &call.GetAppsAppCallsCallParams{
|
||||||
_, err := s.Client.Call.GetAppsAppCalls(&call.GetAppsAppCallsParams{
|
Call: callID,
|
||||||
App: s.AppName,
|
App: s.AppName,
|
||||||
Path: &s.RoutePath,
|
Context: s.Context,
|
||||||
})
|
}
|
||||||
if err != nil {
|
cfg.WithTimeout(time.Second * 60)
|
||||||
switch err.(type) {
|
|
||||||
case *call.GetAppsAppCallsCallNotFound:
|
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
||||||
msg := err.(*call.GetAppsAppCallsCallNotFound).Payload.Error.Message
|
_, err = s.Client.Call.GetAppsAppCallsCall(cfg)
|
||||||
t.Errorf("Unexpected error occurred: %v.", msg)
|
return err
|
||||||
}
|
})
|
||||||
}
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
if retryErr != nil {
|
||||||
})
|
t.Error(retryErr.Error())
|
||||||
|
}
|
||||||
t.Run("list-calls", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
CallAsync(t, u, &bytes.Buffer{})
|
|
||||||
time.Sleep(time.Second * 8)
|
|
||||||
|
|
||||||
cfg := &call.GetAppsAppCallsParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Path: &s.RoutePath,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
calls, err := s.Client.Call.GetAppsAppCalls(cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %s", err)
|
|
||||||
}
|
|
||||||
if calls == nil || calls.Payload == nil || calls.Payload.Calls == nil || len(calls.Payload.Calls) == 0 {
|
|
||||||
t.Errorf("Must fail. There should be at least one call to `%v` route.", s.RoutePath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, c := range calls.Payload.Calls {
|
|
||||||
if c.Path != s.RoutePath {
|
|
||||||
t.Errorf("Call path mismatch.\n\tExpected: %v\n\tActual: %v", c.Path, s.RoutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,15 +14,6 @@ import (
|
|||||||
"github.com/fnproject/fn_go/client/operations"
|
"github.com/fnproject/fn_go/client/operations"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrMsg struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TimeoutBody struct {
|
|
||||||
Error ErrMsg `json:"error"`
|
|
||||||
CallID string `json:"request_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func CallAsync(t *testing.T, u url.URL, content io.Reader) string {
|
func CallAsync(t *testing.T, u url.URL, content io.Reader) string {
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||||
@@ -49,119 +40,125 @@ func CallAsync(t *testing.T, u url.URL, content io.Reader) string {
|
|||||||
return callID.CallID
|
return callID.CallID
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouteExecutions(t *testing.T) {
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
||||||
|
|
||||||
|
content := &bytes.Buffer{}
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expectedOutput := "Hello World!\n"
|
||||||
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
||||||
|
|
||||||
|
content := &bytes.Buffer{}
|
||||||
|
json.NewEncoder(content).Encode(struct {
|
||||||
|
Name string
|
||||||
|
}{Name: "John"})
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expectedOutput := "Hello John!\n"
|
||||||
|
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"
|
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)
|
||||||
|
|
||||||
t.Run("run-sync-fnproject/hello-no-input", func(t *testing.T) {
|
u := url.URL{
|
||||||
t.Parallel()
|
Scheme: "http",
|
||||||
s := SetupDefaultSuite()
|
Host: Host(),
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
}
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
|
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
_, err := UpdateRoute(
|
||||||
Scheme: "http",
|
t, s.Context, s.Client,
|
||||||
Host: Host(),
|
s.AppName, s.RoutePath,
|
||||||
}
|
s.Image, newRouteType, s.Format,
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
s.Memory, s.RouteConfig, s.RouteHeaders, "")
|
||||||
|
|
||||||
content := &bytes.Buffer{}
|
CheckRouteResponseError(t, err)
|
||||||
output := &bytes.Buffer{}
|
|
||||||
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
CallAsync(t, u, &bytes.Buffer{})
|
||||||
if err != nil {
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
}
|
||||||
}
|
|
||||||
expectedOutput := "Hello World!\n"
|
func TestCanGetAsyncState(t *testing.T) {
|
||||||
if !strings.Contains(expectedOutput, output.String()) {
|
newRouteType := "async"
|
||||||
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
t.Parallel()
|
||||||
}
|
s := SetupDefaultSuite()
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
callID := CallAsync(t, u, &bytes.Buffer{})
|
||||||
|
cfg := &call.GetAppsAppCallsCallParams{
|
||||||
|
Call: callID,
|
||||||
|
App: s.AppName,
|
||||||
|
Context: s.Context,
|
||||||
|
}
|
||||||
|
cfg.WithTimeout(time.Second * 60)
|
||||||
|
|
||||||
|
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
||||||
|
_, err = s.Client.Call.GetAppsAppCallsCall(cfg)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("run-sync-fnproject/hello-with-input", func(t *testing.T) {
|
if retryErr != nil {
|
||||||
t.Parallel()
|
t.Error(retryErr.Error())
|
||||||
s := SetupDefaultSuite()
|
} else {
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Name string
|
|
||||||
}{Name: "John"})
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
_, err := CallFN(u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
expectedOutput := "Hello John!\n"
|
|
||||||
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)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("run-async-fnproject/hello", func(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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
|
||||||
CallAsync(t, u, &bytes.Buffer{})
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("run-async-fnproject/hello-with-status-check", func(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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
|
||||||
callID := CallAsync(t, u, &bytes.Buffer{})
|
|
||||||
time.Sleep(time.Second * 10)
|
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
callResponse, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
callResponse, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
@@ -184,46 +181,54 @@ func TestRouteExecutions(t *testing.T) {
|
|||||||
if callObject.Status != "success" {
|
if callObject.Status != "success" {
|
||||||
t.Errorf("Call object status mismatch.\n\tExpected: %v\n\tActual:%v", "success", callObject.Status)
|
t.Errorf("Call object status mismatch.\n\tExpected: %v\n\tActual:%v", "success", callObject.Status)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
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"
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
||||||
|
|
||||||
|
content := &bytes.Buffer{}
|
||||||
|
json.NewEncoder(content).Encode(struct {
|
||||||
|
Seconds int64 `json:"seconds"`
|
||||||
|
}{Seconds: 11})
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
|
||||||
|
headers, _ := CallFN(u.String(), content, output, "POST", []string{})
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "Timed out") {
|
||||||
|
t.Errorf("Must fail because of timeout, but got error message: %v", output.String())
|
||||||
|
}
|
||||||
|
cfg := &call.GetAppsAppCallsCallParams{
|
||||||
|
Call: headers.Get("FN_CALL_ID"),
|
||||||
|
App: s.AppName,
|
||||||
|
Context: s.Context,
|
||||||
|
}
|
||||||
|
cfg.WithTimeout(time.Second * 60)
|
||||||
|
|
||||||
|
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
||||||
|
_, err = s.Client.Call.GetAppsAppCallsCall(cfg)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("exec-timeout-test", func(t *testing.T) {
|
if retryErr != nil {
|
||||||
t.Parallel()
|
t.Error(retryErr.Error())
|
||||||
s := SetupDefaultSuite()
|
} else {
|
||||||
routePath := "/" + RandStringBytes(10)
|
|
||||||
image := "funcy/timeout:0.0.1"
|
|
||||||
routeType := "sync"
|
|
||||||
|
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
|
||||||
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Seconds int64 `json:"seconds"`
|
|
||||||
}{Seconds: 31})
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
|
|
||||||
headers, _ := CallFN(u.String(), content, output, "POST", []string{})
|
|
||||||
|
|
||||||
if !strings.Contains(output.String(), "Timed out") {
|
|
||||||
t.Errorf("Must fail because of timeout, but got error message: %v", output.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
|
||||||
Call: headers.Get("FN_CALL_ID"),
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
callObj, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
callObj, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %s", err)
|
t.Errorf("Unexpected error: %s", err)
|
||||||
@@ -232,36 +237,43 @@ func TestRouteExecutions(t *testing.T) {
|
|||||||
t.Errorf("Call status mismatch.\n\tExpected: %v\n\tActual: %v",
|
t.Errorf("Call status mismatch.\n\tExpected: %v\n\tActual: %v",
|
||||||
"output", "callObj.Payload.Call.Status")
|
"output", "callObj.Payload.Call.Status")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
func TestMultiLog(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
s := SetupDefaultSuite()
|
||||||
|
routePath := "/multi-log"
|
||||||
|
image := "funcy/multi-log:0.0.1"
|
||||||
|
routeType := "async"
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
||||||
|
|
||||||
|
callID := CallAsync(t, u, &bytes.Buffer{})
|
||||||
|
|
||||||
|
cfg := &operations.GetAppsAppCallsCallLogParams{
|
||||||
|
Call: callID,
|
||||||
|
App: s.AppName,
|
||||||
|
Context: s.Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
||||||
|
_, err = s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("exec-multi-log-test", func(t *testing.T) {
|
if retryErr != nil {
|
||||||
t.Parallel()
|
t.Error(retryErr.Error())
|
||||||
s := SetupDefaultSuite()
|
} else {
|
||||||
routePath := "/multi-log"
|
|
||||||
image := "funcy/multi-log:0.0.1"
|
|
||||||
routeType := "async"
|
|
||||||
|
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
|
||||||
|
|
||||||
callID := CallAsync(t, u, &bytes.Buffer{})
|
|
||||||
time.Sleep(15 * time.Second)
|
|
||||||
|
|
||||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
logObj, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
logObj, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %s", err)
|
t.Errorf("Unexpected error: %s", err)
|
||||||
@@ -277,115 +289,120 @@ func TestRouteExecutions(t *testing.T) {
|
|||||||
t.Errorf("Log entry must contain `Second line` "+
|
t.Errorf("Log entry must contain `Second line` "+
|
||||||
"string, but got: %v", logObj.Payload.Log.Log)
|
"string, but got: %v", logObj.Payload.Log.Log)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
||||||
|
content := &bytes.Buffer{}
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
CallFN(u.String(), content, output, "POST",
|
||||||
|
[]string{
|
||||||
|
"ACCEPT: application/xml",
|
||||||
|
"ACCEPT: application/json; q=0.2",
|
||||||
|
})
|
||||||
|
res := output.String()
|
||||||
|
if !strings.Contains("application/xml, application/json; q=0.2", res) {
|
||||||
|
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 := "async"
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "http",
|
||||||
|
Host: Host(),
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
||||||
|
content := &bytes.Buffer{}
|
||||||
|
json.NewEncoder(content).Encode(struct {
|
||||||
|
Size int
|
||||||
|
}{Size: 20})
|
||||||
|
|
||||||
|
callID := CallAsync(t, u, content)
|
||||||
|
|
||||||
|
cfg := &operations.GetAppsAppCallsCallLogParams{
|
||||||
|
Call: callID,
|
||||||
|
App: s.AppName,
|
||||||
|
Context: s.Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
||||||
|
_, err = s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("verify-headers-separator", func(t *testing.T) {
|
if retryErr != nil {
|
||||||
t.Parallel()
|
t.Error(retryErr.Error())
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
Scheme: "http",
|
}
|
||||||
Host: Host(),
|
|
||||||
}
|
func TestOversizedLog(t *testing.T) {
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
t.Parallel()
|
||||||
content := &bytes.Buffer{}
|
s := SetupDefaultSuite()
|
||||||
output := &bytes.Buffer{}
|
routePath := "/log"
|
||||||
CallFN(u.String(), content, output, "POST",
|
image := "funcy/log:0.0.1"
|
||||||
[]string{
|
routeType := "async"
|
||||||
"ACCEPT: application/xml",
|
|
||||||
"ACCEPT: application/json; q=0.2",
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
})
|
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
|
||||||
res := output.String()
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
if !strings.Contains("application/xml, application/json; q=0.2", res) {
|
|
||||||
t.Errorf("HEADER_ACCEPT='application/xml, application/json; q=0.2' "+
|
size := 1 * 1024 * 1024 * 1024
|
||||||
"should be in output, have:%s\n", res)
|
u := url.URL{
|
||||||
}
|
Scheme: "http",
|
||||||
DeleteRoute(t, s.Context, s.Client, s.AppName, routePath)
|
Host: Host(),
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
}
|
||||||
|
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
||||||
|
content := &bytes.Buffer{}
|
||||||
|
json.NewEncoder(content).Encode(struct {
|
||||||
|
Size int
|
||||||
|
}{Size: size}) //exceeding log by 1 symbol
|
||||||
|
|
||||||
|
callID := CallAsync(t, u, content)
|
||||||
|
|
||||||
|
cfg := &operations.GetAppsAppCallsCallLogParams{
|
||||||
|
Call: callID,
|
||||||
|
App: s.AppName,
|
||||||
|
Context: s.Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
||||||
|
_, err = s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
|
if retryErr != nil {
|
||||||
t.Run("exec-log-test", func(t *testing.T) {
|
t.Error(retryErr.Error())
|
||||||
//XXX: Fix this test.
|
} else {
|
||||||
t.Skip("Flaky test needs to be rewritten. https://github.com/fnproject/fn/issues/253")
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupDefaultSuite()
|
|
||||||
routePath := "/log"
|
|
||||||
image := "funcy/log:0.0.1"
|
|
||||||
routeType := "async"
|
|
||||||
|
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Size int
|
|
||||||
}{Size: 20})
|
|
||||||
|
|
||||||
callID := CallAsync(t, u, content)
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
|
|
||||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("exec-oversized-log-test", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
t.Skip("Skipped until fix for https://gitlab-odx.oracle.com/odx/functions/issues/86.")
|
|
||||||
|
|
||||||
s := SetupDefaultSuite()
|
|
||||||
routePath := "/log"
|
|
||||||
image := "funcy/log:0.0.1"
|
|
||||||
routeType := "async"
|
|
||||||
|
|
||||||
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.RouteConfig, s.RouteHeaders)
|
|
||||||
|
|
||||||
size := 1 * 1024 * 1024 * 1024
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, routePath)
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Size int
|
|
||||||
}{Size: size}) //exceeding log by 1 symbol
|
|
||||||
|
|
||||||
callID := CallAsync(t, u, content)
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
logObj, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
logObj, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %s", err)
|
t.Errorf("Unexpected error: %s", err)
|
||||||
@@ -394,7 +411,7 @@ func TestRouteExecutions(t *testing.T) {
|
|||||||
t.Errorf("Log entry suppose to be truncated up to expected size %v, got %v",
|
t.Errorf("Log entry suppose to be truncated up to expected size %v, got %v",
|
||||||
size/1024, len(logObj.Payload.Log.Log))
|
size/1024, len(logObj.Payload.Log.Log))
|
||||||
}
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
}
|
||||||
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,59 +14,54 @@ type JSONResponse struct {
|
|||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFnFormats(t *testing.T) {
|
func TestFnJSONFormats(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
s := SetupDefaultSuite()
|
||||||
|
|
||||||
t.Run("test-json-format", func(t *testing.T) {
|
// TODO(treeder): put image in fnproject @ dockerhub
|
||||||
t.Parallel()
|
image := "denismakogon/test-hot-json-go:0.0.1"
|
||||||
s := SetupDefaultSuite()
|
format := "json"
|
||||||
|
route := "/test-hot-json-go"
|
||||||
|
|
||||||
// TODO(treeder): put image in fnproject @ dockerhub
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
image := "denismakogon/test-hot-json-go:0.0.1"
|
CreateRoute(t, s.Context, s.Client, s.AppName, route, image, "sync",
|
||||||
format := "json"
|
format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
route := "/test-hot-json-go"
|
|
||||||
|
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
u := url.URL{
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, route, image, "sync",
|
Scheme: "http",
|
||||||
format, s.RouteConfig, s.RouteHeaders)
|
Host: Host(),
|
||||||
|
}
|
||||||
u := url.URL{
|
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
b, _ := json.Marshal(&struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}{
|
|
||||||
Name: "Jimmy",
|
|
||||||
})
|
|
||||||
content := bytes.NewBuffer(b)
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
headers, err := CallFN(u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &JSONResponse{}
|
|
||||||
json.Unmarshal(output.Bytes(), msg)
|
|
||||||
expectedOutput := "Hello Jimmy"
|
|
||||||
if !strings.Contains(expectedOutput, msg.Message) {
|
|
||||||
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedHeaderNames := []string{"Content-Type", "Content-Length"}
|
|
||||||
expectedHeaderValues := []string{"application/json; charset=utf-8", strconv.Itoa(output.Len())}
|
|
||||||
for i, name := range expectedHeaderNames {
|
|
||||||
actual := headers.Get(name)
|
|
||||||
expected := expectedHeaderValues[i]
|
|
||||||
if !strings.Contains(expected, actual) {
|
|
||||||
t.Errorf("HTTP header assertion error for %v."+
|
|
||||||
"\n\tExpected: %v\n\tActual: %v", name, expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
|
|
||||||
|
b, _ := json.Marshal(&struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}{
|
||||||
|
Name: "Jimmy",
|
||||||
})
|
})
|
||||||
|
content := bytes.NewBuffer(b)
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
headers, err := CallFN(u.String(), content, output, "POST", []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &JSONResponse{}
|
||||||
|
json.Unmarshal(output.Bytes(), msg)
|
||||||
|
expectedOutput := "Hello Jimmy"
|
||||||
|
if !strings.Contains(expectedOutput, msg.Message) {
|
||||||
|
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedHeaderNames := []string{"Content-Type", "Content-Length"}
|
||||||
|
expectedHeaderValues := []string{"application/json; charset=utf-8", strconv.Itoa(output.Len())}
|
||||||
|
for i, name := range expectedHeaderNames {
|
||||||
|
actual := headers.Get(name)
|
||||||
|
expected := expectedHeaderValues[i]
|
||||||
|
if !strings.Contains(expected, actual) {
|
||||||
|
t.Errorf("HTTP header assertion error for %v."+
|
||||||
|
"\n\tExpected: %v\n\tActual: %v", name, expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,17 +89,19 @@ func assertRouteFields(t *testing.T, routeObject *models.Route, path, image, rou
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, routePath, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) (*routes.PostAppsAppRoutesOK, error) {
|
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) {
|
||||||
cfg := &routes.PostAppsAppRoutesParams{
|
cfg := &routes.PostAppsAppRoutesParams{
|
||||||
App: appName,
|
App: appName,
|
||||||
Body: &models.RouteWrapper{
|
Body: &models.RouteWrapper{
|
||||||
Route: &models.Route{
|
Route: &models.Route{
|
||||||
Config: routeConfig,
|
Config: routeConfig,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
Image: image,
|
Image: image,
|
||||||
Path: routePath,
|
Path: routePath,
|
||||||
Type: routeType,
|
Type: routeType,
|
||||||
Format: routeFormat,
|
Format: routeFormat,
|
||||||
|
Timeout: &timeout,
|
||||||
|
IDLETimeout: &idleTimeout,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
@@ -119,8 +121,8 @@ func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, route
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath, image, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) {
|
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, routeConfig, headers)
|
routeResponse, err := createRoute(ctx, fnclient, appName, image, routePath, routeType, routeFormat, timeout, idleTimeout, routeConfig, headers)
|
||||||
CheckRouteResponseError(t, err)
|
CheckRouteResponseError(t, err)
|
||||||
|
|
||||||
assertRouteFields(t, routeResponse.Payload.Route, routePath, image, routeType, routeFormat)
|
assertRouteFields(t, routeResponse.Payload.Route, routePath, image, routeType, routeFormat)
|
||||||
|
|||||||
@@ -4,208 +4,207 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fnproject/fn/api/id"
|
"github.com/fnproject/fn/api/id"
|
||||||
api_models "github.com/fnproject/fn/api/models"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
"github.com/fnproject/fn_go/models"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRoutes(t *testing.T) {
|
func TestCreateRouteEmptyType(t *testing.T) {
|
||||||
newRouteType := "sync"
|
t.Parallel()
|
||||||
newRoutePath := id.New().String()
|
s := SetupDefaultSuite()
|
||||||
t.Run("create-route-with-empty-type", func(t *testing.T) {
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
t.Parallel()
|
_, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "",
|
||||||
s := SetupDefaultSuite()
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
if err == nil {
|
||||||
_, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "", s.Format,
|
t.Errorf("Should fail with Invalid route Type.")
|
||||||
s.RouteConfig, s.RouteHeaders)
|
}
|
||||||
if err == nil {
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
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()
|
||||||
t.Run("create-route", func(t *testing.T) {
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
t.Parallel()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
s := SetupDefaultSuite()
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
}
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
func TestListRoutes(t *testing.T) {
|
||||||
})
|
t.Parallel()
|
||||||
|
s := SetupDefaultSuite()
|
||||||
t.Run("list-and-find-route", func(t *testing.T) {
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
t.Parallel()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
s := SetupDefaultSuite()
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
if !assertContainsRoute(ListRoutes(t, s.Context, s.Client, s.AppName), s.RoutePath) {
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
}
|
||||||
if !assertContainsRoute(ListRoutes(t, s.Context, s.Client, s.AppName), s.RoutePath) {
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
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()
|
||||||
t.Run("can-get-corresponding-route", func(t *testing.T) {
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
t.Parallel()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
s := SetupDefaultSuite()
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
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,
|
rObjects := []*models.Route{GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)}
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
if !assertContainsRoute(rObjects, s.RoutePath) {
|
||||||
|
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
|
||||||
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)
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
func TestCanUpdateRouteType(t *testing.T) {
|
||||||
})
|
newRouteType := "sync"
|
||||||
|
t.Parallel()
|
||||||
t.Run("can-update-route-info", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s := SetupDefaultSuite()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
routeResp, err := UpdateRoute(
|
||||||
|
t, s.Context, s.Client,
|
||||||
routeResp, err := UpdateRoute(
|
s.AppName, s.RoutePath,
|
||||||
t, s.Context, s.Client,
|
s.Image, newRouteType, s.Format,
|
||||||
s.AppName, s.RoutePath,
|
s.Memory, s.RouteConfig, s.RouteHeaders, "")
|
||||||
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)
|
||||||
CheckRouteResponseError(t, err)
|
|
||||||
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, newRouteType, s.Format)
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestCanUpdateRouteConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
t.Run("patch-route-with-config", func(t *testing.T) {
|
s := SetupDefaultSuite()
|
||||||
t.Parallel()
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s := SetupDefaultSuite()
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
newRouteConf := map[string]string{
|
||||||
|
"A": "a",
|
||||||
newRouteConf := map[string]string{
|
}
|
||||||
"A": "a",
|
|
||||||
}
|
routeResp, err := UpdateRoute(
|
||||||
|
t, s.Context, s.Client,
|
||||||
routeResp, err := UpdateRoute(
|
s.AppName, s.RoutePath,
|
||||||
t, s.Context, s.Client,
|
s.Image, s.RouteType, s.Format,
|
||||||
s.AppName, s.RoutePath,
|
s.Memory, newRouteConf, s.RouteHeaders, "")
|
||||||
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)
|
||||||
CheckRouteResponseError(t, err)
|
|
||||||
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, s.RouteType, s.Format)
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
func TestCantUpdateRoutePath(t *testing.T) {
|
||||||
|
|
||||||
t.Run("fail-to-update-route-path", func(t *testing.T) {
|
t.Parallel()
|
||||||
t.Parallel()
|
newRoutePath := id.New().String()
|
||||||
s := SetupDefaultSuite()
|
s := SetupDefaultSuite()
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
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,
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
|
|
||||||
_, err := UpdateRoute(
|
_, err := UpdateRoute(
|
||||||
t, s.Context, s.Client,
|
t, s.Context, s.Client,
|
||||||
s.AppName, s.RoutePath,
|
s.AppName, s.RoutePath,
|
||||||
s.Image, s.RouteType, s.Format,
|
s.Image, s.RouteType, s.Format,
|
||||||
s.Memory, s.RouteConfig, s.RouteHeaders, newRoutePath)
|
s.Memory, s.RouteConfig, s.RouteHeaders, newRoutePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Route path suppose to be immutable, but it's not.")
|
t.Errorf("Route path suppose to be immutable, but it's not.")
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
})
|
}
|
||||||
|
|
||||||
t.Run("create-route-duplicate", func(t *testing.T) {
|
func TestRouteDuplicate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
s := SetupDefaultSuite()
|
newRouteType := "async"
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s := SetupDefaultSuite()
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
_, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath,
|
|
||||||
newRouteType, s.Format, s.RouteConfig, s.RouteHeaders)
|
_, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath,
|
||||||
if err == nil {
|
newRouteType, s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
t.Errorf("Route duplicate error should appear, but it didn't")
|
if err == nil {
|
||||||
}
|
t.Errorf("Route duplicate error should appear, but it didn't")
|
||||||
|
}
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
t.Run("can-delete-route", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
func TestCanDeleteRoute(t *testing.T) {
|
||||||
s := SetupDefaultSuite()
|
t.Parallel()
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s := SetupDefaultSuite()
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
|
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
|
||||||
})
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
t.Run("fail-to-delete-missing-route", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
func TestCantDeleteRoute(t *testing.T) {
|
||||||
s := SetupDefaultSuite()
|
t.Parallel()
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s := SetupDefaultSuite()
|
||||||
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
_, err := deleteRoute(s.Context, s.Client, s.AppName, "dummy-route")
|
|
||||||
if err == nil {
|
_, err := deleteRoute(s.Context, s.Client, s.AppName, "dummy-route")
|
||||||
t.Error("Delete from missing route must fail.")
|
if err == nil {
|
||||||
}
|
t.Error("Delete from missing route must fail.")
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
}
|
||||||
})
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
t.Run("deploy-route-without-existing-app", func(T *testing.T) {
|
|
||||||
t.Parallel()
|
func TestDeployNewApp(t *testing.T) {
|
||||||
s := SetupDefaultSuite()
|
t.Parallel()
|
||||||
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
|
s := SetupDefaultSuite()
|
||||||
GetApp(t, s.Context, s.Client, s.AppName)
|
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
|
||||||
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
|
GetApp(t, s.Context, s.Client, s.AppName)
|
||||||
DeleteApp(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)
|
||||||
|
}
|
||||||
t.Run("deploy-route-with-existing-app", func(T *testing.T) {
|
|
||||||
s := SetupDefaultSuite()
|
func TestDeployExistingApp(t *testing.T) {
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s := SetupDefaultSuite()
|
||||||
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
GetApp(t, s.Context, s.Client, s.AppName)
|
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
|
||||||
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
|
GetApp(t, s.Context, s.Client, s.AppName)
|
||||||
DeleteApp(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)
|
||||||
|
}
|
||||||
t.Run("deploy-update-with-existing-route-and-app", func(T *testing.T) {
|
|
||||||
newRouteType := "sync"
|
func TestDeployUpdate(t *testing.T) {
|
||||||
s := SetupDefaultSuite()
|
newRouteType := "sync"
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s := SetupDefaultSuite()
|
||||||
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
|
||||||
|
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
|
||||||
updatedRoute := DeployRoute(
|
|
||||||
t, s.Context, s.Client,
|
updatedRoute := DeployRoute(
|
||||||
s.AppName, s.RoutePath,
|
t, s.Context, s.Client,
|
||||||
s.Image, newRouteType,
|
s.AppName, s.RoutePath,
|
||||||
s.Format, s.RouteConfig, s.RouteHeaders)
|
s.Image, newRouteType,
|
||||||
assertRouteFields(t, updatedRoute, s.RoutePath, s.Image, newRouteType, s.Format)
|
s.Format, s.RouteConfig, s.RouteHeaders)
|
||||||
|
assertRouteFields(t, updatedRoute, s.RoutePath, s.Image, newRouteType, s.Format)
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
|
||||||
})
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
}
|
||||||
t.Run("multiple-deploy-route-with-headers", func(T *testing.T) {
|
|
||||||
s := SetupDefaultSuite()
|
func TestMulpileDeployExistingApp(t *testing.T) {
|
||||||
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
s := SetupDefaultSuite()
|
||||||
routeHeaders := map[string][]string{}
|
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
|
||||||
routeHeaders["A"] = []string{"a"}
|
routeHeaders := map[string][]string{}
|
||||||
routeHeaders["B"] = []string{"b"}
|
routeHeaders["A"] = []string{"a"}
|
||||||
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
|
routeHeaders["B"] = []string{"b"}
|
||||||
sameRoute := DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
|
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
|
||||||
if !api_models.Headers(sameRoute.Headers).Equals(api_models.Headers(routeHeaders)) {
|
sameRoute := DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
|
||||||
t.Error("Route headers should remain the same after multiple deploys with exact the same parameters")
|
if ok := reflect.DeepEqual(sameRoute.Headers, routeHeaders); !ok {
|
||||||
}
|
t.Error("Route headers should remain the same after multiple deploys with exact the same parameters")
|
||||||
DeleteApp(t, s.Context, s.Client, s.AppName)
|
}
|
||||||
})
|
DeleteApp(t, s.Context, s.Client, s.AppName)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fnproject/fn/api/common"
|
"github.com/fnproject/fn/api/common"
|
||||||
@@ -104,6 +105,8 @@ type SuiteSetup struct {
|
|||||||
RouteType string
|
RouteType string
|
||||||
Format string
|
Format string
|
||||||
Memory uint64
|
Memory uint64
|
||||||
|
Timeout int32
|
||||||
|
IdleTimeout int32
|
||||||
RouteConfig map[string]string
|
RouteConfig map[string]string
|
||||||
RouteHeaders map[string][]string
|
RouteHeaders map[string][]string
|
||||||
Cancel context.CancelFunc
|
Cancel context.CancelFunc
|
||||||
@@ -131,6 +134,8 @@ func SetupDefaultSuite() *SuiteSetup {
|
|||||||
RouteHeaders: map[string][]string{},
|
RouteHeaders: map[string][]string{},
|
||||||
Cancel: cancel,
|
Cancel: cancel,
|
||||||
Memory: uint64(256),
|
Memory: uint64(256),
|
||||||
|
Timeout: int32(30),
|
||||||
|
IdleTimeout: int32(30),
|
||||||
}
|
}
|
||||||
|
|
||||||
if Host() != "localhost:8080" {
|
if Host() != "localhost:8080" {
|
||||||
@@ -224,3 +229,16 @@ func MyCaller() string {
|
|||||||
f, l := fun.FileLine(fpcs[0] - 1)
|
f, l := fun.FileLine(fpcs[0] - 1)
|
||||||
return fmt.Sprintf("%s:%d", f, l)
|
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()
|
||||||
|
if err == nil {
|
||||||
|
t.Log("Exiting retry loop, API call was successful")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(sleep)
|
||||||
|
t.Logf("[%v] - Retryting API call after unsuccessful attemt with error: %v", i, err.Error())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user