mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 18:31:41 +00:00
@@ -0,0 +1,405 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestHealthEndpoint tests the health check endpoint
|
||||
func TestHealthEndpoint(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleHealth(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if response["status"] != "healthy" {
|
||||
t.Errorf("Expected status healthy, got %v", response["status"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestPaymentInstruments tests getting payment instruments
|
||||
func TestPaymentInstruments(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/payment/instruments", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handlePaymentInstruments(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var instruments []map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&instruments); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if len(instruments) == 0 {
|
||||
t.Error("Expected at least one payment instrument")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCanMakePayment tests payment capability check
|
||||
func TestCanMakePayment(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
"origin": "https://localhost:3000",
|
||||
"methodData": []map[string]interface{}{
|
||||
{
|
||||
"supportedMethods": "https://motor.sonr.io/pay",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(payload)
|
||||
req := httptest.NewRequest("POST", "/payment/canmakepayment", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleCanMakePayment(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if response["canMakePayment"] != true {
|
||||
t.Errorf("Expected canMakePayment to be true")
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcessPayment tests payment processing with security
|
||||
func TestProcessPayment(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"origin": "https://localhost:3000",
|
||||
"topOrigin": "https://localhost:3000",
|
||||
"paymentRequestId": "test-request-123",
|
||||
"methodData": []map[string]interface{}{
|
||||
{
|
||||
"supportedMethods": "https://motor.sonr.io/pay",
|
||||
},
|
||||
},
|
||||
"details": map[string]interface{}{
|
||||
"total": map[string]interface{}{
|
||||
"label": "Test Payment",
|
||||
"amount": map[string]interface{}{
|
||||
"currency": "USD",
|
||||
"value": "100.00",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(payload)
|
||||
req := httptest.NewRequest("POST", "/api/payment/process", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Origin", "https://localhost:3000")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleProcessPayment(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if response["paymentId"] == "" {
|
||||
t.Error("Expected payment ID in response")
|
||||
}
|
||||
|
||||
if response["status"] != "pending" {
|
||||
t.Errorf("Expected status pending, got %v", response["status"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestCardTokenization tests PCI-compliant card tokenization
|
||||
func TestCardTokenization(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
// Test valid card
|
||||
token, err := TokenizeCard("4111111111111111", "123", 12, 2025)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to tokenize valid card: %v", err)
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
t.Error("Expected token to be generated")
|
||||
}
|
||||
|
||||
// Test invalid card number
|
||||
_, err = TokenizeCard("1234567890123456", "123", 12, 2025)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid card number")
|
||||
}
|
||||
|
||||
// Test expired card
|
||||
_, err = TokenizeCard("4111111111111111", "123", 1, 2020)
|
||||
if err == nil {
|
||||
t.Error("Expected error for expired card")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTransactionSigning tests transaction signature verification
|
||||
func TestTransactionSigning(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
txData := map[string]interface{}{
|
||||
"id": "test-tx-123",
|
||||
"amount": "100.00",
|
||||
"currency": "USD",
|
||||
"method": "card",
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
|
||||
// Sign transaction
|
||||
signature, err := SignTransaction(txData)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to sign transaction: %v", err)
|
||||
}
|
||||
|
||||
if signature == "" {
|
||||
t.Error("Expected signature to be generated")
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
valid := VerifyTransactionSignature(txData, signature)
|
||||
if !valid {
|
||||
t.Error("Expected signature to be valid")
|
||||
}
|
||||
|
||||
// Test invalid signature
|
||||
invalid := VerifyTransactionSignature(txData, "invalid-signature")
|
||||
if invalid {
|
||||
t.Error("Expected invalid signature to fail verification")
|
||||
}
|
||||
}
|
||||
|
||||
// TestOIDCDiscovery tests OIDC discovery endpoint
|
||||
func TestOIDCDiscovery(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/.well-known/openid-configuration", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleOIDCDiscovery(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var config map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&config); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
// Check required OIDC fields
|
||||
requiredFields := []string{
|
||||
"issuer",
|
||||
"authorization_endpoint",
|
||||
"token_endpoint",
|
||||
"userinfo_endpoint",
|
||||
"jwks_uri",
|
||||
}
|
||||
|
||||
for _, field := range requiredFields {
|
||||
if _, exists := config[field]; !exists {
|
||||
t.Errorf("Missing required OIDC field: %s", field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestJWKS tests JWKS endpoint
|
||||
func TestJWKS(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/.well-known/jwks.json", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleJWKS(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var jwks map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&jwks); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
keys, ok := jwks["keys"].([]interface{})
|
||||
if !ok || len(keys) == 0 {
|
||||
t.Error("Expected at least one key in JWKS")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRateLimiting tests rate limiting functionality
|
||||
func TestRateLimiting(t *testing.T) {
|
||||
// Initialize with low rate limit for testing
|
||||
securityConfig.RateLimit = 5
|
||||
securityConfig.RateWindow = time.Second
|
||||
rateLimiter = NewRateLimiter(5, time.Second)
|
||||
|
||||
// Make requests up to the limit
|
||||
for i := 0; i < 5; i++ {
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
req.Header.Set("Origin", "test-client")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Request %d: Expected status 200, got %d", i+1, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Next request should be rate limited
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
req.Header.Set("Origin", "test-client")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
if w.Code != http.StatusTooManyRequests {
|
||||
t.Errorf("Expected rate limit (429), got %d", w.Code)
|
||||
}
|
||||
|
||||
// Wait for rate limit window to reset
|
||||
time.Sleep(time.Second + 100*time.Millisecond)
|
||||
|
||||
// Should work again
|
||||
req = httptest.NewRequest("GET", "/health", nil)
|
||||
req.Header.Set("Origin", "test-client")
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("After reset: Expected status 200, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSecurityHeaders tests security headers are properly set
|
||||
func TestSecurityHeaders(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
// Check security headers
|
||||
headers := map[string]string{
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Frame-Options": "DENY",
|
||||
"X-XSS-Protection": "1; mode=block",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
}
|
||||
|
||||
for header, expected := range headers {
|
||||
actual := w.Header().Get(header)
|
||||
if actual != expected {
|
||||
t.Errorf("Header %s: expected %s, got %s", header, expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Check CSP is present
|
||||
csp := w.Header().Get("Content-Security-Policy")
|
||||
if csp == "" {
|
||||
t.Error("Expected Content-Security-Policy header")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPCICompliance tests PCI compliance audit logging
|
||||
func TestPCICompliance(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
// Log some actions
|
||||
pciCompliance.LogAction("TEST_ACTION", "user123", "resource456", "SUCCESS", "127.0.0.1")
|
||||
|
||||
// Get audit log
|
||||
logs := pciCompliance.GetAuditLog(10)
|
||||
|
||||
if len(logs) == 0 {
|
||||
t.Error("Expected audit log entries")
|
||||
}
|
||||
|
||||
// Check last entry
|
||||
lastLog := logs[len(logs)-1]
|
||||
if lastLog.Action != "TEST_ACTION" {
|
||||
t.Errorf("Expected action TEST_ACTION, got %s", lastLog.Action)
|
||||
}
|
||||
|
||||
if lastLog.UserID != "user123" {
|
||||
t.Errorf("Expected user ID user123, got %s", lastLog.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDataEncryption tests sensitive data encryption
|
||||
func TestDataEncryption(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
sensitiveData := "4111-1111-1111-1111"
|
||||
|
||||
// Encrypt data
|
||||
encrypted, err := EncryptSensitiveData(sensitiveData)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encrypt data: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == sensitiveData {
|
||||
t.Error("Encrypted data should not match plaintext")
|
||||
}
|
||||
|
||||
// Decrypt data
|
||||
decrypted, err := DecryptSensitiveData(encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decrypt data: %v", err)
|
||||
}
|
||||
|
||||
if decrypted != sensitiveData {
|
||||
t.Errorf("Decrypted data doesn't match original: got %s, want %s", decrypted, sensitiveData)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCardMasking tests card number masking
|
||||
func TestCardMasking(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"4111111111111111", "**** **** **** 1111"},
|
||||
{"5500000000000004", "**** **** **** 0004"},
|
||||
{"340000000000009", "********** 00009"},
|
||||
{"123", "123"}, // Too short to mask
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
masked := MaskCardNumber(tc.input)
|
||||
if masked != tc.expected {
|
||||
t.Errorf("MaskCardNumber(%s): got %s, want %s", tc.input, masked, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user