mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
* functions: add bounded concurrency * functions: plug runners to sync and async interfaces * functions: update documentation about the new env var * functions: fix test flakiness * functions: the runner is self-regulated, no need to set a number of runners * functions: push the execution to the background on incoming requests * functions: ensure async tasks are always on * functions: add prioritization to tasks consumption Ensure that Sync tasks are consumed before Async tasks. Also, fixes termination races problems for free. * functions: remove stale comments * functions: improve mem availability calculation * functions: parallel run for async tasks * functions: check for memory availability before pulling async task * functions: comment about rnr.hasAvailableMemory and sync.Cond * functions: implement memory check for async runners using Cond vars * functions: code grooming - remove unnecessary goroutines - fix stale docs - reorganize import group * Revert "functions: implement memory check for async runners using Cond vars" This reverts commit 922e64032201a177c03ce6a46240925e3d35430d. * Revert "functions: comment about rnr.hasAvailableMemory and sync.Cond" This reverts commit 49ad7d52d341f12da9603b1a1df9d145871f0e0a. * functions: set a minimum memory availability for sync * functions: simplify the implementation by removing the priority queue * functions: code grooming - code deduplication - review waitgroups Waits
238 lines
6.3 KiB
Go
238 lines
6.3 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/iron-io/functions/api/datastore"
|
|
"github.com/iron-io/functions/api/models"
|
|
"github.com/iron-io/functions/api/mqs"
|
|
"github.com/iron-io/functions/api/runner"
|
|
)
|
|
|
|
func setLogBuffer() *bytes.Buffer {
|
|
var buf bytes.Buffer
|
|
buf.WriteByte('\n')
|
|
logrus.SetOutput(&buf)
|
|
gin.DefaultErrorWriter = &buf
|
|
gin.DefaultWriter = &buf
|
|
log.SetOutput(&buf)
|
|
return &buf
|
|
}
|
|
|
|
func mockTasksConduit() chan runner.TaskRequest {
|
|
tasks := make(chan runner.TaskRequest)
|
|
go func() {
|
|
for range tasks {
|
|
}
|
|
}()
|
|
return tasks
|
|
}
|
|
|
|
func TestAppCreate(t *testing.T) {
|
|
buf := setLogBuffer()
|
|
tasks := mockTasksConduit()
|
|
defer close(tasks)
|
|
for i, test := range []struct {
|
|
mock *datastore.Mock
|
|
path string
|
|
body string
|
|
expectedCode int
|
|
expectedError error
|
|
}{
|
|
// errors
|
|
{&datastore.Mock{}, "/v1/apps", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
|
{&datastore.Mock{}, "/v1/apps", `{}`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
|
{&datastore.Mock{}, "/v1/apps", `{ "name": "Test" }`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
|
{&datastore.Mock{}, "/v1/apps", `{ "app": { "name": "" } }`, http.StatusInternalServerError, models.ErrAppsValidationMissingName},
|
|
{&datastore.Mock{}, "/v1/apps", `{ "app": { "name": "1234567890123456789012345678901" } }`, http.StatusInternalServerError, models.ErrAppsValidationTooLongName},
|
|
{&datastore.Mock{}, "/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
|
{&datastore.Mock{}, "/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
|
|
|
// success
|
|
{&datastore.Mock{}, "/v1/apps", `{ "app": { "name": "teste" } }`, http.StatusCreated, nil},
|
|
} {
|
|
s := New(test.mock, &mqs.Mock{}, testRunner(t), tasks)
|
|
router := testRouter(s)
|
|
|
|
body := bytes.NewBuffer([]byte(test.body))
|
|
_, rec := routerRequest(t, router, "POST", test.path, body)
|
|
|
|
if rec.Code != test.expectedCode {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
|
i, test.expectedCode, rec.Code)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
resp := getErrorResponse(t, rec)
|
|
|
|
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected error message to have `%s`",
|
|
i, test.expectedError.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppDelete(t *testing.T) {
|
|
buf := setLogBuffer()
|
|
tasks := mockTasksConduit()
|
|
defer close(tasks)
|
|
|
|
s := New(&datastore.Mock{}, &mqs.Mock{}, testRunner(t), tasks)
|
|
router := testRouter(s)
|
|
|
|
for i, test := range []struct {
|
|
path string
|
|
body string
|
|
expectedCode int
|
|
expectedError error
|
|
}{
|
|
{"/v1/apps", "", http.StatusNotFound, nil},
|
|
{"/v1/apps/myapp", "", http.StatusOK, nil},
|
|
} {
|
|
_, rec := routerRequest(t, router, "DELETE", test.path, nil)
|
|
|
|
if rec.Code != test.expectedCode {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
|
i, test.expectedCode, rec.Code)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
resp := getErrorResponse(t, rec)
|
|
|
|
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected error message to have `%s`",
|
|
i, test.expectedError.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppList(t *testing.T) {
|
|
buf := setLogBuffer()
|
|
tasks := mockTasksConduit()
|
|
defer close(tasks)
|
|
|
|
s := New(&datastore.Mock{}, &mqs.Mock{}, testRunner(t), tasks)
|
|
router := testRouter(s)
|
|
|
|
for i, test := range []struct {
|
|
path string
|
|
body string
|
|
expectedCode int
|
|
expectedError error
|
|
}{
|
|
{"/v1/apps", "", http.StatusOK, nil},
|
|
} {
|
|
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
|
|
|
if rec.Code != test.expectedCode {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
|
i, test.expectedCode, rec.Code)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
resp := getErrorResponse(t, rec)
|
|
|
|
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected error message to have `%s`",
|
|
i, test.expectedError.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppGet(t *testing.T) {
|
|
buf := setLogBuffer()
|
|
tasks := mockTasksConduit()
|
|
defer close(tasks)
|
|
|
|
s := New(&datastore.Mock{}, &mqs.Mock{}, testRunner(t), tasks)
|
|
router := testRouter(s)
|
|
|
|
for i, test := range []struct {
|
|
path string
|
|
body string
|
|
expectedCode int
|
|
expectedError error
|
|
}{
|
|
{"/v1/apps/myapp", "", http.StatusNotFound, nil},
|
|
} {
|
|
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
|
|
|
if rec.Code != test.expectedCode {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
|
i, test.expectedCode, rec.Code)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
resp := getErrorResponse(t, rec)
|
|
|
|
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected error message to have `%s`",
|
|
i, test.expectedError.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppUpdate(t *testing.T) {
|
|
buf := setLogBuffer()
|
|
tasks := mockTasksConduit()
|
|
defer close(tasks)
|
|
|
|
for i, test := range []struct {
|
|
mock *datastore.Mock
|
|
path string
|
|
body string
|
|
expectedCode int
|
|
expectedError error
|
|
}{
|
|
// errors
|
|
{&datastore.Mock{}, "/v1/apps/myapp", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
|
|
|
// success
|
|
{&datastore.Mock{
|
|
FakeApp: &models.App{
|
|
Name: "myapp",
|
|
},
|
|
}, "/v1/apps/myapp", `{ "app": { "config": { "test": "1" } } }`, http.StatusOK, nil},
|
|
} {
|
|
s := New(test.mock, &mqs.Mock{}, testRunner(t), tasks)
|
|
router := testRouter(s)
|
|
|
|
body := bytes.NewBuffer([]byte(test.body))
|
|
_, rec := routerRequest(t, router, "PUT", test.path, body)
|
|
|
|
if rec.Code != test.expectedCode {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
|
i, test.expectedCode, rec.Code)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
resp := getErrorResponse(t, rec)
|
|
|
|
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
|
t.Log(buf.String())
|
|
t.Errorf("Test %d: Expected error message to have `%s`",
|
|
i, test.expectedError.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|