Files
fn-serverless/test/fn-system-tests/exec_test.go
Tolga Ceylan 881a0ba1db fn: agent call overrider (#1080)
Similar to LB Agent call overrider, this PR adds Agent overrider
for Agents to modify/analyze a Call/Extensions during GetCall().
2018-06-20 16:21:09 -07:00

356 lines
8.5 KiB
Go

package tests
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"testing"
"time"
apiutils "github.com/fnproject/fn/test/fn-api-tests"
sdkmodels "github.com/fnproject/fn_go/models"
)
// See fn-test-utils for json response
func getEchoContent(respBytes []byte) (string, error) {
var respJs map[string]interface{}
err := json.Unmarshal(respBytes, &respJs)
if err != nil {
return "", err
}
req, ok := respJs["request"].(map[string]interface{})
if !ok {
return "", errors.New("unexpected json: request map")
}
echo, ok := req["echoContent"].(string)
if !ok {
return "", errors.New("unexpected json: echoContent string")
}
return echo, nil
}
// See fn-test-utils for json response
func getConfigContent(key string, respBytes []byte) (string, error) {
var respJs map[string]interface{}
err := json.Unmarshal(respBytes, &respJs)
if err != nil {
return "", err
}
cfg, ok := respJs["config"].(map[string]interface{})
if !ok {
return "", errors.New("unexpected json: config map")
}
val, ok := cfg[key].(string)
if !ok {
return "", fmt.Errorf("unexpected json: %s string", key)
}
return val, nil
}
func TestCanExecuteFunction(t *testing.T) {
s := apiutils.SetupHarness()
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Image = "fnproject/fn-test-utils"
rt.Format = "json"
rt.Memory = 64
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
u := url.URL{
Scheme: "http",
Host: lb,
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
content := bytes.NewBuffer([]byte(body))
output := &bytes.Buffer{}
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
echo, err := getEchoContent(output.Bytes())
if err != nil || echo != "HelloWorld" {
t.Fatalf("getEchoContent/HelloWorld check failed on %v", output)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("StatusCode check failed on %v", resp.StatusCode)
}
// Now let's check FN_CHEESE, since LB and runners have override/extension mechanism
// to insert FN_CHEESE into config
cheese, err := getConfigContent("FN_CHEESE", output.Bytes())
if err != nil || cheese != "Tete de Moine" {
t.Fatalf("getConfigContent/FN_CHEESE check failed (%v) on %v", err, output)
}
// Now let's check FN_WINE, since runners have override to insert this.
wine, err := getConfigContent("FN_WINE", output.Bytes())
if err != nil || wine != "1982 Margaux" {
t.Fatalf("getConfigContent/FN_WINE check failed (%v) on %v", err, output)
}
}
func TestCanExecuteBigOutput(t *testing.T) {
s := apiutils.SetupHarness()
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Image = "fnproject/fn-test-utils"
rt.Format = "json"
rt.Memory = 64
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
u := url.URL{
Scheme: "http",
Host: lb,
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
// Approx 5.3MB output
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true, "trailerRepeat": 410000}`
content := bytes.NewBuffer([]byte(body))
output := &bytes.Buffer{}
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
t.Logf("getEchoContent/HelloWorld size %d", len(output.Bytes()))
echo, err := getEchoContent(output.Bytes())
if err != nil || echo != "HelloWorld" {
t.Fatalf("getEchoContent/HelloWorld check failed on %v", output)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("StatusCode check failed on %v", resp.StatusCode)
}
}
func TestCanExecuteTooBigOutput(t *testing.T) {
s := apiutils.SetupHarness()
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Image = "fnproject/fn-test-utils"
rt.Format = "json"
rt.Memory = 64
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
u := url.URL{
Scheme: "http",
Host: lb,
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
// > 6MB output
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true, "trailerRepeat": 600000}`
content := bytes.NewBuffer([]byte(body))
output := &bytes.Buffer{}
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
exp := "{\"error\":{\"message\":\"function response too large\"}}\n"
actual := output.String()
if !strings.Contains(exp, actual) || len(exp) != len(actual) {
t.Fatalf("Assertion error.\n\tExpected: %v\n\tActual: %v", exp, output.String())
}
if resp.StatusCode != http.StatusBadGateway {
t.Fatalf("StatusCode check failed on %v", resp.StatusCode)
}
}
func TestCanExecuteEmptyOutput(t *testing.T) {
s := apiutils.SetupHarness()
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Image = "fnproject/fn-test-utils"
rt.Format = "json"
rt.Memory = 64
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
u := url.URL{
Scheme: "http",
Host: lb,
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
// empty body output
body := `{"sleepTime": 0, "isDebug": true, "isEmptyBody": true}`
content := bytes.NewBuffer([]byte(body))
output := &bytes.Buffer{}
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
actual := output.String()
if 0 != len(actual) {
t.Fatalf("Assertion error.\n\tExpected empty\n\tActual: %v", output.String())
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("StatusCode check failed on %v", resp.StatusCode)
}
}
func TestBasicConcurrentExecution(t *testing.T) {
s := apiutils.SetupHarness()
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
defer s.Cleanup()
rt := s.BasicRoute()
rt.Image = "fnproject/fn-test-utils"
rt.Format = "json"
rt.Memory = 32
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
u := url.URL{
Scheme: "http",
Host: lb,
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
results := make(chan error)
concurrentFuncs := 10
for i := 0; i < concurrentFuncs; i++ {
go func() {
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
content := bytes.NewBuffer([]byte(body))
output := &bytes.Buffer{}
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
if err != nil {
results <- fmt.Errorf("Got unexpected error: %v", err)
return
}
echo, err := getEchoContent(output.Bytes())
if err != nil || echo != "HelloWorld" {
results <- fmt.Errorf("Assertion error.\n\tActual: %v", output.String())
return
}
if resp.StatusCode != http.StatusOK {
results <- fmt.Errorf("StatusCode check failed on %v", resp.StatusCode)
return
}
results <- nil
}()
}
for i := 0; i < concurrentFuncs; i++ {
err := <-results
if err != nil {
t.Fatalf("Error in basic concurrency execution test: %v", err)
}
}
}
func TestSaturatedSystem(t *testing.T) {
s := apiutils.SetupHarness()
// override default 60 secs with shorter.
s.Cancel()
s.Context, s.Cancel = context.WithTimeout(context.Background(), 4*time.Second)
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
defer s.Cleanup()
timeout := int32(1)
rt := s.BasicRoute()
rt.Image = "fnproject/fn-test-utils"
rt.Format = "json"
rt.Timeout = &timeout
rt.Memory = 300
rt.Type = "sync"
s.GivenRouteExists(t, s.AppName, rt)
lb, err := LB()
if err != nil {
t.Fatalf("Got unexpected error: %v", err)
}
u := url.URL{
Scheme: "http",
Host: lb,
}
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
content := bytes.NewBuffer([]byte(body))
output := &bytes.Buffer{}
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
if resp != nil || err == nil || s.Context.Err() == nil {
t.Fatalf("Expected response: %v err:%v", resp, err)
}
}