Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/billing"
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/config"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestHandleStripePortalMockLocal(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := &Server{
|
||||
Config: config.Config{WebOrigin: "http://localhost:5174", StripeMock: true},
|
||||
Stripe: &billing.StripeService{
|
||||
Cfg: billing.StripeConfig{ForceMock: true, WebOrigin: "http://localhost:5174"},
|
||||
},
|
||||
}
|
||||
cid := uuid.New()
|
||||
uid := uuid.New()
|
||||
ctx := context.WithValue(context.Background(), ctxUserID, uid)
|
||||
ctx = context.WithValue(ctx, ctxCompanyID, cid)
|
||||
ctx = context.WithValue(ctx, ctxRole, "admin")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/billing/portal", nil)
|
||||
req = req.WithContext(ctx)
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleStripePortal(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var body billing.PortalResult
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !body.Mock || body.URL != "http://localhost:5174/billing?portal=mock" {
|
||||
t.Fatalf("got %#v", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleStripeWebhookRejectsBadSignature(t *testing.T) {
|
||||
t.Parallel()
|
||||
secret := "whsec_handler_test"
|
||||
s := &Server{
|
||||
Stripe: &billing.StripeService{
|
||||
Cfg: billing.StripeConfig{ForceMock: true, WebhookSecret: secret},
|
||||
},
|
||||
}
|
||||
payload := []byte(`{"id":"evt_bad","type":"ping"}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/billing/webhook", bytes.NewReader(payload))
|
||||
req.Header.Set("Stripe-Signature", "t=1,v1=deadbeef")
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleStripeWebhook(rec, req)
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("status=%d body=%s want 400", rec.Code, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleStripeWebhookUnsignedWithoutSecretNeedsForceMock(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Misconfigured live (no webhook secret, no ForceMock) must 503 — never process unsigned.
|
||||
s := &Server{
|
||||
Stripe: &billing.StripeService{Cfg: billing.StripeConfig{}},
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/billing/webhook", bytes.NewReader([]byte(`{"id":"evt_x","type":"ping"}`)))
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleStripeWebhook(rec, req)
|
||||
if rec.Code != http.StatusServiceUnavailable {
|
||||
t.Fatalf("status=%d body=%s want 503", rec.Code, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleStripeCheckoutMockRequiresAdmin(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := &Server{
|
||||
Config: config.Config{StripeMock: true},
|
||||
Stripe: &billing.StripeService{Cfg: billing.StripeConfig{ForceMock: true}},
|
||||
}
|
||||
cid := uuid.New()
|
||||
uid := uuid.New()
|
||||
ctx := context.WithValue(context.Background(), ctxUserID, uid)
|
||||
ctx = context.WithValue(ctx, ctxCompanyID, cid)
|
||||
ctx = context.WithValue(ctx, ctxRole, "member")
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/billing/checkout", bytes.NewBufferString(`{"plan":"starter","term":"monthly"}`))
|
||||
req = req.WithContext(ctx)
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleStripeCheckout(rec, req)
|
||||
if rec.Code != http.StatusForbidden {
|
||||
t.Fatalf("status=%d want 403", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func signHandlerStripePayload(t *testing.T, secret string, payload []byte) string {
|
||||
t.Helper()
|
||||
ts := time.Now().Unix()
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
_, _ = fmt.Fprintf(mac, "%d.", ts)
|
||||
_, _ = mac.Write(payload)
|
||||
return fmt.Sprintf("t=%d,v1=%s", ts, hex.EncodeToString(mac.Sum(nil)))
|
||||
}
|
||||
|
||||
func TestHandleStripeWebhookValidSignatureStillVerifiedUnderMock(t *testing.T) {
|
||||
t.Parallel()
|
||||
secret := "whsec_handler_ok"
|
||||
// No Pool: claim fails closed after signature passes — proves verify runs before apply.
|
||||
s := &Server{
|
||||
Stripe: &billing.StripeService{
|
||||
Cfg: billing.StripeConfig{ForceMock: true, WebhookSecret: secret},
|
||||
},
|
||||
}
|
||||
payload := []byte(`{"id":"evt_ok","type":"ping"}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/billing/webhook", bytes.NewReader(payload))
|
||||
req.Header.Set("Stripe-Signature", signHandlerStripePayload(t, secret, payload))
|
||||
rec := httptest.NewRecorder()
|
||||
s.handleStripeWebhook(rec, req)
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("status=%d body=%s — expect apply/store failure after verify", rec.Code, rec.Body.String())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user