github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/stone/stone_test.go (about) 1 //go:build integration || !unit 2 // +build integration !unit 3 4 package stone 5 6 import ( 7 "fmt" 8 "testing" 9 "time" 10 11 "github.com/PMoneda/flow" 12 "github.com/mundipagg/boleto-api/mock" 13 "github.com/mundipagg/boleto-api/models" 14 "github.com/mundipagg/boleto-api/test" 15 "github.com/mundipagg/boleto-api/tmpl" 16 "github.com/mundipagg/boleto-api/util" 17 "github.com/stretchr/testify/assert" 18 ) 19 20 var boletoTypeParameters = []test.Parameter{ 21 {Input: models.Title{BoletoType: ""}, Expected: "bill_of_exchange"}, 22 {Input: models.Title{BoletoType: "NSA"}, Expected: "bill_of_exchange"}, 23 {Input: models.Title{BoletoType: "BDP"}, Expected: "bill_of_exchange"}, 24 } 25 26 var boletoResponseFailParameters = []test.Parameter{ 27 {Input: newStubBoletoRequestStone().WithAccessKey("").Build(), Expected: models.ErrorResponse{Code: `MP400`, Message: `o campo AccessKey não pode ser vazio`}}, 28 {Input: newStubBoletoRequestStone().WithAmountInCents(200).Build(), Expected: models.ErrorResponse{Code: "MPOurNumberFail", Message: "our number was not returned by the bank"}}, 29 {Input: newStubBoletoRequestStone().WithAmountInCents(401).Build(), Expected: models.ErrorResponse{Code: `srn:error:unauthenticated`, Message: `srn:error:unauthenticated`}}, 30 {Input: newStubBoletoRequestStone().WithAmountInCents(403).Build(), Expected: models.ErrorResponse{Code: `srn:error:unauthorized`, Message: `srn:error:unauthorized`}}, 31 {Input: newStubBoletoRequestStone().WithAmountInCents(409).Build(), Expected: models.ErrorResponse{Code: `srn:error:conflict`, Message: `srn:error:conflict`}}, 32 {Input: newStubBoletoRequestStone().WithAmountInCents(422).Build(), Expected: models.ErrorResponse{Code: `srn:error:product_not_enabled`, Message: `barcode_payment_invoice_bill_of_exchange is not ena bled on this account`}}, 33 {Input: newStubBoletoRequestStone().WithAmountInCents(4001).Build(), Expected: models.ErrorResponse{Code: `srn:error:validation`, Message: `[{error:is invalid,path:[customer,document]}]`}}, 34 {Input: newStubBoletoRequestStone().WithAmountInCents(4002).Build(), Expected: models.ErrorResponse{Code: `srn:error:validation`, Message: `[{error:can't be blank,path:[customer,legal_name]}]`}}, 35 {Input: newStubBoletoRequestStone().WithAmountInCents(4003).Build(), Expected: models.ErrorResponse{Code: `srn:error:validation`, Message: `[{error:not allowed,path:[amount]}]`}}, 36 {Input: newStubBoletoRequestStone().WithAmountInCents(4004).Build(), Expected: models.ErrorResponse{Code: `srn:error:validation`, Message: `[{error:is invalid,path:[receiver,document]}]`}}, 37 {Input: newStubBoletoRequestStone().WithAmountInCents(4005).Build(), Expected: models.ErrorResponse{Code: `srn:error:validation`, Message: `[{error:is invalid,path:[account_id]},{error:not allowed,path:[amount]}]`}}, 38 {Input: newStubBoletoRequestStone().WithAmountInCents(504).Build(), Expected: models.ErrorResponse{Code: `MPTimeout`, Message: `Post http://localhost:9052/stone/registrarBoleto: context deadline exceeded`}}, 39 } 40 41 var buyerNameWithSpecialCharactersParameters = []test.Parameter{ 42 {Input: `Kakarotto da Silva "Feliz"`, Expected: "Kakarotto da Silva Feliz"}, 43 {Input: "Kakarotto da Silva 'Feliz'", Expected: "Kakarotto da Silva Feliz"}, 44 {Input: "Kakarotto da Silva &Feliz&", Expected: "Kakarotto da Silva Feliz"}, 45 {Input: "Kakarotto da Silva +Feliz+", Expected: "Kakarotto da Silva Feliz"}, 46 } 47 48 func Test_GetBoletoType_WhenCalled_ShouldBeMapTypeSuccessful(t *testing.T) { 49 request := new(models.BoletoRequest) 50 for _, fact := range boletoTypeParameters { 51 request.Title = fact.Input.(models.Title) 52 _, result := getBoletoType(request) 53 assert.Equal(t, fact.Expected, result, "Deve mapear o boleto type corretamente") 54 } 55 } 56 func Test_TemplateRequestStone_WhenBuyerIsPerson_ParseSuccessful(t *testing.T) { 57 var result map[string]interface{} 58 f := flow.NewFlow() 59 input := newStubBoletoRequestStone().WithBoletoType("DM").Build() 60 61 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 62 util.FromJSON(body, &result) 63 64 assert.Equal(t, result["account_id"], input.Authentication.AccessKey) 65 assert.Equal(t, uint64(result["amount"].(float64)), input.Title.AmountInCents) 66 assert.Equal(t, result["expiration_date"], input.Title.ExpireDate) 67 assert.Equal(t, result["invoice_type"], input.Title.BoletoTypeCode) 68 assert.Equal(t, result["customer"].(map[string]interface{})["document"], input.Buyer.Document.Number) 69 assert.Equal(t, result["customer"].(map[string]interface{})["legal_name"], input.Buyer.Name) 70 assert.Equal(t, result["customer"].(map[string]interface{})["trade_name"], nil) 71 } 72 func Test_TemplateRequestStone_WhenBuyerIsCompany_ParseSuccessful(t *testing.T) { 73 var result map[string]interface{} 74 f := flow.NewFlow() 75 input := newStubBoletoRequestStone().WithDocument("12123123000112", "CNPJ").WithBoletoType("DM").Build() 76 77 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 78 util.FromJSON(body, &result) 79 80 assert.Equal(t, result["account_id"], input.Authentication.AccessKey) 81 assert.Equal(t, uint64(result["amount"].(float64)), input.Title.AmountInCents) 82 assert.Equal(t, result["expiration_date"], input.Title.ExpireDate) 83 assert.Equal(t, result["invoice_type"], input.Title.BoletoTypeCode) 84 assert.Equal(t, result["customer"].(map[string]interface{})["document"], input.Buyer.Document.Number) 85 assert.Equal(t, result["customer"].(map[string]interface{})["legal_name"], input.Buyer.Name) 86 assert.Equal(t, result["customer"].(map[string]interface{})["trade_name"], input.Buyer.Name) 87 } 88 89 func Test_TemplateRequestStone_WhenTheBuyerNameHasMoreThanTwoSpacesTogether_ShouldReplaceTheSpacesWithOnlyOneSpace(t *testing.T) { 90 var result map[string]interface{} 91 f := flow.NewFlow() 92 buyerName := "Kakarotto da Silva" 93 documentNumber := "12123123000112" 94 documentType := "CNPJ" 95 96 input := newStubBoletoRequestStone(). 97 WithBuyerName(buyerName). 98 WithDocument(documentNumber, documentType). 99 Build() 100 101 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 102 103 err := util.FromJSON(body, &result) 104 105 buyerNameExpected := "Kakarotto da Silva" 106 107 assert.Nil(t, err) 108 assert.Equal(t, buyerNameExpected, result["customer"].(map[string]interface{})["legal_name"]) 109 assert.Equal(t, buyerNameExpected, result["customer"].(map[string]interface{})["trade_name"]) 110 } 111 112 func Test_TemplateRequestStone_WhenTheBuyerNameHasMoreThan50Characters_ShouldTakeOnlyTheFirst50Characters(t *testing.T) { 113 var result map[string]interface{} 114 f := flow.NewFlow() 115 buyerName := "Kakarotto da Silva com mais de 50 caracteres emitindo um boleto maneiro" 116 documentNumber := "12123123000112" 117 documentType := "CNPJ" 118 119 input := newStubBoletoRequestStone(). 120 WithBuyerName(buyerName). 121 WithDocument(documentNumber, documentType). 122 Build() 123 124 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 125 126 err := util.FromJSON(body, &result) 127 128 buyerNameExpected := "Kakarotto da Silva com mais de 50 caracteres emiti" 129 130 assert.Nil(t, err) 131 assert.Equal(t, buyerNameExpected, result["customer"].(map[string]interface{})["legal_name"]) 132 assert.Equal(t, buyerNameExpected, result["customer"].(map[string]interface{})["trade_name"]) 133 } 134 135 func Test_TemplateRequestStone_WhenTheBuyerNameHasSpecialCharacters_ShouldTakeOnlyAlphaNumerics(t *testing.T) { 136 for _, fact := range buyerNameWithSpecialCharactersParameters { 137 var result map[string]interface{} 138 f := flow.NewFlow() 139 buyerName := fact.Input.(string) 140 documentNumber := "12123123000112" 141 documentType := "CNPJ" 142 143 input := newStubBoletoRequestStone(). 144 WithBuyerName(buyerName). 145 WithDocument(documentNumber, documentType). 146 Build() 147 148 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 149 150 err := util.FromJSON(body, &result) 151 152 buyerNameExpected := fact.Expected.(string) 153 154 assert.Nil(t, err) 155 assert.Equal(t, buyerNameExpected, result["customer"].(map[string]interface{})["legal_name"]) 156 assert.Equal(t, buyerNameExpected, result["customer"].(map[string]interface{})["trade_name"]) 157 } 158 } 159 160 func Test_ProcessBoleto_WhenServiceRespondsSuccessfully_ShouldHasSuccessfulBoletoResponse(t *testing.T) { 161 mock.StartMockService("9051") 162 163 input := newStubBoletoRequestStone().WithAmountInCents(201).Build() 164 bank := New() 165 166 output, _ := bank.ProcessBoleto(input) 167 168 test.AssertProcessBoletoWithSuccess(t, output) 169 } 170 171 func Test_ProcessBoleto_WhenServiceRespondsUnsuccessful_ShouldHasErrorResponse(t *testing.T) { 172 bank := New() 173 mock.StartMockService("9052") 174 175 for _, fact := range boletoResponseFailParameters { 176 request := fact.Input.(*models.BoletoRequest) 177 response, _ := bank.ProcessBoleto(request) 178 179 test.AssertProcessBoletoFailed(t, response) 180 assert.Equal(t, fact.Expected.(models.ErrorResponse).Code, response.Errors[0].Code) 181 assert.Equal(t, fact.Expected.(models.ErrorResponse).Message, response.Errors[0].Message) 182 } 183 } 184 185 func Test_TemplateRequestStone_WhenHasMaxDaysToPayPastDue_ShouldConsiderTheMaxDaysToPayPastDueAsTheDaysLimit(t *testing.T) { 186 var result map[string]interface{} 187 f := flow.NewFlow() 188 189 var expectedMaxDaysToPayPastDue = 5 190 191 input := newStubBoletoRequestStone().WithMaxDaysToPayPastDue(uint(expectedMaxDaysToPayPastDue)).Build() 192 193 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 194 195 err := util.FromJSON(body, &result) 196 197 limitDateExpected := input.Title.ExpireDateTime.Add(time.Duration(expectedMaxDaysToPayPastDue) * day).Format("2006-01-02") 198 199 assert.Nil(t, err) 200 assert.Equal(t, limitDateExpected, result["limit_date"]) 201 } 202 203 func Test_TemplateRequestStone_WhenHasntMaxDaysToPayPastDue_ShouldUseTheDefaultValueOfLimitDays(t *testing.T) { 204 var result map[string]interface{} 205 f := flow.NewFlow() 206 207 input := newStubBoletoRequestStone().Build() 208 209 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 210 211 err := util.FromJSON(body, &result) 212 213 limitDateExpected := input.Title.ExpireDateTime.Add(1 * day).Format("2006-01-02") 214 215 assert.Nil(t, err) 216 assert.Equal(t, limitDateExpected, result["limit_date"]) 217 } 218 219 func Test_GetBankNumber(t *testing.T) { 220 bank := New() 221 222 result := bank.GetBankNumber() 223 224 assert.Equal(t, models.Stone, int(result)) 225 } 226 227 func Test_GetBankNameIntegration(t *testing.T) { 228 bank := New() 229 230 result := bank.GetBankNameIntegration() 231 232 assert.Equal(t, "Stone", result) 233 } 234 235 func Test_GetBankLog(t *testing.T) { 236 bank := New() 237 238 result := bank.Log() 239 240 assert.NotNil(t, result) 241 } 242 243 func Test_bankStone_ProcessBoleto(t *testing.T) { 244 mock.StartMockService("9050") 245 246 bankInst := New() 247 248 type args struct { 249 request *models.BoletoRequest 250 } 251 tests := []struct { 252 name string 253 b bankStone 254 args args 255 want models.BoletoResponse 256 wantErr bool 257 }{ 258 { 259 name: "StoneEmptyAccessKeyRequest", 260 b: bankInst, 261 args: args{ 262 request: successRequest, 263 }, 264 want: models.BoletoResponse{ 265 StatusCode: 0, 266 Errors: []models.ErrorResponse{ 267 { 268 Code: "MP400", 269 Message: "o campo AccessKey não pode ser vazio", 270 }, 271 }, 272 ID: "", 273 DigitableLine: "", 274 BarCodeNumber: "", 275 OurNumber: "", 276 Links: []models.Link{}, 277 }, 278 wantErr: true, 279 }, 280 } 281 for _, tt := range tests { 282 t.Run(tt.name, func(t *testing.T) { 283 got, _ := tt.b.ProcessBoleto(tt.args.request) 284 assert.Greater(t, len(got.Errors), 0) 285 err := got.Errors[0] 286 assert.Equal(t, err.Code, "MP400") 287 assert.Equal(t, err.Message, "o campo AccessKey não pode ser vazio") 288 }) 289 } 290 } 291 292 func BenchmarkBankStoneProcessBoleto(b *testing.B) { 293 mock.StartMockService("9049") 294 295 input := newStubBoletoRequestStone().WithAmountInCents(201).Build() 296 bank := New() 297 298 bank.ProcessBoleto(input) 299 } 300 301 func TestTemplateResponse_WhenRequestHasSpecialCharacter_ShouldBeParsedSuccessful(t *testing.T) { 302 mock.StartMockService("9053") 303 input := newStubBoletoRequestStone().WithAmountInCents(201).WithBuyerName("Nome do \tComprador (Cliente)").Build() 304 bank := New() 305 306 output, _ := bank.ProcessBoleto(input) 307 308 test.AssertProcessBoletoWithSuccess(t, output) 309 } 310 311 func Test_TemplateRequestStone_WhenHasFine_AndWithPercentageOnTotal_ParseSuccessful(t *testing.T) { 312 mock.StartMockService("9094") 313 var result map[string]interface{} 314 f := flow.NewFlow() 315 316 var boletoDate uint = 1 317 var amount uint64 = 0 318 var percentage float64 = 2.0 319 320 input := newStubBoletoRequestStone().WithFine(boletoDate, amount, percentage).Build() 321 322 valueFineExpected := "2.00" 323 daysToAdd := input.Title.Fees.Fine.DaysAfterExpirationDate 324 dateFineExpected := input.Title.ExpireDateTime.UTC().Add(day * time.Duration(daysToAdd)).Format("2006-01-02") 325 326 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 327 _ = util.FromJSON(body, &result) 328 329 assert.Equal(t, result["fine"].(map[string]interface{})["date"], dateFineExpected) 330 assert.Equal(t, result["fine"].(map[string]interface{})["value"], valueFineExpected) 331 } 332 333 func Test_TemplateRequestStone_WhenHasFine_AndWithAmountInCents_ParseSuccessful(t *testing.T) { 334 mock.StartMockService("9095") 335 var result map[string]interface{} 336 f := flow.NewFlow() 337 338 var boletoDate uint = 1 339 var amount uint64 = 25 340 var percentage float64 = 0 341 input := newStubBoletoRequestStone().WithAmountInCents(20000).WithFine(boletoDate, amount, percentage).Build() 342 343 valueFineExpected := "0.12" 344 daysToAdd := input.Title.Fees.Fine.DaysAfterExpirationDate 345 dateFineExpected := input.Title.ExpireDateTime.UTC().Add(day * time.Duration(daysToAdd)).Format("2006-01-02") 346 347 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 348 _ = util.FromJSON(body, &result) 349 350 assert.Equal(t, result["fine"].(map[string]interface{})["date"], dateFineExpected) 351 assert.Equal(t, result["fine"].(map[string]interface{})["value"], valueFineExpected) 352 } 353 354 func Test_TemplateRequestStone_WhenHasInterest_AndWithPercentageOnTotal_ParseSuccessful(t *testing.T) { 355 mock.StartMockService("9096") 356 var result map[string]interface{} 357 f := flow.NewFlow() 358 359 var boletoDate uint = 1 360 var amount uint64 = 0 361 var percentage float64 = 1.0 362 input := newStubBoletoRequestStone().WithInterest(boletoDate, amount, percentage).Build() 363 364 valueFineExpected := "1.00" 365 daysToAdd := input.Title.Fees.Interest.DaysAfterExpirationDate 366 dateFineExpected := input.Title.ExpireDateTime.UTC().Add(day * time.Duration(daysToAdd)).Format("2006-01-02") 367 368 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 369 _ = util.FromJSON(body, &result) 370 371 assert.Equal(t, result["interest"].(map[string]interface{})["date"], dateFineExpected) 372 assert.Equal(t, result["interest"].(map[string]interface{})["value"], valueFineExpected) 373 } 374 375 func Test_TemplateRequestStone_WhenHasInterest_AndWithAmountInCents_ParseSuccessful(t *testing.T) { 376 mock.StartMockService("9097") 377 var result map[string]interface{} 378 f := flow.NewFlow() 379 380 var boletoDate uint = 1 381 var amount uint64 = 5 382 var percentage float64 = 0 383 input := newStubBoletoRequestStone().WithAmountInCents(20000).WithInterest(boletoDate, amount, percentage).Build() 384 385 valueInterestxpected := "0.75" 386 daysToAdd := input.Title.Fees.Interest.DaysAfterExpirationDate 387 dateInterestExpected := input.Title.ExpireDateTime.UTC().Add(day * time.Duration(daysToAdd)).Format("2006-01-02") 388 389 body := fmt.Sprintf("%v", f.From("message://?source=inline", input, templateRequest, tmpl.GetFuncMaps()).GetBody()) 390 _ = util.FromJSON(body, &result) 391 392 assert.Equal(t, result["interest"].(map[string]interface{})["date"], dateInterestExpected) 393 assert.Equal(t, result["interest"].(map[string]interface{})["value"], valueInterestxpected) 394 }