package main import ( "testing" "github.com/stretchr/testify/assert" ) func TestDomainValidation(t *testing.T) { tests := []struct { domain string valid bool reason string }{ {"example.com", true, "Valid domain"}, {"sub.example.com", true, "Valid subdomain"}, {"test.co.uk", true, "Valid domain with multiple TLDs"}, {"", false, "Empty domain"}, {"invalid", false, "No TLD"}, {".com", false, "Starts with dot"}, {"com.", false, "Ends with dot"}, {"..double.com", false, "Double dots"}, {"toolong" + string(make([]byte, 300)) + ".com", false, "Too long domain"}, {"valid-domain.com", true, "Valid domain with hyphen"}, {"-invalid.com", false, "Starts with hyphen"}, {"invalid-.com", false, "Ends with hyphen"}, {"valid123.com", true, "Valid domain with numbers"}, } for _, test := range tests { t.Run(test.reason, func(t *testing.T) { // Simple domain validation logic valid := isValidDomain(test.domain) assert.Equal(t, test.valid, valid, "Domain: %s", test.domain) }) } } func TestEmailValidation(t *testing.T) { tests := []struct { email string valid bool reason string }{ {"test@example.com", true, "Valid email"}, {"user.name@domain.co.uk", true, "Valid email with subdomain"}, {"user+tag@example.org", true, "Valid email with plus tag"}, {"", false, "Empty email"}, {"invalid", false, "No @ symbol"}, {"@domain.com", false, "No local part"}, {"user@", false, "No domain part"}, {"user..name@example.com", false, "Double dots in local part"}, {"user@.com", false, "Domain starts with dot"}, {"user@com.", false, "Domain ends with dot"}, {"user@toolong" + string(make([]byte, 300)) + ".com", false, "Too long email"}, } for _, test := range tests { t.Run(test.reason, func(t *testing.T) { // Simple email validation logic valid := isValidEmail(test.email) assert.Equal(t, test.valid, valid, "Email: %s", test.email) }) } } func TestCreditCardValidation(t *testing.T) { tests := []struct { cardNumber string valid bool reason string }{ {"4242424242424242", true, "Valid Visa test card"}, {"5555555555554444", true, "Valid Mastercard test card"}, {"378282246310005", true, "Valid Amex test card"}, {"", false, "Empty card number"}, {"4111", false, "Too short"}, {"42424242424242424242", false, "Too long"}, {"abcdabcdabcdabcd", false, "Non-numeric"}, {"4242-4242-4242-4242", false, "Contains dashes"}, {"4242 4242 4242 4242", false, "Contains spaces"}, } for _, test := range tests { t.Run(test.reason, func(t *testing.T) { // Simple credit card validation logic valid := isValidCreditCard(test.cardNumber) assert.Equal(t, test.valid, valid, "Card: %s", test.cardNumber) }) } } func TestBusinessLogic(t *testing.T) { t.Run("Customer ID generation", func(t *testing.T) { // Test that customer IDs are unique id1 := generateCustomerID() id2 := generateCustomerID() assert.NotEmpty(t, id1) assert.NotEmpty(t, id2) assert.NotEqual(t, id1, id2) }) t.Run("Pricing calculation", func(t *testing.T) { // Test pricing logic assert.Equal(t, 250.0, calculateMonthlyPrice()) assert.Equal(t, 3000.0, calculateYearlyPrice()) }) t.Run("Provisioning steps", func(t *testing.T) { // Test provisioning workflow steps := getProvisioningSteps() expectedSteps := []string{ "Domain Registration", "VPS Provisioning", "Cloudron Installation", "DNS Configuration", "Business Setup", } assert.Equal(t, len(expectedSteps), len(steps)) for i, expected := range expectedSteps { assert.Equal(t, expected, steps[i].Name) } }) } func TestProvisioningProgress(t *testing.T) { t.Run("Initial progress", func(t *testing.T) { progress := calculateProgress(0) // No steps completed assert.Equal(t, 0, progress) }) t.Run("Partial progress", func(t *testing.T) { progress := calculateProgress(2) // 2 of 5 steps completed assert.Equal(t, 40, progress) }) t.Run("Complete progress", func(t *testing.T) { progress := calculateProgress(5) // All 5 steps completed assert.Equal(t, 100, progress) }) t.Run("Invalid progress", func(t *testing.T) { progress := calculateProgress(10) // More than total steps assert.Equal(t, 100, progress) // Should cap at 100 }) } func TestErrorHandling(t *testing.T) { t.Run("Empty request validation", func(t *testing.T) { err := validateLaunchRequest("", "", "") assert.Error(t, err) assert.Contains(t, err.Error(), "required") }) t.Run("Valid request", func(t *testing.T) { err := validateLaunchRequest("example.com", "test@example.com", "4242424242424242") assert.NoError(t, err) }) t.Run("Invalid domain", func(t *testing.T) { err := validateLaunchRequest("invalid", "test@example.com", "4242424242424242") assert.Error(t, err) }) t.Run("Invalid email", func(t *testing.T) { err := validateLaunchRequest("example.com", "invalid", "4242424242424242") assert.Error(t, err) }) t.Run("Invalid card", func(t *testing.T) { err := validateLaunchRequest("example.com", "test@example.com", "invalid") assert.Error(t, err) }) } // Helper functions for testing func isValidDomain(domain string) bool { if domain == "" || len(domain) > 253 { return false } // Simple validation - in production, use more robust validation return len(domain) > 3 && contains(domain, ".") } func isValidEmail(email string) bool { if email == "" || len(email) > 254 { return false } // Simple validation - in production, use more robust validation return contains(email, "@") && contains(email, ".") } func isValidCreditCard(cardNumber string) bool { if cardNumber == "" { return false } // Simple validation - in production, use Luhn algorithm and proper card validation return len(cardNumber) >= 13 && len(cardNumber) <= 19 && isNumeric(cardNumber) } func contains(s, substr string) bool { return len(s) >= len(substr) && s[len(s)-len(substr):] == substr || (len(s) > len(substr) && s[:len(substr)] == substr) || (len(s) > len(substr) && findInString(s, substr)) } func findInString(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false } func isNumeric(s string) bool { for _, c := range s { if c < '0' || c > '9' { return false } } return true } func generateCustomerID() string { // Mock implementation return "cust_" + string(rune(len("test") * 1000)) } func calculateMonthlyPrice() float64 { return 250.0 } func calculateYearlyPrice() float64 { return calculateMonthlyPrice() * 12 } type ProvisioningStep struct { Name string Status string } func getProvisioningSteps() []ProvisioningStep { return []ProvisioningStep{ {Name: "Domain Registration", Status: "pending"}, {Name: "VPS Provisioning", Status: "pending"}, {Name: "Cloudron Installation", Status: "pending"}, {Name: "DNS Configuration", Status: "pending"}, {Name: "Business Setup", Status: "pending"}, } } func calculateProgress(completedSteps int) int { totalSteps := len(getProvisioningSteps()) if completedSteps >= totalSteps { return 100 } return (completedSteps * 100) / totalSteps } func validateLaunchRequest(domain, email, cardNumber string) error { if domain == "" { return &ValidationError{Message: "domain is required"} } if email == "" { return &ValidationError{Message: "email is required"} } if cardNumber == "" { return &ValidationError{Message: "card number is required"} } if !isValidDomain(domain) { return &ValidationError{Message: "invalid domain"} } if !isValidEmail(email) { return &ValidationError{Message: "invalid email"} } if !isValidCreditCard(cardNumber) { return &ValidationError{Message: "invalid credit card"} } return nil } type ValidationError struct { Message string } func (e *ValidationError) Error() string { return e.Message }