From 7e0a1f55d027a00ad67e50189eb073b1f733f7be Mon Sep 17 00:00:00 2001 From: c21128522 <HammondD3@cardiff.ac.uk> Date: Sat, 10 Aug 2024 21:07:39 +0100 Subject: [PATCH] update --- .../data/testdb.mv.db | Bin 36864 -> 20480 bytes .../ContributorControllerTest.java | 71 +++++++++++++ .../controllers/FundSeekerControllerTest.java | 97 ++++++++++++++++++ .../ContributorRepositoryTest.java | 45 ++++++++ 4 files changed, 213 insertions(+) create mode 100644 CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/ContributorControllerTest.java create mode 100644 CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/FundSeekerControllerTest.java create mode 100644 CrowdFunding Application/CrowdFunding Application/src/test/java/com/repositories/ContributorRepositoryTest.java diff --git a/CrowdFunding Application/CrowdFunding Application/data/testdb.mv.db b/CrowdFunding Application/CrowdFunding Application/data/testdb.mv.db index bfcef377c5ee90adb56b91909c53321b636772da..c179c79be3f9c109baa0d9eb18c834b127515c8d 100644 GIT binary patch delta 1751 zcmb`IJ8aWH9LCR1n>0l&T|lTvP^9pXFzNa1^Q#g{c?cLlfeKXdlI(jxXqu#Lf(lEr zupj{<X0DJ}7+6XWQb!gRHdYn}b~e^>b{yLkm4XD*-T$H}-+kZjyL4X5o!3&~qJtE# zS|>}6-d`6gkC~%F2o-X_Ra<ggjlC=s_lrg0Rn{c;*{Wkumz$X4@y(SNdtY|)-Kp{E zfB5?Qne5bUSEEXm)f`o6xcAtaqbbYoy2FHyWw+tzma<x}HyvbgU1)>ILlh~^$})2> z!)9=zJY=hDm3mDmrb>;iH^q&bb>dJ=eRt+^F86d|8pucek0twvrn$P#(U^uQ(QU&R z(}<_qn9`iaiRyaX|5cjLS%zkK82WhL7&Dm3Ai{R<!Tf32FP*9SZ_nQHSI!lBnRCx2 zaZnHk3%wubThgff3T#MzrF<~2n^~d0s9U{fWmU?!8u33}Jru0H?YX_uDczKH3jnz* zQyJtzKD4~3ay8nyniubg|NYp0|MbFEdqWyT<3-sSp>-&-PE3`Dtemn@wJjJPUKXBu zVjp^H3_N8d@DxMUPCSL&Vq9s}T5F8XH*dt=5=Bpp_CkkM!{$nzkXFO3kw=cEZpH#h zB+&Agt{-lT=wcPIhA=97AcY4?f_$+^FN#-4;V_bt#J!ueL>UQ{X<SVS^PnoJ6EQ-N zQWQ_YFIr;iTY(_5VKSLG^_dQ_&Hw-%08TE7wFKmeuVNK3zW=(pvPbUDWRE8_Cz5DV zAkBo_cDd$n&R+2wGb8@?Y)&+Q?P!4BZ@?g}qybjwkcDxQmI)E^QVn4J2H?;I;+jDN zMx`%%7D4ash0oGxwltZQdLQ6ic0{_9`PMse>85nFD{p0nQAlG7-^C#hr!WY6-Pu85 zoPX5oVGQ<z4{q5H-bq7AQ&-=eN&Y65OZ}hbzP6Ss3F<qYqSH&>o#-hU`uzOKV_Fp* zyhRa@?Vu@8AIc*S!w%>Sc#I~SsR66~2_Xgrk9h&$$ua;^34o0Q0AvK0#mN9mAV0Yl qxVNK5?fBXJQ#VPuxm%C^M~x1_zwFVV6G@L!e?&SGJt9eur19U6ND7_+ literal 36864 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 E50<NlSO5S3 diff --git a/CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/ContributorControllerTest.java b/CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/ContributorControllerTest.java new file mode 100644 index 0000000..e0bab2f --- /dev/null +++ b/CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/ContributorControllerTest.java @@ -0,0 +1,71 @@ +package com.controllers; + +import com.app.CrowdFundingApplication; +import com.app.controllers.ContributorController; +import com.app.models.Contributor; +import com.app.repositories.ContributorRepository; +import com.app.repositories.FundSeekerRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@ContextConfiguration(classes = CrowdFundingApplication.class) +@WebMvcTest(ContributorController.class) +public class ContributorControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private ContributorRepository contributorRepository; + + @MockBean + private FundSeekerRepository fundSeekerRepository; + + @Test + public void showContributionForm_ShouldReturnContributeView() throws Exception { + mockMvc.perform(get("/contribute/{fundSeekerId}", 1L)) + .andExpect(status().isOk()) + .andExpect(view().name("contribute")) + .andExpect(model().attributeExists("contributor")) + .andExpect(model().attribute("contributor", org.hamcrest.Matchers.hasProperty("fundSeekerId", org.hamcrest.Matchers.equalTo(1L)))); + } + + @Test + public void contribute_WithValidAmount_ShouldRedirectToFundSeekers() throws Exception { + Contributor contributor = new Contributor(); + contributor.setFundSeekerId(1L); + contributor.setAmount(100.0); + + when(contributorRepository.save(any(Contributor.class))).thenReturn(contributor); + + mockMvc.perform(post("/contribute") + .param("fundSeekerId", "1") + .flashAttr("contributor", contributor)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/fundseekers")); + } + + @Test + public void contribute_WithInvalidAmount_ShouldReturnContributeView() throws Exception { + Contributor contributor = new Contributor(); + contributor.setFundSeekerId(1L); + contributor.setAmount(-100.0); + + mockMvc.perform(post("/contribute") + .param("fundSeekerId", "1") + .flashAttr("contributor", contributor)) + .andExpect(status().isOk()) + .andExpect(view().name("contribute")) + .andExpect(model().attributeExists("error")); + } +} diff --git a/CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/FundSeekerControllerTest.java b/CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/FundSeekerControllerTest.java new file mode 100644 index 0000000..cbab092 --- /dev/null +++ b/CrowdFunding Application/CrowdFunding Application/src/test/java/com/controllers/FundSeekerControllerTest.java @@ -0,0 +1,97 @@ +package com.controllers; + +import com.app.CrowdFundingApplication; +import com.app.controllers.FundSeekerController; +import com.app.models.Contributor; +import com.app.models.FundSeeker; +import com.app.repositories.ContributorRepository; +import com.app.repositories.FundSeekerRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Arrays; +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@ContextConfiguration(classes = CrowdFundingApplication.class) +@WebMvcTest(FundSeekerController.class) +public class FundSeekerControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private FundSeekerRepository fundSeekerRepository; + + @MockBean + private ContributorRepository contributorRepository; + + @Test + public void showRegistrationForm_ShouldReturnRegisterView() throws Exception { + mockMvc.perform(get("/register")) + .andExpect(status().isOk()) + .andExpect(view().name("register")) + .andExpect(model().attributeExists("fundSeeker")); + } + + @Test + public void registerFundSeeker_WithExistingFriendlyUrl_ShouldReturnRegisterViewWithError() throws Exception { + when(fundSeekerRepository.findByFriendlyUrl(any(String.class))).thenReturn(new FundSeeker()); + + mockMvc.perform(post("/register") + .param("friendlyUrl", "existing-url")) + .andExpect(status().isOk()) + .andExpect(view().name("register")) + .andExpect(model().attributeExists("error")); + } + + + + @Test + public void showFundSeeker_WithExistingUrl_ShouldReturnFundSeekerView() throws Exception { + FundSeeker fundSeeker = new FundSeeker(); + fundSeeker.setId(1L); + fundSeeker.setFriendlyUrl("existing-url"); + + Contributor contributor = new Contributor(); + contributor.setAmount(100.0); + contributor.setFundSeekerId(1L); + + when(fundSeekerRepository.findByFriendlyUrl("existing-url")).thenReturn(fundSeeker); + when(contributorRepository.findByFundSeekerId(1L)).thenReturn(Collections.singletonList(contributor)); + + mockMvc.perform(get("/fundseeker/existing-url")) + .andExpect(status().isOk()) + .andExpect(view().name("fundseeker")) + .andExpect(model().attributeExists("fundSeeker")) + .andExpect(model().attributeExists("contributors")) + .andExpect(model().attributeExists("totalAmount")); + } + + + + @Test + public void listFundSeekers_ShouldReturnFundSeekersView() throws Exception { + FundSeeker fundSeeker1 = new FundSeeker(); + fundSeeker1.setFriendlyUrl("url1"); + + FundSeeker fundSeeker2 = new FundSeeker(); + fundSeeker2.setFriendlyUrl("url2"); + + when(fundSeekerRepository.findAll()).thenReturn(Arrays.asList(fundSeeker1, fundSeeker2)); + + mockMvc.perform(get("/fundseekers")) + .andExpect(status().isOk()) + .andExpect(view().name("fundseekers")) + .andExpect(model().attributeExists("fundSeekers")); + } +} diff --git a/CrowdFunding Application/CrowdFunding Application/src/test/java/com/repositories/ContributorRepositoryTest.java b/CrowdFunding Application/CrowdFunding Application/src/test/java/com/repositories/ContributorRepositoryTest.java new file mode 100644 index 0000000..411a665 --- /dev/null +++ b/CrowdFunding Application/CrowdFunding Application/src/test/java/com/repositories/ContributorRepositoryTest.java @@ -0,0 +1,45 @@ +package com.repositories; + +import com.app.CrowdFundingApplication; +import com.app.models.Contributor; +import com.app.repositories.ContributorRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +@ContextConfiguration(classes = CrowdFundingApplication.class) +public class ContributorRepositoryTest { + + @Autowired + private ContributorRepository contributorRepository; + + @Test + public void testFindByFundSeekerId() { + // Given + Contributor contributor = new Contributor(); + contributor.setAmount(100.0); + contributor.setContributorName("John Doe"); + contributor.setContributorEmail("johndoe@example.com"); + contributor.setCardNumber(1234567812345678L); + contributor.setExpiryDate("12/25"); + contributor.setCvv(123); + contributor.setFundSeekerId(1L); + contributorRepository.save(contributor); + + // When + List<Contributor> contributors = contributorRepository.findByFundSeekerId(1L); + + // Then + assertThat(contributors).isNotEmpty(); + assertThat(contributors).extracting(Contributor::getContributorName).contains("John Doe"); + } +} -- GitLab