- Add Go modules with required dependencies (Gin, UUID, JWT, etc.) - Implement main web server with landing page endpoint - Add comprehensive API endpoints for health and status - Include proper error handling and request validation - Set up CORS middleware and security headers
290 lines
7.5 KiB
Go
290 lines
7.5 KiB
Go
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
// Integration Test Suite
|
|
type IntegrationTestSuite struct {
|
|
suite.Suite
|
|
baseURL string
|
|
httpClient *http.Client
|
|
authToken string
|
|
testUserID string
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) SetupSuite() {
|
|
// Configuration for integration tests
|
|
suite.baseURL = "http://localhost:8080"
|
|
suite.httpClient = &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
}
|
|
|
|
// Wait for the application to be ready
|
|
suite.waitForApplication()
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TearDownSuite() {
|
|
// Cleanup if needed
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) waitForApplication() {
|
|
maxAttempts := 30
|
|
attempt := 0
|
|
|
|
for attempt < maxAttempts {
|
|
resp, err := suite.httpClient.Get(suite.baseURL + "/health")
|
|
if err == nil && resp.StatusCode == http.StatusOK {
|
|
resp.Body.Close()
|
|
return
|
|
}
|
|
|
|
if resp != nil {
|
|
resp.Body.Close()
|
|
}
|
|
|
|
attempt++
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
|
|
suite.T().Fatal("Application not ready after timeout")
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) makeRequest(method, endpoint string, body interface{}, headers map[string]string) (*http.Response, error) {
|
|
var reqBody *bytes.Buffer
|
|
if body != nil {
|
|
jsonBody, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reqBody = bytes.NewBuffer(jsonBody)
|
|
} else {
|
|
reqBody = bytes.NewBuffer(nil)
|
|
}
|
|
|
|
req, err := http.NewRequest(method, suite.baseURL+endpoint, reqBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
if suite.authToken != "" {
|
|
req.Header.Set("Authorization", "Bearer "+suite.authToken)
|
|
}
|
|
|
|
for key, value := range headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
return suite.httpClient.Do(req)
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestApplicationHealth() {
|
|
resp, err := suite.makeRequest("GET", "/health", nil, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusOK, resp.StatusCode)
|
|
|
|
var health map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&health)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.Equal("healthy", health["status"])
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestUserRegistrationAndLogin() {
|
|
// Test user registration
|
|
userData := map[string]interface{}{
|
|
"email": "testuser@example.com",
|
|
"first_name": "Test",
|
|
"last_name": "User",
|
|
"password": "testpassword123",
|
|
}
|
|
|
|
resp, err := suite.makeRequest("POST", "/api/v1/register", userData, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusCreated, resp.StatusCode)
|
|
|
|
var registerResponse map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(®isterResponse)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.Equal("User created successfully", registerResponse["message"])
|
|
|
|
// Test user login
|
|
loginData := map[string]interface{}{
|
|
"email": "testuser@example.com",
|
|
"password": "testpassword123",
|
|
}
|
|
|
|
resp, err = suite.makeRequest("POST", "/api/v1/login", loginData, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusOK, resp.StatusCode)
|
|
|
|
var loginResponse map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&loginResponse)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.NotEmpty(loginResponse["token"])
|
|
|
|
suite.authToken = loginResponse["token"].(string)
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestProtectedEndpoints() {
|
|
// First, login to get auth token
|
|
suite.TestUserRegistrationAndLogin()
|
|
|
|
// Test getting user profile
|
|
resp, err := suite.makeRequest("GET", "/api/v1/profile", nil, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusOK, resp.StatusCode)
|
|
|
|
var profileResponse map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&profileResponse)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.NotNil(profileResponse["user"])
|
|
|
|
user := profileResponse["user"].(map[string]interface{})
|
|
suite.Equal("testuser@example.com", user["email"])
|
|
suite.Equal("Test", user["first_name"])
|
|
suite.Equal("User", user["last_name"])
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestPricingEndpoint() {
|
|
resp, err := suite.makeRequest("GET", "/api/v1/pricing", nil, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusOK, resp.StatusCode)
|
|
|
|
var pricingResponse map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&pricingResponse)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.NotNil(pricingResponse["plans"])
|
|
|
|
plans := pricingResponse["plans"].([]interface{})
|
|
suite.Len(plans, 1)
|
|
|
|
plan := plans[0].(map[string]interface{})
|
|
suite.Equal("Sovereign Hosting", plan["name"])
|
|
suite.Equal(float64(25000), plan["price"]) // $250.00 in cents
|
|
suite.Equal("usd", plan["currency"])
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestCheckoutSession() {
|
|
// Test creating a checkout session
|
|
checkoutData := map[string]interface{}{
|
|
"domain_name": "testdomain.com",
|
|
"email": "customer@example.com",
|
|
}
|
|
|
|
resp, err := suite.makeRequest("POST", "/api/v1/checkout", checkoutData, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusOK, resp.StatusCode)
|
|
|
|
var checkoutResponse map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&checkoutResponse)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.NotNil(checkoutResponse["checkout_url"])
|
|
suite.Contains(checkoutResponse["checkout_url"].(string), "checkout.stripe.com")
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestErrorHandling() {
|
|
// Test invalid registration data
|
|
invalidUserData := map[string]interface{}{
|
|
"email": "invalid-email",
|
|
"first_name": "",
|
|
"last_name": "",
|
|
"password": "123", // Too short
|
|
}
|
|
|
|
resp, err := suite.makeRequest("POST", "/api/v1/register", invalidUserData, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusBadRequest, resp.StatusCode)
|
|
|
|
var errorResponse map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
|
|
resp.Body.Close()
|
|
|
|
suite.NoError(err)
|
|
suite.NotNil(errorResponse["error"])
|
|
|
|
// Test invalid login credentials
|
|
invalidLoginData := map[string]interface{}{
|
|
"email": "nonexistent@example.com",
|
|
"password": "wrongpassword",
|
|
}
|
|
|
|
resp, err = suite.makeRequest("POST", "/api/v1/login", invalidLoginData, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusUnauthorized, resp.StatusCode)
|
|
|
|
// Test protected endpoint without auth
|
|
resp, err = suite.makeRequest("GET", "/api/v1/profile", nil, nil)
|
|
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusUnauthorized, resp.StatusCode)
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestRateLimiting() {
|
|
// This test would require configuring rate limits for testing
|
|
// For now, we'll just make multiple requests to ensure basic functionality
|
|
|
|
for i := 0; i < 5; i++ {
|
|
resp, err := suite.makeRequest("GET", "/api/v1/pricing", nil, nil)
|
|
suite.NoError(err)
|
|
suite.Equal(http.StatusOK, resp.StatusCode)
|
|
resp.Body.Close()
|
|
}
|
|
}
|
|
|
|
func (suite *IntegrationTestSuite) TestCORSEnabled() {
|
|
// Test CORS headers
|
|
headers := map[string]string{
|
|
"Origin": "http://localhost:3000",
|
|
}
|
|
|
|
resp, err := suite.makeRequest("OPTIONS", "/api/v1/pricing", nil, headers)
|
|
|
|
suite.NoError(err)
|
|
suite.True(resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent)
|
|
|
|
// Check CORS headers
|
|
assert.Equal(suite.T(), "http://localhost:3000", resp.Header.Get("Access-Control-Allow-Origin"))
|
|
assert.Contains(suite.T(), resp.Header.Get("Access-Control-Allow-Methods"), "GET")
|
|
assert.Contains(suite.T(), resp.Header.Get("Access-Control-Allow-Headers"), "Content-Type")
|
|
|
|
resp.Body.Close()
|
|
}
|
|
|
|
// Run the integration test suite
|
|
func TestIntegrationSuite(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration tests in short mode")
|
|
}
|
|
|
|
suite.Run(t, new(IntegrationTestSuite))
|
|
} |