Builder SoC (builder/context.go):
- Moved generateDockerfile, runtimeBaseImage, zipToTarGz from handler/upload.go
to internal/builder/context.go.
Reason: knowledge about runtime images and build context structure is a
build concern, not an HTTP handler concern.
- Added PrepareContext(zipData []byte, runtime string) (*bytes.Buffer, error) —
single public entry point. Handler calls one function, gets ready buffer.
- zipToTarGz now accepts *zip.Reader instead of []byte to avoid double parsing.
- upload.go reduced from ~200 LOC to ~60 LOC (build logic gone).
auth.go — JWKS insertion point:
- Added verifySignature() stub with detailed comment explaining what v2
implementation needs (JWKS endpoint, kid lookup, RS256/ES256 verify).
- Shows exactly where to add the call in validateJWT.
Unit tests (9 total, all pass):
- controllers: TestBuildDeployment_EnvVarsSorted, TestBuildDeployment_EmptyEnv
- handler: TestHopByHopHeaders_* (3 tests)
- builder: TestPrepareContext_PythonWithRequirements, _NodeNoPackageJSON,
_UnsupportedRuntime, _DockerfileIsFirst
86 lines
2.7 KiB
Go
86 lines
2.7 KiB
Go
// Создано: 2026-03-11
|
||
// Юнит-тесты для FunctionReconciler (без k8s envtest).
|
||
// Проверяют логику которую можно тестировать изолированно.
|
||
|
||
package controllers
|
||
|
||
import (
|
||
"testing"
|
||
|
||
slessv1alpha1 "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/api/v1alpha1"
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
)
|
||
|
||
// TestBuildDeployment_EnvVarsSorted проверяет что env vars в Deployment всегда
|
||
// идут в алфавитном порядке — независимо от порядка в map.
|
||
// Важно: нестабильный порядок приводит к лишним pod restarts в k8s.
|
||
func TestBuildDeployment_EnvVarsSorted(t *testing.T) {
|
||
r := &FunctionReconciler{
|
||
RegistrySecret: "",
|
||
}
|
||
|
||
fn := &slessv1alpha1.Function{
|
||
ObjectMeta: metav1.ObjectMeta{Name: "test-fn", Namespace: "test-ns"},
|
||
Spec: slessv1alpha1.FunctionSpec{
|
||
Entrypoint: "handler.handle",
|
||
MemoryMB: 128,
|
||
Env: map[string]string{
|
||
"ZEBRA": "last",
|
||
"ALPHA": "first",
|
||
"MIDDLE": "middle",
|
||
"DATABASE": "url",
|
||
},
|
||
},
|
||
Status: slessv1alpha1.FunctionStatus{
|
||
ImageRef: "registry/test:abc123",
|
||
},
|
||
}
|
||
|
||
dep := r.buildDeployment(fn, "sless-fn-test-ns")
|
||
envs := dep.Spec.Template.Spec.Containers[0].Env
|
||
|
||
// Первый env всегда SLESS_ENTRYPOINT
|
||
if envs[0].Name != "SLESS_ENTRYPOINT" {
|
||
t.Fatalf("first env should be SLESS_ENTRYPOINT, got %s", envs[0].Name)
|
||
}
|
||
|
||
// Остальные — отсортированы по алфавиту
|
||
userEnvs := envs[1:]
|
||
for i := 1; i < len(userEnvs); i++ {
|
||
if userEnvs[i].Name < userEnvs[i-1].Name {
|
||
t.Errorf("env vars not sorted at index %d: %s before %s",
|
||
i, userEnvs[i-1].Name, userEnvs[i].Name)
|
||
}
|
||
}
|
||
|
||
// Все 4 ключа присутствуют
|
||
if len(userEnvs) != 4 {
|
||
t.Errorf("expected 4 user env vars, got %d", len(userEnvs))
|
||
}
|
||
}
|
||
|
||
// TestBuildDeployment_EmptyEnv проверяет что функция без env vars корректно
|
||
// создаёт Deployment только с SLESS_ENTRYPOINT.
|
||
func TestBuildDeployment_EmptyEnv(t *testing.T) {
|
||
r := &FunctionReconciler{}
|
||
|
||
fn := &slessv1alpha1.Function{
|
||
ObjectMeta: metav1.ObjectMeta{Name: "bare-fn", Namespace: "ns"},
|
||
Spec: slessv1alpha1.FunctionSpec{
|
||
Entrypoint: "main.run",
|
||
MemoryMB: 64,
|
||
},
|
||
Status: slessv1alpha1.FunctionStatus{ImageRef: "reg/bare:tag"},
|
||
}
|
||
|
||
dep := r.buildDeployment(fn, "sless-fn-ns")
|
||
envs := dep.Spec.Template.Spec.Containers[0].Env
|
||
|
||
if len(envs) != 1 {
|
||
t.Errorf("expected only SLESS_ENTRYPOINT, got %d env vars", len(envs))
|
||
}
|
||
if envs[0].Name != "SLESS_ENTRYPOINT" || envs[0].Value != "main.run" {
|
||
t.Errorf("unexpected env: %+v", envs[0])
|
||
}
|
||
}
|