diff --git a/go.mod b/go.mod index 1b5cd11..4a51bc6 100644 --- a/go.mod +++ b/go.mod @@ -2,17 +2,21 @@ module verifycat go 1.22 -require github.com/gin-gonic/gin v1.9.1 +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/stretchr/testify v1.8.4 +) require ( github.com/bytedance/sonic v1.10.2 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.17.0 // indirect + github.com/go-playground/validator/v10 v10.18.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect @@ -21,6 +25,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.7.0 // indirect diff --git a/go.sum b/go.sum index 8390f50..cb005b3 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= diff --git a/validate/api.go b/validate/api.go index 971175d..2491a32 100644 --- a/validate/api.go +++ b/validate/api.go @@ -20,15 +20,6 @@ type CreditCardResult struct { Brand string `json:"brand,omitempty"` } -// Mapeamento de tipos para funções de validação -var validationFuncMap = map[string]func(string) (bool, string){ - "cpf": func(value string) (bool, string) { return IsValidCPF(value), "" }, - "cnpj": func(value string) (bool, string) { return IsValidCNPJ(value), "" }, - "url": func(value string) (bool, string) { return IsValidURL(value), "" }, - "creditcard": ValidateCreditCard, - "email": func(value string) (bool, string) { return IsValidEmail(value), "" }, -} - func ValidateHandler(c *gin.Context) { var req ValidationRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -36,30 +27,40 @@ func ValidateHandler(c *gin.Context) { return } - validationFunc, exists := validationFuncMap[req.Type] - if !exists { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid validation type"}) - return - } - var isValid bool + var message string var brand string - if req.Type == "creditcard" { - isValid, brand = validationFunc(req.Value) - } else { - isValid, _ = validationFunc(req.Value) + switch req.Type { + case "cpf": + isValid = IsValidCPF(req.Value) + message = "CPF" + case "cnpj": + isValid = IsValidCNPJ(req.Value) + message = "CNPJ" + case "url": + isValid = IsValidURL(req.Value) + message = "URL" + case "creditcard": + isValid, brand = ValidateCreditCard(req.Value) + message = "Credit Card" + case "email": + isValid = IsValidEmail(req.Value) + message = "Email" + default: + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid validation type"}) + return } var result interface{} if req.Type == "creditcard" { result = struct { ValidationResult - CreditCard CreditCardResult `json:"creditcard,omitempty"` + CreditCard CreditCardResult `json:"creditCard,omitempty"` }{ ValidationResult: ValidationResult{ IsValid: isValid, - Message: req.Type, + Message: message, }, CreditCard: CreditCardResult{ Brand: brand, @@ -68,7 +69,7 @@ func ValidateHandler(c *gin.Context) { } else { result = ValidationResult{ IsValid: isValid, - Message: req.Type, + Message: message, } } diff --git a/validate/cnpj_test.go b/validate/cnpj_test.go new file mode 100644 index 0000000..5fce56d --- /dev/null +++ b/validate/cnpj_test.go @@ -0,0 +1,43 @@ +package validate + +import ( + "testing" +) + +func TestIsValidCNPJ(t *testing.T) { + // Caso de teste para CNPJ válido + validCNPJ := "11.222.333/0001-81" + if !IsValidCNPJ(validCNPJ) { + t.Errorf("Expected CNPJ %s to be valid, but got invalid", validCNPJ) + } + + // Caso de teste para CNPJ inválido + invalidCNPJ := "11.222.333/0001-82" + if IsValidCNPJ(invalidCNPJ) { + t.Errorf("Expected CNPJ %s to be invalid, but got valid", invalidCNPJ) + } + + // Caso de teste para CNPJ vazio + emptyCNPJ := "" + if IsValidCNPJ(emptyCNPJ) { + t.Errorf("Expected empty CNPJ to be invalid, but got valid") + } + + // Caso de teste para CNPJ com tamanho incorreto + invalidSizeCNPJ := "11.222.333" + if IsValidCNPJ(invalidSizeCNPJ) { + t.Errorf("Expected CNPJ %s with incorrect size to be invalid, but got valid", invalidSizeCNPJ) + } + + // Caso de teste para CNPJ com dígitos repetidos + repeatedDigitsCNPJ := "11.111.111/1111-11" + if IsValidCNPJ(repeatedDigitsCNPJ) { + t.Errorf("Expected CNPJ %s with repeated digits to be invalid, but got valid", repeatedDigitsCNPJ) + } + + // Caso de teste para CNPJ sem caracteres especiais + withoutSpecialCharsCNPJ := "11222333000181" + if !IsValidCNPJ(withoutSpecialCharsCNPJ) { + t.Errorf("Expected CNPJ %s without special characters to be valid, but got invalid", withoutSpecialCharsCNPJ) + } +} diff --git a/validate/cpf.go b/validate/cpf.go index 42d1ac7..638f492 100644 --- a/validate/cpf.go +++ b/validate/cpf.go @@ -33,6 +33,11 @@ func IsValidCPF(cpf string) bool { cpf = strings.ReplaceAll(cpf, ".", "") cpf = strings.ReplaceAll(cpf, "-", "") + // Check if the CPF contains only numeric digits + if _, err := strconv.Atoi(cpf); err != nil { + return false + } + // Check for known invalid CPFs if strings.Count(cpf, string(cpf[0])) == 11 { return false @@ -41,11 +46,16 @@ func IsValidCPF(cpf string) bool { // Extract digits from CPF digits := make([]int, 11) for i := 0; i < 11; i++ { - digit, err := strconv.Atoi(string(cpf[i])) - if err != nil { - // Handle error as needed + if i < len(cpf) { + digit, err := strconv.Atoi(string(cpf[i])) + if err != nil { + // Handle error as needed + } + digits[i] = digit + } else { + // Handle the case where the string is too short + return false } - digits[i] = digit } // Calculate the first verification digit diff --git a/validate/cpf_test.go b/validate/cpf_test.go new file mode 100644 index 0000000..b8906cb --- /dev/null +++ b/validate/cpf_test.go @@ -0,0 +1,58 @@ +package validate + +import ( + "fmt" + "testing" +) + +func TestIsValidCPFAllSameNumbers(t *testing.T) { + // Test with CPFs where all numbers are the same (0 to 9) + for digit := 0; digit <= 9; digit++ { + allSameCPF := fmt.Sprintf("%d%d%d.%d%d%d.%d%d%d-%d%d", digit, digit, digit, digit, digit, digit, digit, digit, digit, digit, digit) + result := IsValidCPF(allSameCPF) + + if result { + t.Errorf("Expected CPF %s to be invalid, but got valid", allSameCPF) + } + } +} + +func TestIsValidCPFValid(t *testing.T) { + // Test a valid CPF + validCPF := "123.456.789-09" + result := IsValidCPF(validCPF) + + if !result { + t.Errorf("Expected CPF %s to be valid, but got invalid", validCPF) + } +} + +func TestIsValidCPFInvalid(t *testing.T) { + // Test an invalid CPF + invalidCPF := "111.222.333-44" + result := IsValidCPF(invalidCPF) + + if result { + t.Errorf("Expected CPF %s to be invalid, but got valid", invalidCPF) + } +} + +func TestIsValidCPFShortInput(t *testing.T) { + // Test with a short input string + shortCPF := "123.456" + result := IsValidCPF(shortCPF) + + if result { + t.Errorf("Expected CPF %s to be invalid due to short input, but got valid", shortCPF) + } +} + +func TestIsValidCPFNonNumericInput(t *testing.T) { + // Test with non-numeric input + nonNumericCPF := "ABC.DEF.GHI-JK" + result := IsValidCPF(nonNumericCPF) + + if result { + t.Errorf("Expected CPF %s to be invalid due to non-numeric characters, but got valid", nonNumericCPF) + } +} diff --git a/validate/creditcard_test.go b/validate/creditcard_test.go new file mode 100644 index 0000000..fecae3f --- /dev/null +++ b/validate/creditcard_test.go @@ -0,0 +1,57 @@ +package validate + +import ( + "testing" +) + +// TestIsValidCreditCard verifica se a função IsValidCreditCard funciona corretamente. +func TestIsValidCreditCard(t *testing.T) { + // Teste para um número de cartão válido + validCard := "1234567812345670" + if !IsValidCreditCard(validCard) { + t.Errorf("Erro para cartão válido. Esperado true, obteve false.") + } + + // Teste para um número de cartão inválido + invalidCard := "1234-5678-1234-5678" + if IsValidCreditCard(invalidCard) { + t.Errorf("Erro para cartão inválido. Esperado false, obteve true.") + } +} + +// TestValidateCreditCard verifica se a função ValidateCreditCard funciona corretamente. +func TestValidateCreditCard(t *testing.T) { + // Teste para um número de cartão válido + validCard := "4539445433274775" + isValid, brand := ValidateCreditCard(validCard) + if !isValid || brand != "Visa" { + t.Errorf("Erro para cartão válido. Esperado (true, 'Visa'), obteve (%t, '%s').", isValid, brand) + } + + // Teste para um número de cartão inválido + invalidCard := "1234-5678-1234-5678" + isValid, brand = ValidateCreditCard(invalidCard) + if isValid || brand != "" { + t.Errorf("Erro para cartão inválido. Esperado (false, ''), obteve (%t, '%s').", isValid, brand) + } +} + +// TestIdentifyCardBrand verifica se a função IdentifyCardBrand funciona corretamente. +func TestIdentifyCardBrand(t *testing.T) { + // Teste para diferentes marcas de cartão + visaCard := "4123456781234567" + if brand := IdentifyCardBrand(visaCard); brand != "Visa" { + t.Errorf("Erro para cartão Visa. Esperado 'Visa', obteve '%s'.", brand) + } + + masterCard := "5212345678901234" + if brand := IdentifyCardBrand(masterCard); brand != "MasterCard" { + t.Errorf("Erro para cartão MasterCard. Esperado 'MasterCard', obteve '%s'.", brand) + } + + amexCard := "371234567890123" + if brand := IdentifyCardBrand(amexCard); brand != "American Express" { + t.Errorf("Erro para cartão American Express. Esperado 'American Express', obteve '%s'.", brand) + } + +} diff --git a/validate/email_test.go b/validate/email_test.go new file mode 100644 index 0000000..452a236 --- /dev/null +++ b/validate/email_test.go @@ -0,0 +1,59 @@ +package validate + +import ( + "testing" +) + +func TestIsValidEmail_ValidEmail(t *testing.T) { + email := "test@example.com" + result := IsValidEmail(email) + + if !result { + t.Errorf("Expected email '%s' to be valid, but it was invalid", email) + } +} + +func TestIsValidEmail_InvalidEmail(t *testing.T) { + email := "invalid-email" + result := IsValidEmail(email) + + if result { + t.Errorf("Expected email '%s' to be invalid, but it was valid", email) + } +} + +func TestIsValidEmail_EmptyEmail(t *testing.T) { + email := "" + result := IsValidEmail(email) + + if result { + t.Errorf("Expected empty email to be invalid, but it was valid") + } +} + +func TestIsValidEmail_InvalidCharacters(t *testing.T) { + email := "test!@example.com" + result := IsValidEmail(email) + + if result { + t.Errorf("Expected email '%s' with invalid characters to be invalid, but it was valid", email) + } +} + +func TestIsValidEmail_InvalidDomain(t *testing.T) { + email := "test@example" + result := IsValidEmail(email) + + if result { + t.Errorf("Expected email '%s' with invalid domain to be invalid, but it was valid", email) + } +} + +func TestIsValidEmail_InvalidTLD(t *testing.T) { + email := "test@example.c" + result := IsValidEmail(email) + + if result { + t.Errorf("Expected email '%s' with invalid top-level domain to be invalid, but it was valid", email) + } +} diff --git a/validate/url_test.go b/validate/url_test.go new file mode 100644 index 0000000..5ae81bb --- /dev/null +++ b/validate/url_test.go @@ -0,0 +1,50 @@ +package validate + +import ( + "testing" +) + +func TestIsValidURL_ValidURL_HTTP(t *testing.T) { + urlStr := "http://www.example.com" + result := IsValidURL(urlStr) + + if !result { + t.Errorf("Expected URL '%s' to be valid, but it was invalid", urlStr) + } +} + +func TestIsValidURL_ValidURL_HTTPS(t *testing.T) { + urlStr := "https://www.example.com" + result := IsValidURL(urlStr) + + if !result { + t.Errorf("Expected URL '%s' to be valid, but it was invalid", urlStr) + } +} + +func TestIsValidURL_InvalidScheme(t *testing.T) { + urlStr := "ftp://www.example.com" + result := IsValidURL(urlStr) + + if result { + t.Errorf("Expected URL '%s' with invalid scheme to be invalid, but it was valid", urlStr) + } +} + +func TestIsValidURL_InvalidHost(t *testing.T) { + urlStr := "http://invalid-host" + result := IsValidURL(urlStr) + + if result { + t.Errorf("Expected URL '%s' with invalid host to be invalid, but it was valid", urlStr) + } +} + +func TestIsValidURL_EmptyURL(t *testing.T) { + urlStr := "" + result := IsValidURL(urlStr) + + if result { + t.Errorf("Expected empty URL to be invalid, but it was valid") + } +}