remove legacy console package and refresh syntax tests
This commit is contained in:
-2426
File diff suppressed because it is too large
Load Diff
@@ -1,284 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
)
|
||||
|
||||
func newTestServer(objs ...runtime.Object) *server {
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
environmentGVR: "EnvironmentList",
|
||||
packageGVR: "PackageList",
|
||||
functionGVR: "FunctionList",
|
||||
httpTrigGVR: "HTTPTriggerList",
|
||||
timeTrigGVR: "TimeTriggerList",
|
||||
}
|
||||
dyn := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), listKinds, objs...)
|
||||
return &server{dyn: dyn, ns: "default", routerURL: "http://example.invalid", http: &http.Client{}, saTokenPath: ""}
|
||||
}
|
||||
|
||||
func TestNormalizeMethods(t *testing.T) {
|
||||
got := normalizeMethods([]string{"get", " POST ", "get", ""})
|
||||
if len(got) != 2 || got[0] != "GET" || got[1] != "POST" {
|
||||
t.Fatalf("unexpected methods: %#v", got)
|
||||
}
|
||||
|
||||
fallback := normalizeMethods([]string{"", " "})
|
||||
if len(fallback) != 1 || fallback[0] != "GET" {
|
||||
t.Fatalf("expected default GET, got %#v", fallback)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFunctionValidation(t *testing.T) {
|
||||
s := newTestServer()
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/functions", strings.NewReader(`{"name":""}`))
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
s.handleCreateFunction(rec, req)
|
||||
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d: %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFunctionSuccessAndGetDetails(t *testing.T) {
|
||||
env := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "fission.io/v1",
|
||||
"kind": "Environment",
|
||||
"metadata": map[string]any{
|
||||
"name": "python",
|
||||
"namespace": "default",
|
||||
},
|
||||
}}
|
||||
s := newTestServer(env)
|
||||
|
||||
body := `{"name":"t-fn","environment":"python","code":"def main(ctx):\n return {\"ok\": True}","entrypoint":"main.main","route":"/t-fn","methods":["get"]}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/functions", strings.NewReader(body))
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleCreateFunction(rec, req)
|
||||
if rec.Code != http.StatusCreated {
|
||||
t.Fatalf("expected 201, got %d: %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
pkg, err := s.dyn.Resource(packageGVR).Namespace("default").Get(ctx, "t-fn-pkg", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("package not created: %v", err)
|
||||
}
|
||||
literal, _, _ := unstructured.NestedString(pkg.Object, "spec", "deployment", "literal")
|
||||
decoded, err := base64.StdEncoding.DecodeString(literal)
|
||||
if err != nil {
|
||||
t.Fatalf("decode literal: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(decoded), "def main") {
|
||||
t.Fatalf("unexpected package code: %q", string(decoded))
|
||||
}
|
||||
|
||||
getReq := httptest.NewRequest(http.MethodGet, "/api/functions/t-fn", nil)
|
||||
getRec := httptest.NewRecorder()
|
||||
s.handleGetFunction(getRec, getReq, "t-fn")
|
||||
if getRec.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", getRec.Code, getRec.Body.String())
|
||||
}
|
||||
|
||||
var out map[string]any
|
||||
if err := json.Unmarshal(getRec.Body.Bytes(), &out); err != nil {
|
||||
t.Fatalf("decode get response: %v", err)
|
||||
}
|
||||
if out["name"] != "t-fn" {
|
||||
t.Fatalf("unexpected name: %#v", out["name"])
|
||||
}
|
||||
if out["environment"] != "python" {
|
||||
t.Fatalf("unexpected environment: %#v", out["environment"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateFunctionCode(t *testing.T) {
|
||||
env := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "fission.io/v1",
|
||||
"kind": "Environment",
|
||||
"metadata": map[string]any{"name": "python", "namespace": "default"},
|
||||
}}
|
||||
s := newTestServer(env)
|
||||
|
||||
createReq := httptest.NewRequest(http.MethodPost, "/api/functions", strings.NewReader(`{"name":"upd-fn","environment":"python","code":"old","entrypoint":"main.main"}`))
|
||||
createRec := httptest.NewRecorder()
|
||||
s.handleCreateFunction(createRec, createReq)
|
||||
if createRec.Code != http.StatusCreated {
|
||||
t.Fatalf("create failed: %d %s", createRec.Code, createRec.Body.String())
|
||||
}
|
||||
|
||||
updReq := httptest.NewRequest(http.MethodPut, "/api/functions/upd-fn/code", bytes.NewBufferString(`{"code":"new-code"}`))
|
||||
updRec := httptest.NewRecorder()
|
||||
s.handleUpdateFunctionCode(updRec, updReq, "upd-fn")
|
||||
if updRec.Code != http.StatusOK {
|
||||
t.Fatalf("update failed: %d %s", updRec.Code, updRec.Body.String())
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
pkg, err := s.dyn.Resource(packageGVR).Namespace("default").Get(ctx, "upd-fn-pkg", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("get package: %v", err)
|
||||
}
|
||||
literal, _, _ := unstructured.NestedString(pkg.Object, "spec", "deployment", "literal")
|
||||
decoded, err := base64.StdEncoding.DecodeString(literal)
|
||||
if err != nil {
|
||||
t.Fatalf("decode literal: %v", err)
|
||||
}
|
||||
if string(decoded) != "new-code" {
|
||||
t.Fatalf("expected new-code, got %q", string(decoded))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFunctionUsesSourceLiteralWhenDeploymentLiteralMissing(t *testing.T) {
|
||||
env := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "fission.io/v1",
|
||||
"kind": "Environment",
|
||||
"metadata": map[string]any{"name": "go-acc", "namespace": "default"},
|
||||
}}
|
||||
|
||||
s := newTestServer(env)
|
||||
|
||||
pkg := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "fission.io/v1",
|
||||
"kind": "Package",
|
||||
"metadata": map[string]any{
|
||||
"name": "fn-go-acc-pkg",
|
||||
"namespace": "default",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"source": map[string]any{
|
||||
"literal": base64.StdEncoding.EncodeToString([]byte("package main\n\nfunc Handler() {}\n")),
|
||||
},
|
||||
"deployment": map[string]any{
|
||||
"type": "url",
|
||||
"url": "http://storagesvc.fission/v1/archive?id=dummy",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
fn := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "fission.io/v1",
|
||||
"kind": "Function",
|
||||
"metadata": map[string]any{
|
||||
"name": "fn-go-acc",
|
||||
"namespace": "default",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"environment": map[string]any{"name": "go-acc", "namespace": "default"},
|
||||
"package": map[string]any{
|
||||
"functionName": "Handler",
|
||||
"packageref": map[string]any{
|
||||
"name": "fn-go-acc-pkg",
|
||||
"namespace": "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
if _, err := s.dyn.Resource(packageGVR).Namespace("default").Create(context.Background(), pkg, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatalf("create package: %v", err)
|
||||
}
|
||||
if _, err := s.dyn.Resource(functionGVR).Namespace("default").Create(context.Background(), fn, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatalf("create function: %v", err)
|
||||
}
|
||||
|
||||
getReq := httptest.NewRequest(http.MethodGet, "/api/functions/fn-go-acc", nil)
|
||||
getRec := httptest.NewRecorder()
|
||||
s.handleGetFunction(getRec, getReq, "fn-go-acc")
|
||||
if getRec.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", getRec.Code, getRec.Body.String())
|
||||
}
|
||||
|
||||
var out map[string]any
|
||||
if err := json.Unmarshal(getRec.Body.Bytes(), &out); err != nil {
|
||||
t.Fatalf("decode get response: %v", err)
|
||||
}
|
||||
|
||||
code, _ := out["code"].(string)
|
||||
if !strings.Contains(code, "func Handler") {
|
||||
t.Fatalf("expected source code from spec.source.literal, got %q", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvokeFunctionWithJWTAuth(t *testing.T) {
|
||||
// Mock router: /auth/login returns JWT, /inv-fn returns hello
|
||||
var gotAuth string
|
||||
mockRouter := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/auth/login" && r.Method == http.MethodPost {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"accesstoken":"fake-jwt-token-xyz","tokentype":"Bearer"}`))
|
||||
return
|
||||
}
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{"hello":"world"}`))
|
||||
}))
|
||||
defer mockRouter.Close()
|
||||
|
||||
env := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "fission.io/v1",
|
||||
"kind": "Environment",
|
||||
"metadata": map[string]any{"name": "python", "namespace": "default"},
|
||||
}}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
environmentGVR: "EnvironmentList",
|
||||
packageGVR: "PackageList",
|
||||
functionGVR: "FunctionList",
|
||||
httpTrigGVR: "HTTPTriggerList",
|
||||
timeTrigGVR: "TimeTriggerList",
|
||||
}
|
||||
dyn := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), listKinds, env)
|
||||
s := &server{
|
||||
dyn: dyn,
|
||||
ns: "default",
|
||||
routerURL: mockRouter.URL,
|
||||
http: mockRouter.Client(),
|
||||
authUser: "admin",
|
||||
authPass: "pass",
|
||||
}
|
||||
|
||||
// Create function first
|
||||
createBody := `{"name":"inv-fn","environment":"python","code":"print(1)","route":"/inv-fn","methods":["GET"]}`
|
||||
createRec := httptest.NewRecorder()
|
||||
s.handleCreateFunction(createRec, httptest.NewRequest(http.MethodPost, "/api/functions", strings.NewReader(createBody)))
|
||||
if createRec.Code != http.StatusCreated {
|
||||
t.Fatalf("create: %d %s", createRec.Code, createRec.Body.String())
|
||||
}
|
||||
|
||||
// Invoke
|
||||
invokeRec := httptest.NewRecorder()
|
||||
s.handleInvokeFunction(invokeRec, httptest.NewRequest(http.MethodPost, "/api/functions/inv-fn/invoke", strings.NewReader(`{}`)), "inv-fn")
|
||||
if invokeRec.Code != http.StatusOK {
|
||||
t.Fatalf("invoke: %d %s", invokeRec.Code, invokeRec.Body.String())
|
||||
}
|
||||
|
||||
// Verify JWT was obtained via login and sent
|
||||
if gotAuth != "Bearer fake-jwt-token-xyz" {
|
||||
t.Fatalf("expected 'Bearer fake-jwt-token-xyz', got %q", gotAuth)
|
||||
}
|
||||
|
||||
var out map[string]any
|
||||
if err := json.Unmarshal(invokeRec.Body.Bytes(), &out); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if out["status"] != float64(200) {
|
||||
t.Fatalf("expected status 200, got %v", out["status"])
|
||||
}
|
||||
if !strings.Contains(out["response_raw"].(string), "hello") {
|
||||
t.Fatalf("unexpected response: %v", out["response_raw"])
|
||||
}
|
||||
}
|
||||
+18
-1
@@ -38,6 +38,23 @@ init_call() {
|
||||
printf '%s' "$code"
|
||||
}
|
||||
|
||||
invoke_call() {
|
||||
local user="$1"
|
||||
local fn="$2"
|
||||
local code=""
|
||||
local try
|
||||
for try in 1 2 3 4; do
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 40 -X POST "${BASE}/functions/${fn}/invoke" -H "X-Test-Sub: ${user}" -H "Content-Type: application/json" -d '{}' 2>/dev/null || echo 000)
|
||||
echo "INVOKE_TRY user=${user} fn=${fn} try=${try} http=${code}" >&2
|
||||
if [ "$code" = "200" ]; then
|
||||
printf '%s' "$code"
|
||||
return 0
|
||||
fi
|
||||
[ "$try" -lt 4 ] && sleep 4
|
||||
done
|
||||
printf '%s' "$code"
|
||||
}
|
||||
|
||||
echo "=== MULTIUSER TEST START run=$RUN_ID count=$COUNT ==="
|
||||
|
||||
for i in $(seq 1 "$COUNT"); do
|
||||
@@ -93,7 +110,7 @@ for i in $(seq 1 "$COUNT"); do
|
||||
(
|
||||
U="${RUN_ID}-u${i}@test.local"
|
||||
FN="mtfn-${RUN_ID}-${i}"
|
||||
C=$(curl -s -o /dev/null -w "%{http_code}" --max-time 40 -X POST "${BASE}/functions/${FN}/invoke" -H "X-Test-Sub: ${U}" -H "Content-Type: application/json" -d '{}' 2>/dev/null || echo 000)
|
||||
C=$(invoke_call "${U}" "${FN}")
|
||||
echo "$i:$C" > "${TMP}/invoke-$i"
|
||||
) &
|
||||
done
|
||||
|
||||
+16
-16
@@ -116,14 +116,14 @@ check N04 "невалидный JS" nodejs fail \
|
||||
'const x = @@@;
|
||||
module.exports = function(ctx) { return x; };'
|
||||
|
||||
# WRONG ENTRYPOINT: exports nothing
|
||||
check N05 "нет module.exports (нет entrypoint)" nodejs fail \
|
||||
# SYNTAX-ONLY: exports nothing, но синтаксис корректен
|
||||
check N05 "нет module.exports, но синтаксис корректен" nodejs ok \
|
||||
'async function doStuff(context) {
|
||||
return { status: 200, body: "ok" };
|
||||
}'
|
||||
|
||||
# SEMANTIC: returns wrong shape (no status)
|
||||
check N06 "возвращает строку вместо объекта" nodejs fail \
|
||||
# SYNTAX-ONLY: возвращает строку, но синтаксис корректен
|
||||
check N06 "возвращает строку, но синтаксис корректен" nodejs ok \
|
||||
'module.exports = async function(context) {
|
||||
return "just a string";
|
||||
};'
|
||||
@@ -153,13 +153,13 @@ check P04 "невалидный синтаксис Python" python fail \
|
||||
x = @@@
|
||||
return x'
|
||||
|
||||
# WRONG ENTRYPOINT
|
||||
check P05 "нет функции main/handler" python fail \
|
||||
# SYNTAX-ONLY: нет main/handler, но синтаксис корректен
|
||||
check P05 "нет main/handler, но синтаксис корректен" python ok \
|
||||
'def process(data):
|
||||
return "ok"'
|
||||
|
||||
# SEMANTIC: unused import, no return
|
||||
check P06 "функция без return" python fail \
|
||||
# SYNTAX-ONLY: функция без return, но синтаксис корректен
|
||||
check P06 "функция без return, но синтаксис корректен" python ok \
|
||||
'def main(data):
|
||||
name = data.get("name", "World")
|
||||
print("Hello " + name)'
|
||||
@@ -192,8 +192,8 @@ func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello")
|
||||
}'
|
||||
|
||||
# WRONG SIGNATURE
|
||||
check G03 "неверная сигнатура Handler" go fail \
|
||||
# SYNTAX-ONLY: неверная сигнатура Handler, но синтаксис корректен
|
||||
check G03 "неверная сигнатура Handler, но синтаксис корректен" go ok \
|
||||
'package main
|
||||
|
||||
import "fmt"
|
||||
@@ -202,8 +202,8 @@ func Handler(name string) string {
|
||||
return fmt.Sprintf("Hello %s", name)
|
||||
}'
|
||||
|
||||
# WRONG PACKAGE
|
||||
check G04 "неверный package" go fail \
|
||||
# SYNTAX-ONLY: неверный package, но синтаксис корректен
|
||||
check G04 "неверный package, но синтаксис корректен" go ok \
|
||||
'package utils
|
||||
|
||||
import "net/http"
|
||||
@@ -225,8 +225,8 @@ check R02 "синтаксическая ошибка Ruby" ruby fail \
|
||||
{ status: 200 body: "missing comma" }
|
||||
end'
|
||||
|
||||
# NO ENTRYPOINT
|
||||
check R03 "нет функции handler" ruby fail \
|
||||
# SYNTAX-ONLY: нет handler, но синтаксис корректен
|
||||
check R03 "нет handler, но синтаксис корректен" ruby ok \
|
||||
'def process(ctx)
|
||||
{ status: 200, body: "ok" }
|
||||
end'
|
||||
@@ -250,8 +250,8 @@ function handler(array $context): array {
|
||||
return ["status" => 200, "body" => "Hello " . $name];
|
||||
}'
|
||||
|
||||
# NO ENTRYPOINT
|
||||
check H03 "нет функции handler в PHP" php fail \
|
||||
# SYNTAX-ONLY: нет handler, но синтаксис корректен
|
||||
check H03 "нет функции handler в PHP, но синтаксис корректен" php ok \
|
||||
'<?php
|
||||
function process(array $data): array {
|
||||
return ["status" => 200];
|
||||
|
||||
Reference in New Issue
Block a user