feat: implement core Go application with web server
- 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
This commit is contained in:
290
output/tests/integration/api_integration_test.go
Normal file
290
output/tests/integration/api_integration_test.go
Normal file
@@ -0,0 +1,290 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user