44import com .itau .transferencia .http .requests .CustomerRequest ;
55import com .itau .transferencia .http .requests .TransferRequest ;
66import com .itau .transferencia .repositories .CustomerRepository ;
7+ import com .itau .transferencia .repositories .TransferRepository ;
78import org .junit .jupiter .api .Test ;
89import org .springframework .beans .factory .annotation .Autowired ;
910import org .springframework .boot .test .context .SpringBootTest ;
1516
1617import java .math .BigDecimal ;
1718
19+ import static org .hamcrest .Matchers .greaterThan ;
1820import static org .junit .jupiter .api .Assertions .assertEquals ;
1921import static org .junit .jupiter .api .Assertions .assertTrue ;
2022import static org .springframework .boot .test .context .SpringBootTest .WebEnvironment .RANDOM_PORT ;
@@ -40,11 +42,31 @@ class CustomerControllerTest {
4042 @ Autowired
4143 private CustomerRepository customerRepository ;
4244
45+ @ Autowired
46+ private TransferRepository transferRepository ;
47+
4348 @ Test
4449 void getAll () throws Exception {
4550 mockMvc .perform (get ("/v1/customers" ))
4651 .andExpect (status ().isOk ())
4752 .andExpect (jsonPath ("$.length()" ).value (3 ))
53+ .andExpect (jsonPath ("$[0].name" ).value ("Pedro" ))
54+ .andExpect (jsonPath ("$[0].account" ).value ("00001-1" ))
55+ .andExpect (jsonPath ("$[0].balance" ).value (100.00 ))
56+ .andExpect (jsonPath ("$[0].sentTransfers.length()" ).value (2 ))
57+ .andExpect (jsonPath ("$[0].sentTransfers[0].amount" ).value (250.99 ))
58+ .andExpect (jsonPath ("$[0].receivedTransfers" ).isEmpty ())
59+ .andDo (print ());
60+ }
61+
62+ @ Test
63+ void getAllReturnsEmptyList () throws Exception {
64+ transferRepository .deleteAll ();
65+ customerRepository .deleteAll ();
66+
67+ mockMvc .perform (get ("/v1/customers" ))
68+ .andExpect (status ().isOk ())
69+ .andExpect (jsonPath ("$.length()" ).value (0 ))
4870 .andDo (print ());
4971 }
5072
@@ -58,14 +80,21 @@ void findByAccountNumber() throws Exception {
5880 .andDo (print ());
5981 }
6082
83+ @ Test
84+ void findByAccountNumberNotFound () throws Exception {
85+ mockMvc .perform (get ("/v1/customers/search?account=00000-0" ))
86+ .andExpect (status ().isNotFound ())
87+ .andDo (print ());
88+ }
89+
6190 @ Test
6291 void create () throws Exception {
6392 var account = "00009-9" ;
6493 var request = new CustomerRequest ("test" , account , BigDecimal .ZERO );
6594
6695 mockMvc .perform (post ("/v1/customers" )
6796 .contentType (APPLICATION_JSON )
68- .content (objectMapper . writeValueAsString (request )))
97+ .content (requestBody (request )))
6998 .andExpect (status ().isCreated ())
7099 .andExpect (jsonPath ("$.name" ).value ("test" ))
71100 .andExpect (jsonPath ("$.account" ).value (account ))
@@ -76,12 +105,73 @@ void create() throws Exception {
76105 assertEquals ("test" , savedCustomer .get ().getName ());
77106 }
78107
108+ @ Test
109+ void createFailsWithNegativeBalance () throws Exception {
110+ var request = new CustomerRequest ("Test" , "12345-6" , BigDecimal .valueOf (-10.00 ));
111+
112+ mockMvc .perform (post ("/v1/customers" )
113+ .contentType (APPLICATION_JSON )
114+ .content (requestBody (request )))
115+ .andExpect (status ().isBadRequest ())
116+ .andExpect (jsonPath ("$.errors[0]" ).value ("balance must be positive" ))
117+ .andDo (print ());
118+ }
119+
120+ @ Test
121+ void createFailsWithInvalidAccountFormat () throws Exception {
122+ var request = new CustomerRequest ("Test" , "123-4" , BigDecimal .ZERO );
123+
124+ mockMvc .perform (post ("/v1/customers" )
125+ .contentType (APPLICATION_JSON )
126+ .content (requestBody (request )))
127+ .andExpect (status ().isBadRequest ())
128+ .andExpect (jsonPath ("$.errors[0]" ).value ("account must be 5 digits, a hyphen, and 1 digit (7 total)" ))
129+ .andDo (print ());
130+ }
131+
132+ @ Test
133+ void createFailsWhenAccountAlreadyExists () throws Exception {
134+ var request = new CustomerRequest ("Pedro" , "00001-1" , BigDecimal .ZERO );
135+
136+ mockMvc .perform (post ("/v1/customers" )
137+ .contentType (APPLICATION_JSON )
138+ .content (requestBody (request )))
139+ .andExpect (status ().isConflict ())
140+ .andExpect (jsonPath ("$.errors[0]" ).value ("Account number already exists." ))
141+ .andDo (print ());
142+ }
143+
144+ @ Test
145+ void createFailsWhenNameIsMissing () throws Exception {
146+ var request = new CustomerRequest ("" , "00009-9" , BigDecimal .ZERO );
147+
148+ mockMvc .perform (post ("/v1/customers" )
149+ .contentType (APPLICATION_JSON )
150+ .content (requestBody (request )))
151+ .andExpect (status ().isBadRequest ())
152+ .andExpect (jsonPath ("$.errors[0]" ).value ("name is required" ))
153+ .andDo (print ());
154+ }
155+
79156 @ Test
80157 void getTransfers () throws Exception {
81158 var account = "00001-1" ;
82159 mockMvc .perform (get ("/v1/customers/" + account + "/transfers" ))
83160 .andExpect (status ().isOk ())
84- .andExpect (jsonPath ("$.length()" ).value (2 ))
161+ .andExpect (jsonPath ("$.length()" , greaterThan (0 )))
162+ .andExpect (jsonPath ("$[0].amount" ).isNumber ())
163+ .andExpect (jsonPath ("$[0].createdAt" ).isNotEmpty ())
164+ .andExpect (jsonPath ("$[0].sourceAccount" ).value (account ))
165+ .andExpect (jsonPath ("$[0].destinationAccount" ).isNotEmpty ())
166+ .andExpect (jsonPath ("$[?(@.status == 'Completed')]" ).exists ())
167+ .andDo (print ());
168+ }
169+
170+ @ Test
171+ void getTransfersReturnsEmptyList () throws Exception {
172+ mockMvc .perform (get ("/v1/customers/00000-0/transfers" ))
173+ .andExpect (status ().isOk ())
174+ .andExpect (jsonPath ("$.length()" ).value (0 ))
85175 .andDo (print ());
86176 }
87177
@@ -93,12 +183,63 @@ void transfer() throws Exception {
93183
94184 mockMvc .perform (post ("/v1/customers/" + account + "/transfers" )
95185 .contentType (APPLICATION_JSON )
96- .content (objectMapper . writeValueAsString (transferRequest )))
186+ .content (requestBody (transferRequest )))
97187 .andExpect (status ().isCreated ())
98188 .andExpect (jsonPath ("$.sourceAccount" ).value (account ))
99189 .andExpect (jsonPath ("$.destinationAccount" ).value (destinationAccount ))
100190 .andExpect (jsonPath ("$.amount" ).value (50.00 ))
101191 .andExpect (jsonPath ("$.status" ).value (TransferStatus .COMPLETED .toString ()))
102192 .andDo (print ());
103193 }
194+
195+ @ Test
196+ void transferFailsWhenAccountNotFound () throws Exception {
197+ var account = "00001-1" ;
198+ var nonExistentAccount = "00003-1" ;
199+ var transferRequest = new TransferRequest (nonExistentAccount , BigDecimal .valueOf (50.00 ));
200+
201+ mockMvc .perform (post ("/v1/customers/" + account + "/transfers" )
202+ .contentType (APPLICATION_JSON )
203+ .content (objectMapper .writeValueAsString (transferRequest )))
204+ .andExpect (status ().isNotFound ())
205+ .andExpect (jsonPath ("$.errors[0]" ).value ("Account " + nonExistentAccount + " not found" ))
206+ .andDo (print ());
207+ }
208+
209+ @ Test
210+ void transferFailsWithInsufficientFunds () throws Exception {
211+ var account = "00001-1" ;
212+ var destinationAccount = "00002-2" ;
213+ var transferRequest = new TransferRequest (destinationAccount , BigDecimal .valueOf (10000.00 ));
214+
215+ var initialBalance = customerRepository .findByAccount (account ).get ().getBalance ();
216+ assertEquals (new BigDecimal ("100.00" ), initialBalance );
217+
218+ mockMvc .perform (post ("/v1/customers/" + account + "/transfers" )
219+ .contentType (APPLICATION_JSON )
220+ .content (objectMapper .writeValueAsString (transferRequest )))
221+ .andExpect (status ().isBadRequest ())
222+ .andExpect (jsonPath ("$.errors[0]" ).value ("Insufficient funds for this operation." ))
223+ .andDo (print ());
224+
225+ var finalBalance = customerRepository .findByAccount (account ).get ().getBalance ();
226+ assertEquals (new BigDecimal ("100.00" ), finalBalance );
227+ }
228+
229+ @ Test
230+ void transferFailsWhenSourceIsSameAsDestination () throws Exception {
231+ var account = "00001-1" ;
232+ var transferRequest = new TransferRequest (account , BigDecimal .valueOf (10.00 ));
233+
234+ mockMvc .perform (post ("/v1/customers/" + account + "/transfers" )
235+ .contentType (APPLICATION_JSON )
236+ .content (objectMapper .writeValueAsString (transferRequest )))
237+ .andExpect (status ().isBadRequest ())
238+ .andExpect (jsonPath ("$.errors[0]" ).value ("Cannot transfer to the same account." ))
239+ .andDo (print ());
240+ }
241+
242+ private String requestBody (Object body ) {
243+ return objectMapper .writeValueAsString (body );
244+ }
104245}
0 commit comments