1+ package  me .oldboy .integration .controllers .api_wiremock_scenario ;
2+ 3+ import  com .fasterxml .jackson .core .type .TypeReference ;
4+ import  com .fasterxml .jackson .databind .ObjectMapper ;
5+ import  com .fasterxml .jackson .datatype .jsr310 .JavaTimeModule ;
6+ import  com .github .tomakehurst .wiremock .WireMockServer ;
7+ import  com .github .tomakehurst .wiremock .client .WireMock ;
8+ import  lombok .SneakyThrows ;
9+ import  me .oldboy .config .security_details .ClientDetailsService ;
10+ import  me .oldboy .config .test_data_source .TestContainerInit ;
11+ import  me .oldboy .dto .loan_dto .LoanCreateDto ;
12+ import  me .oldboy .dto .loan_dto .LoanReadDto ;
13+ import  me .oldboy .integration .annotation .IT ;
14+ import  me .oldboy .jwt_test_utils .JwtTestUtils ;
15+ import  me .oldboy .services .LoanService ;
16+ import  org .jose4j .jwk .JsonWebKeySet ;
17+ import  org .jose4j .jwk .RsaJsonWebKey ;
18+ import  org .jose4j .jwk .RsaJwkGenerator ;
19+ import  org .jose4j .jws .AlgorithmIdentifiers ;
20+ import  org .jose4j .lang .JoseException ;
21+ import  org .junit .jupiter .api .AfterEach ;
22+ import  org .junit .jupiter .api .BeforeEach ;
23+ import  org .junit .jupiter .api .Test ;
24+ import  org .springframework .beans .factory .annotation .Autowired ;
25+ import  org .springframework .http .MediaType ;
26+ import  org .springframework .test .annotation .DirtiesContext ;
27+ import  org .springframework .test .web .servlet .MockMvc ;
28+ import  org .springframework .test .web .servlet .MvcResult ;
29+ import  org .springframework .test .web .servlet .setup .MockMvcBuilders ;
30+ import  org .springframework .web .context .WebApplicationContext ;
31+ 32+ import  java .time .LocalDate ;
33+ import  java .util .HashMap ;
34+ import  java .util .List ;
35+ import  java .util .Map ;
36+ 37+ import  static  com .github .tomakehurst .wiremock .client .WireMock .*;
38+ import  static  me .oldboy .test_constant .TestConstantFields .EXIST_EMAIL ;
39+ import  static  me .oldboy .test_constant .TestConstantFields .EXIST_EMAIL_WITH_READ_AUTH ;
40+ import  static  org .assertj .core .api .Assertions .assertThat ;
41+ import  static  org .springframework .security .test .web .servlet .setup .SecurityMockMvcConfigurers .springSecurity ;
42+ import  static  org .springframework .test .web .servlet .request .MockMvcRequestBuilders .get ;
43+ import  static  org .springframework .test .web .servlet .request .MockMvcRequestBuilders .post ;
44+ import  static  org .springframework .test .web .servlet .result .MockMvcResultMatchers .content ;
45+ import  static  org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
46+ 47+ @ IT 
48+ /* 
49+ В нашем случае тесты проходя поодиночке или даже в комплексе под одним классом при параллельном 
50+ запуске всех разом приводят к падению части из них. Spring Test кэширует контекст, что может 
51+ привести к проблемам. Добавим аннотацию @DirtiesContext на уровне классов, чтобы принудительно 
52+ пересоздавать контекст - это очень сильно замедлит выполнение тестов, но обеспечит изоляцию и 
53+ прохождения всех тестов "разом", при одновременном запуске. 
54+ */ 
55+ @ DirtiesContext (classMode  = DirtiesContext .ClassMode .AFTER_EACH_TEST_METHOD )
56+ class  WireMockLoansControllerIT  extends  TestContainerInit  {
57+ 58+  @ Autowired 
59+  private  LoanService  loanService ;
60+  @ Autowired 
61+  private  ClientDetailsService  clientDetailsService ;
62+ 63+  @ Autowired 
64+  private  WebApplicationContext  webApplicationContext ;
65+  private  MockMvc  mockMvc ;
66+  private  WireMockServer  wireMockServer ;
67+  private  RsaJsonWebKey  rsaJsonWebKey ;
68+  private  Map <String , Object > realmAccessClaimsAdmin ;
69+  private  Map <String , Object > realmAccessClaimsUser ;
70+ 71+  private  Long  testId , anotherId ;
72+  private  LoanCreateDto  testLoanCreateDtoForOwner , testLoanCreateDtoToAnotherClient ;
73+  private  List <LoanCreateDto > testList ;
74+  private  ObjectMapper  objectMapper ;
75+ 76+  @ BeforeEach 
77+  void  setUp () throws  JoseException  {
78+  testId  = 1L ;
79+  anotherId  = 3L ;
80+ 81+  objectMapper  = new  ObjectMapper ().registerModule (new  JavaTimeModule ());
82+ 83+  mockMvc  = MockMvcBuilders .webAppContextSetup (webApplicationContext )
84+  .apply (springSecurity ())
85+  .build ();
86+ 87+  testLoanCreateDtoForOwner  = LoanCreateDto .builder ()
88+  .clientId (testId )
89+  .startDate (LocalDate .of (2021 , 04 ,05 ))
90+  .loanType ("Plane" )
91+  .totalLoan (350000 )
92+  .amountPaid (65000 )
93+  .outstandingAmount (210000 )
94+  .createDate (LocalDate .of (2021 , 03 ,05 ))
95+  .build ();
96+  testLoanCreateDtoToAnotherClient  = LoanCreateDto .builder ()
97+  .clientId (anotherId )
98+  .startDate (LocalDate .of (2022 , 11 ,12 ))
99+  .loanType ("Castle" )
100+  .totalLoan (1350000 )
101+  .amountPaid (115000 )
102+  .outstandingAmount (600000 )
103+  .createDate (LocalDate .of (2022 , 10 ,03 ))
104+  .build ();
105+ 106+  testList  = List .of (testLoanCreateDtoForOwner , testLoanCreateDtoToAnotherClient );
107+ 108+  /* Инициализация WireMock сервера */ 
109+  wireMockServer  = new  WireMockServer (8089 ); // Если установить 0, то порт будет генерироваться случайный 
110+  wireMockServer .start ();
111+  WireMock .configureFor ("localhost" , wireMockServer .port ());
112+ 113+  /* Генерация RSA ключа для JWT */ 
114+  if  (rsaJsonWebKey  == null ) {
115+  rsaJsonWebKey  = RsaJwkGenerator .generateJwk (2048 );
116+  rsaJsonWebKey .setKeyId ("k1" );
117+  rsaJsonWebKey .setAlgorithm (AlgorithmIdentifiers .RSA_USING_SHA256 );
118+  rsaJsonWebKey .setUse ("sig" );
119+  }
120+ 121+  /* Настройка WireMock заглушки JWKS endpoint-a */ 
122+  stubFor (WireMock .get (urlEqualTo ("/auth/realms/test-realm/protocol/openid-connect/certs" ))
123+  .willReturn (aResponse ()
124+  .withHeader ("Content-Type" , "application/json" )
125+  .withBody (new  JsonWebKeySet (rsaJsonWebKey ).toJson ())));
126+ 127+  String  openidConfig  = "{ "  +
128+  "\" issuer\" : \" http://localhost:"  + wireMockServer .port () + "/auth/realms/test-realm\" , "  +
129+  "\" jwks_uri\" : \" http://localhost:"  + wireMockServer .port () + "/auth/realms/test-realm/protocol/openid-connect/certs\"  }" ;
130+ 131+  /* Настройка WireMock заглушки для .well-known/openid-configuration */ 
132+  stubFor (WireMock .get (urlEqualTo ("/auth/realms/test-realm/.well-known/openid-configuration" ))
133+  .willReturn (aResponse ()
134+  .withHeader ("Content-Type" , "application/json" )
135+  .withBody (openidConfig )));
136+ 137+  realmAccessClaimsAdmin  = new  HashMap <>();
138+  realmAccessClaimsAdmin .put ("roles" , List .of ("ROLE_READ" , "ROLE_ADMIN" ));
139+ 140+  realmAccessClaimsUser  = new  HashMap <>();
141+  realmAccessClaimsUser .put ("roles" , List .of ("ROLE_USER" ));
142+  }
143+ 144+  @ AfterEach 
145+  public  void  tearDown () {
146+  if  (wireMockServer  != null ) {
147+  wireMockServer .resetAll ();
148+  wireMockServer .stop ();
149+  wireMockServer  = null ;
150+  }
151+  }
152+ 153+  @ Test 
154+  @ SneakyThrows 
155+  void  getLoanDetails_ShouldReturnDtoList_ForClientWithLoans_Test () {
156+  String  jwt  = JwtTestUtils .generateJWT (EXIST_EMAIL , rsaJsonWebKey , wireMockServer , realmAccessClaimsAdmin );
157+ 158+  MvcResult  result  = mockMvc .perform (get ("/api/myLoans" )
159+  .header ("Authorization" , "Bearer "  + jwt ))
160+  .andExpect (status ().isOk ())
161+  .andReturn ();
162+ 163+  String  strRes  = result .getResponse ().getContentAsString ();
164+  List <LoanReadDto > loansListFromBase  = new  ObjectMapper ()
165+  .registerModule (new  JavaTimeModule ())
166+  .readValue (strRes , new  TypeReference <List <LoanReadDto >>() {});
167+ 168+  assertThat (loansListFromBase .size ()).isGreaterThan (0 );
169+  }
170+ 171+  @ Test 
172+  @ SneakyThrows 
173+  void  getLoanDetails_ShouldReturnEmptyBody_IfClientHasNoLoans_Test () {
174+  String  jwt  = JwtTestUtils .generateJWT (EXIST_EMAIL_WITH_READ_AUTH , rsaJsonWebKey , wireMockServer , realmAccessClaimsUser );
175+ 176+  mockMvc .perform (get ("/api/myLoans" )
177+  .header ("Authorization" , "Bearer "  + jwt ))
178+  .andExpect (status ().is2xxSuccessful ())
179+  .andExpect (content ().string ("" ));
180+  }
181+ 182+  @ Test 
183+  @ SneakyThrows 
184+  void  getLoanDetails_ShouldReturnNotAuth_4xx_WithoutAuth_Test () {
185+  mockMvc .perform (get ("/api/myLoans" ))
186+  .andExpect (status ().is4xxClientError ())
187+  .andExpect (content ().string ("" ));
188+  }
189+ 190+  @ Test 
191+  @ SneakyThrows 
192+  void  createLoan_ShouldReturnOk_AndRecordIdFromBase_Test () {
193+  String  jwt  = JwtTestUtils .generateJWT (EXIST_EMAIL , rsaJsonWebKey , wireMockServer , realmAccessClaimsAdmin );
194+  /* Подготовим данные для сохранения в БД*/ 
195+  String  strLoanCreateDto  = objectMapper .writeValueAsString (testLoanCreateDtoForOwner );
196+ 197+  /* Делаем запрос на сохранение */ 
198+  MvcResult  result  = mockMvc .perform (post ("/api/createLoan" )
199+  .contentType (MediaType .APPLICATION_JSON )
200+  .content (strLoanCreateDto )
201+  .header ("Authorization" , "Bearer "  + jwt ))
202+  .andExpect (status ().isOk ())
203+  .andReturn ();
204+ 205+  /* "Парсим" ответ */ 
206+  String  strRes  = result .getResponse ().getContentAsString ();
207+  Long  afterCreateLoanId  = objectMapper .readValue (strRes , Long .class );
208+ 209+  /* Сравниваем ожидание с результатом - ID последней записи будет больше последнего известного из БД - 8 */ 
210+  assertThat (afterCreateLoanId ).isGreaterThan (8 );
211+  }
212+ 213+  /* Id аутентифицированного клиента и Id того на кого оформлен кредит не совпадает - сохранить нельзя */ 
214+ 215+  @ Test 
216+  @ SneakyThrows 
217+  void  createLoan_ShouldReturnBadRequest_TryToSaveNotYoursLoan_Test () {
218+  String  jwt  = JwtTestUtils .generateJWT (EXIST_EMAIL , rsaJsonWebKey , wireMockServer , realmAccessClaimsAdmin );
219+  /* Подготовим данные для сохранения в БД*/ 
220+  String  strLoanCreateDto  = objectMapper .writeValueAsString (testLoanCreateDtoToAnotherClient );
221+ 222+  /* Делаем запрос на сохранение */ 
223+  mockMvc .perform (post ("/api/createLoan" )
224+  .header ("Authorization" , "Bearer "  + jwt )
225+  .contentType (MediaType .APPLICATION_JSON )
226+  .content (strLoanCreateDto ))
227+  .andExpect (status ().isBadRequest ())
228+  .andExpect (content ().string ("" ));
229+  }
230+ 231+  @ Test 
232+  @ SneakyThrows 
233+  void  saveAllMyRequestLoan_ShouldReturnOk_AndSaveOnlyAuthOwnerLoans_Test () {
234+  String  jwt  = JwtTestUtils .generateJWT (EXIST_EMAIL , rsaJsonWebKey , wireMockServer , realmAccessClaimsAdmin );
235+  /* Получим количество записей в БД */ 
236+  List <LoanReadDto > loansList  = loanService .findAll ();
237+  Integer  listSizeBefore  = loansList .size ();
238+ 239+  /* Подготовим данные для сохранения в БД */ 
240+  String  strList  = objectMapper .writeValueAsString (testList );
241+ 242+  /* Делаем запрос на сохранение */ 
243+  mockMvc .perform (post ("/api/save-all-loans" )
244+  .contentType (MediaType .APPLICATION_JSON )
245+  .content (strList )
246+  .header ("Authorization" , "Bearer "  + jwt ))
247+  .andExpect (status ().isOk ())
248+  .andExpect (content ().string ("Saved all loans!" ));
249+ 250+  /* Проверяем количество записей в БД после сохранения списка кредитов - больше на одну */ 
251+  Integer  listSizeAfter  = loanService .findAll ().size ();
252+  assertThat (listSizeAfter ).isEqualTo (listSizeBefore  + 1 );
253+  }
254+ 255+  @ Test 
256+  @ SneakyThrows 
257+  void  saveAllMyRequestLoan_ShouldReturnOk_ButOperationFailed_Test () {
258+  String  jwt  = JwtTestUtils .generateJWT ("user3@test.com" , rsaJsonWebKey , wireMockServer , realmAccessClaimsUser );
259+  /* Получим количество записей в БД */ 
260+  List <LoanReadDto > loansList  = loanService .findAll ();
261+  Integer  listSizeBefore  = loansList .size ();
262+ 263+  /* Подготовим данные для сохранения в БД */ 
264+  String  strList  = objectMapper .writeValueAsString (testList );
265+ 266+  /* Делаем запрос на сохранение */ 
267+  mockMvc .perform (post ("/api/save-all-loans" )
268+  .contentType (MediaType .APPLICATION_JSON )
269+  .content (strList )
270+  .header ("Authorization" , "Bearer "  + jwt ))
271+  .andExpect (status ().isOk ())
272+  .andExpect (content ().string ("Operation is failed!" ));
273+ 274+  /* Проверяем количество записей в БД после сохранения списка кредитов - неизменен */ 
275+  Integer  listSizeAfter  = loanService .findAll ().size ();
276+  assertThat (listSizeAfter ).isEqualTo (listSizeBefore );
277+  }
278+ 279+  @ Test 
280+  @ SneakyThrows 
281+  void  getAllLoanByType_ShouldReturnOkForRoleAdmin_AndListOfLoansByType_Test () {
282+  String  jwt  = JwtTestUtils .generateJWT (EXIST_EMAIL , rsaJsonWebKey , wireMockServer , realmAccessClaimsAdmin );
283+  String  loanType  = "Home" ;
284+ 285+  MvcResult  result  = mockMvc .perform (get ("/api/get-all-loans-by-type/"  + loanType )
286+  .header ("Authorization" , "Bearer "  + jwt ))
287+  .andExpect (status ().isOk ())
288+  .andReturn ();
289+ 290+  String  strRes  = result .getResponse ().getContentAsString ();
291+ 292+  List <LoanReadDto > respList  = objectMapper .readValue (strRes , new  TypeReference <List <LoanReadDto >>() {});
293+ 294+  assertThat (respList .size ()).isGreaterThan (1 );
295+  }
296+ 297+  @ Test 
298+  @ SneakyThrows 
299+  void  getAllLoanByType_ShouldReturnForbidden_NotAdminAuth_Test () {
300+  String  jwt  = JwtTestUtils .generateJWT ("user3@test.com" , rsaJsonWebKey , wireMockServer , realmAccessClaimsUser );
301+  String  loanType  = "Home" ;
302+ 303+  mockMvc .perform (get ("/api/get-all-loans-by-type/"  + loanType )
304+  .header ("Authorization" , "Bearer "  + jwt ))
305+  .andExpect (status ().isBadRequest ())
306+  .andExpect (content ().string ("{\" exceptionMsg\" :\" Access Denied\" }" ));
307+  }
308+ }
0 commit comments