package handler import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/gofrs/uuid/v5" "github.com/golang-jwt/jwt/v5" "github.com/saveinmed/backend-go/internal/domain" "github.com/saveinmed/backend-go/internal/http/middleware" ) func newAuthedRequest(t *testing.T, method, path string, body *bytes.Buffer, secret []byte, role string, companyID *uuid.UUID) *http.Request { t.Helper() userID := uuid.Must(uuid.NewV7()) claims := jwt.MapClaims{ "sub": userID.String(), "role": role, "exp": time.Now().Add(time.Hour).Unix(), } if companyID != nil { claims["company_id"] = companyID.String() } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenStr, err := token.SignedString(secret) if err != nil { t.Fatalf("failed to sign token: %v", err) } req := httptest.NewRequest(method, path, body) req.Header.Set("Authorization", "Bearer "+tokenStr) req.Header.Set("Content-Type", "application/json") return req } func serveWithAuth(secret []byte, h http.Handler, req *http.Request) *httptest.ResponseRecorder { rec := httptest.NewRecorder() middleware.RequireAuth(secret)(h).ServeHTTP(rec, req) return rec } func TestAdminPaymentGatewayConfigHandlers(t *testing.T) { handler, repo := newTestHandlerWithRepo() repo.gatewayConfigs["stripe"] = domain.PaymentGatewayConfig{ Provider: "stripe", Active: true, Credentials: "token", Environment: "sandbox", Commission: 0.05, } req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/payment-gateways/stripe", nil) req.SetPathValue("provider", "stripe") rec := httptest.NewRecorder() handler.GetPaymentGatewayConfig(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } if !strings.Contains(rec.Body.String(), `"provider":"stripe"`) { t.Fatalf("expected response to contain provider stripe, got %s", rec.Body.String()) } updatePayload := bytes.NewBufferString(`{"active":true,"credentials":"updated","environment":"prod","commission":0.1}`) updateReq := httptest.NewRequest(http.MethodPut, "/api/v1/admin/payment-gateways/stripe", updatePayload) updateReq.SetPathValue("provider", "stripe") updateRec := httptest.NewRecorder() handler.UpdatePaymentGatewayConfig(updateRec, updateReq) if updateRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", updateRec.Code) } if repo.gatewayConfigs["stripe"].Credentials != "updated" { t.Fatalf("expected credentials updated, got %s", repo.gatewayConfigs["stripe"].Credentials) } testReq := httptest.NewRequest(http.MethodPost, "/api/v1/admin/payment-gateways/stripe/test", nil) testRec := httptest.NewRecorder() handler.TestPaymentGateway(testRec, testReq) if testRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", testRec.Code) } } func TestFinancialHandlers_WithAuth(t *testing.T) { handler, _ := newTestHandlerWithRepo() secret := []byte("test-secret") companyID := uuid.Must(uuid.NewV7()) docPayload := bytes.NewBufferString(`{"type":"CNPJ","url":"http://example.com/doc.pdf"}`) docReq := newAuthedRequest(t, http.MethodPost, "/api/v1/companies/documents", docPayload, secret, "Admin", &companyID) docRec := serveWithAuth(secret, http.HandlerFunc(handler.UploadDocument), docReq) if docRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", docRec.Code) } listReq := newAuthedRequest(t, http.MethodGet, "/api/v1/companies/documents", &bytes.Buffer{}, secret, "Admin", &companyID) listRec := serveWithAuth(secret, http.HandlerFunc(handler.GetDocuments), listReq) if listRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", listRec.Code) } ledgerReq := newAuthedRequest(t, http.MethodGet, "/api/v1/finance/ledger?page=1&page_size=10", &bytes.Buffer{}, secret, "Admin", &companyID) ledgerRec := serveWithAuth(secret, http.HandlerFunc(handler.GetLedger), ledgerReq) if ledgerRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", ledgerRec.Code) } balanceReq := newAuthedRequest(t, http.MethodGet, "/api/v1/finance/balance", &bytes.Buffer{}, secret, "Admin", &companyID) balanceRec := serveWithAuth(secret, http.HandlerFunc(handler.GetBalance), balanceReq) if balanceRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", balanceRec.Code) } var balancePayload map[string]int64 if err := json.Unmarshal(balanceRec.Body.Bytes(), &balancePayload); err != nil { t.Fatalf("failed to decode balance: %v", err) } if balancePayload["balance_cents"] != 100000 { t.Fatalf("expected balance 100000, got %d", balancePayload["balance_cents"]) } withdrawPayload := bytes.NewBufferString(`{"amount_cents":5000,"bank_info":"bank"}`) withdrawReq := newAuthedRequest(t, http.MethodPost, "/api/v1/finance/withdrawals", withdrawPayload, secret, "Admin", &companyID) withdrawRec := serveWithAuth(secret, http.HandlerFunc(handler.RequestWithdrawal), withdrawReq) if withdrawRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", withdrawRec.Code) } listWithdrawReq := newAuthedRequest(t, http.MethodGet, "/api/v1/finance/withdrawals", &bytes.Buffer{}, secret, "Admin", &companyID) listWithdrawRec := serveWithAuth(secret, http.HandlerFunc(handler.ListWithdrawals), listWithdrawReq) if listWithdrawRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", listWithdrawRec.Code) } } func TestSellerPaymentHandlers(t *testing.T) { handler, repo := newTestHandlerWithRepo() secret := []byte("test-secret") sellerID := uuid.Must(uuid.NewV7()) repo.sellerAccounts[sellerID] = domain.SellerPaymentAccount{ SellerID: sellerID, Gateway: "stripe", Status: "active", AccountType: "standard", } getReq := newAuthedRequest(t, http.MethodGet, "/api/v1/sellers/"+sellerID.String()+"/payment-config", &bytes.Buffer{}, secret, "Admin", nil) getReq.SetPathValue("id", sellerID.String()) getRec := serveWithAuth(secret, http.HandlerFunc(handler.GetSellerPaymentConfig), getReq) if getRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", getRec.Code) } if !strings.Contains(getRec.Body.String(), `"gateway":"stripe"`) { t.Fatalf("expected gateway stripe, got %s", getRec.Body.String()) } onboardPayload := bytes.NewBufferString(`{"gateway":"stripe"}`) onboardReq := newAuthedRequest(t, http.MethodPost, "/api/v1/sellers/"+sellerID.String()+"/onboarding", onboardPayload, secret, "Admin", nil) onboardReq.SetPathValue("id", sellerID.String()) onboardRec := serveWithAuth(secret, http.HandlerFunc(handler.OnboardSeller), onboardReq) if onboardRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", onboardRec.Code) } if !strings.Contains(onboardRec.Body.String(), "onboarding_url") { t.Fatalf("expected onboarding_url, got %s", onboardRec.Body.String()) } } func TestPushNotificationHandlers(t *testing.T) { handler := newTestHandler() secret := []byte("test-secret") registerPayload := bytes.NewBufferString(`{"token":"abc","platform":"web"}`) registerReq := newAuthedRequest(t, http.MethodPost, "/api/v1/push/register", registerPayload, secret, "Admin", nil) registerRec := serveWithAuth(secret, http.HandlerFunc(handler.RegisterPushToken), registerReq) if registerRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", registerRec.Code) } unregisterPayload := bytes.NewBufferString(`{"token":"abc"}`) unregisterReq := newAuthedRequest(t, http.MethodDelete, "/api/v1/push/unregister", unregisterPayload, secret, "Admin", nil) unregisterRec := serveWithAuth(secret, http.HandlerFunc(handler.UnregisterPushToken), unregisterReq) if unregisterRec.Code != http.StatusNoContent { t.Fatalf("expected status 204, got %d", unregisterRec.Code) } testReq := newAuthedRequest(t, http.MethodPost, "/api/v1/push/test", &bytes.Buffer{}, secret, "Admin", nil) testRec := serveWithAuth(secret, http.HandlerFunc(handler.TestPushNotification), testReq) if testRec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", testRec.Code) } }