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  }