package serverless import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/DeBrosOfficial/network/pkg/serverless" "go.uber.org/zap" ) // --------------------------------------------------------------------------- // Mock SecretsManager // --------------------------------------------------------------------------- type mockSecretsManager struct { secrets map[string]map[string]string // namespace -> name -> value setErr error getErr error listErr error delErr error } func newMockSecretsManager() *mockSecretsManager { return &mockSecretsManager{ secrets: make(map[string]map[string]string), } } func (m *mockSecretsManager) Set(_ context.Context, namespace, name, value string) error { if m.setErr != nil { return m.setErr } if m.secrets[namespace] == nil { m.secrets[namespace] = make(map[string]string) } m.secrets[namespace][name] = value return nil } func (m *mockSecretsManager) Get(_ context.Context, namespace, name string) (string, error) { if m.getErr != nil { return "", m.getErr } ns, ok := m.secrets[namespace] if !ok { return "", serverless.ErrSecretNotFound } v, ok := ns[name] if !ok { return "", serverless.ErrSecretNotFound } return v, nil } func (m *mockSecretsManager) List(_ context.Context, namespace string) ([]string, error) { if m.listErr != nil { return nil, m.listErr } ns := m.secrets[namespace] names := make([]string, 0, len(ns)) for k := range ns { names = append(names, k) } return names, nil } func (m *mockSecretsManager) Delete(_ context.Context, namespace, name string) error { if m.delErr != nil { return m.delErr } ns, ok := m.secrets[namespace] if !ok { return serverless.ErrSecretNotFound } if _, ok := ns[name]; !ok { return serverless.ErrSecretNotFound } delete(ns, name) return nil } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- func newSecretsTestHandlers(sm serverless.SecretsManager) *ServerlessHandlers { logger := zap.NewNop() wsManager := serverless.NewWSManager(logger) return NewServerlessHandlers( nil, newMockRegistry(), wsManager, nil, nil, sm, logger, ) } func decodeJSON(t *testing.T, rec *httptest.ResponseRecorder) map[string]interface{} { t.Helper() var result map[string]interface{} if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil { t.Fatalf("failed to decode response: %v", err) } return result } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- func TestHandleSetSecret_Success(t *testing.T) { sm := newMockSecretsManager() h := newSecretsTestHandlers(sm) body := `{"name":"API_KEY","value":"secret123"}` req := httptest.NewRequest(http.MethodPut, "/v1/functions/secrets?namespace=myns", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() h.HandleSetSecret(rec, req) if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } resp := decodeJSON(t, rec) if resp["name"] != "API_KEY" { t.Errorf("expected name API_KEY, got %v", resp["name"]) } // Verify stored if sm.secrets["myns"]["API_KEY"] != "secret123" { t.Errorf("secret not stored correctly") } } func TestHandleSetSecret_MissingName(t *testing.T) { h := newSecretsTestHandlers(newMockSecretsManager()) body := `{"value":"secret123"}` req := httptest.NewRequest(http.MethodPut, "/v1/functions/secrets?namespace=myns", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() h.HandleSetSecret(rec, req) if rec.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", rec.Code) } } func TestHandleSetSecret_MissingValue(t *testing.T) { h := newSecretsTestHandlers(newMockSecretsManager()) body := `{"name":"API_KEY"}` req := httptest.NewRequest(http.MethodPut, "/v1/functions/secrets?namespace=myns", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() h.HandleSetSecret(rec, req) if rec.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", rec.Code) } } func TestHandleSetSecret_NilManager(t *testing.T) { h := newSecretsTestHandlers(nil) body := `{"name":"API_KEY","value":"secret123"}` req := httptest.NewRequest(http.MethodPut, "/v1/functions/secrets?namespace=myns", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() h.HandleSetSecret(rec, req) if rec.Code != http.StatusNotImplemented { t.Errorf("expected 501, got %d", rec.Code) } } func TestHandleListSecrets_Empty(t *testing.T) { h := newSecretsTestHandlers(newMockSecretsManager()) req := httptest.NewRequest(http.MethodGet, "/v1/functions/secrets?namespace=myns", nil) rec := httptest.NewRecorder() h.HandleListSecrets(rec, req) if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d", rec.Code) } resp := decodeJSON(t, rec) if resp["count"].(float64) != 0 { t.Errorf("expected count 0, got %v", resp["count"]) } } func TestHandleListSecrets_Populated(t *testing.T) { sm := newMockSecretsManager() sm.secrets["myns"] = map[string]string{ "KEY_A": "val_a", "KEY_B": "val_b", } h := newSecretsTestHandlers(sm) req := httptest.NewRequest(http.MethodGet, "/v1/functions/secrets?namespace=myns", nil) rec := httptest.NewRecorder() h.HandleListSecrets(rec, req) if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d", rec.Code) } resp := decodeJSON(t, rec) if resp["count"].(float64) != 2 { t.Errorf("expected count 2, got %v", resp["count"]) } } func TestHandleListSecrets_NilManager(t *testing.T) { h := newSecretsTestHandlers(nil) req := httptest.NewRequest(http.MethodGet, "/v1/functions/secrets?namespace=myns", nil) rec := httptest.NewRecorder() h.HandleListSecrets(rec, req) if rec.Code != http.StatusNotImplemented { t.Errorf("expected 501, got %d", rec.Code) } } func TestHandleDeleteSecret_Success(t *testing.T) { sm := newMockSecretsManager() sm.secrets["myns"] = map[string]string{"API_KEY": "val"} h := newSecretsTestHandlers(sm) req := httptest.NewRequest(http.MethodDelete, "/v1/functions/secrets/API_KEY?namespace=myns", nil) rec := httptest.NewRecorder() h.HandleDeleteSecret(rec, req, "API_KEY") if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d", rec.Code) } if _, exists := sm.secrets["myns"]["API_KEY"]; exists { t.Error("secret should have been deleted") } } func TestHandleDeleteSecret_NotFound(t *testing.T) { h := newSecretsTestHandlers(newMockSecretsManager()) req := httptest.NewRequest(http.MethodDelete, "/v1/functions/secrets/MISSING?namespace=myns", nil) rec := httptest.NewRecorder() h.HandleDeleteSecret(rec, req, "MISSING") if rec.Code != http.StatusNotFound { t.Errorf("expected 404, got %d", rec.Code) } } func TestHandleDeleteSecret_NilManager(t *testing.T) { h := newSecretsTestHandlers(nil) req := httptest.NewRequest(http.MethodDelete, "/v1/functions/secrets/KEY?namespace=myns", nil) rec := httptest.NewRecorder() h.HandleDeleteSecret(rec, req, "KEY") if rec.Code != http.StatusNotImplemented { t.Errorf("expected 501, got %d", rec.Code) } } // Test routing through handleFunctionByName func TestRouting_SecretsSet(t *testing.T) { sm := newMockSecretsManager() h := newSecretsTestHandlers(sm) mux := http.NewServeMux() h.RegisterRoutes(mux) body := `{"name":"MY_SECRET","value":"myval"}` req := httptest.NewRequest(http.MethodPut, "/v1/functions/secrets?namespace=test", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } } func TestRouting_SecretsList(t *testing.T) { h := newSecretsTestHandlers(newMockSecretsManager()) mux := http.NewServeMux() h.RegisterRoutes(mux) req := httptest.NewRequest(http.MethodGet, "/v1/functions/secrets?namespace=test", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } } func TestRouting_SecretsDelete(t *testing.T) { sm := newMockSecretsManager() sm.secrets["test"] = map[string]string{"KEY": "val"} h := newSecretsTestHandlers(sm) mux := http.NewServeMux() h.RegisterRoutes(mux) req := httptest.NewRequest(http.MethodDelete, "/v1/functions/secrets/KEY?namespace=test", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Errorf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } }