From 4927acac63d746e7e2bdd8177fb762f4efa83cdc Mon Sep 17 00:00:00 2001 From: Fredrik Marjoni Date: Tue, 21 Apr 2026 14:53:28 +0200 Subject: [PATCH] test[JUnit]: add Mockito dependency and more JUnit tests ensuring greater test coverage --- pom.xml | 6 ++ .../java/edu/group5/app/model/Repository.java | 8 -- .../organization/OrganizationScraper.java | 86 ++++++++++++------ .../organization/OrganizationScraperTest.java | 91 +++++++++++++++++-- .../app/model/user/UserServiceTest.java | 6 +- 5 files changed, 148 insertions(+), 49 deletions(-) diff --git a/pom.xml b/pom.xml index 5bbba22..8091ffe 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,12 @@ jsoup 1.17.2 + + org.mockito + mockito-inline + 5.2.0 + test + diff --git a/src/main/java/edu/group5/app/model/Repository.java b/src/main/java/edu/group5/app/model/Repository.java index 6dd425e..8b4ec61 100644 --- a/src/main/java/edu/group5/app/model/Repository.java +++ b/src/main/java/edu/group5/app/model/Repository.java @@ -23,12 +23,4 @@ protected Repository(Map content) { ParameterValidator.objectChecker(content, "content"); this.content = content; } - - /** - * Gets the content of the repository. - * @return the content of the repository - */ - public Map getContent() { - return content; - } } diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java index ad93736..beb16aa 100644 --- a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java +++ b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java @@ -40,31 +40,10 @@ public String fetchDescription(String pageUrl) { .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .timeout(5000).get(); - Element section = doc.selectFirst("section.information"); - if (section != null) { - section.select("div.extra-info").remove(); - section.select("a.read-more").remove(); - - // Extract all

tags and

elements as separate paragraphs - String description = section.select("p, div").stream() - .filter(el -> el.tagName().equals("p") || el.select("p").isEmpty()) - .filter(el -> !el.hasClass("extra-info") && !el.hasClass("logo")) - .map(Element::text) - .map(text -> text.replace("Les mer", "").trim()) - .filter(text -> !text.isBlank()) - .collect(Collectors.joining("\n\n")); - - // Fallback: if no paragraphs found, get all text from section - if (description.isBlank()) { - description = section.text().trim(); - } - description = description.replace("Les mer", "").trim(); - - // Only cache and return if we found something meaningful - if (!description.isBlank()) { - descriptionCache.put(pageUrl, description); - return description; - } + String description = parseDescription(doc); + if (!description.isBlank()) { + descriptionCache.put(pageUrl, description); + return description; } } catch (Exception e) { System.out.println("Could not get description for: " + pageUrl); @@ -72,6 +51,42 @@ public String fetchDescription(String pageUrl) { return null; } + /** + * Parses the description from a Document by extracting text content + * from {@code
}. + * + * @param doc the Document to parse + * @return the description text, or empty string if not found + */ + protected String parseDescription(Document doc) { + Element section = doc.selectFirst("section.information"); + if (section != null) { + section.select("div.extra-info").remove(); + section.select("a.read-more").remove(); + + // Extract all

tags and

elements as separate paragraphs + String description = section.select("p, div").stream() + .filter(el -> el.tagName().equals("p") || el.select("p").isEmpty()) + .filter(el -> !el.hasClass("extra-info") && !el.hasClass("logo")) + .map(Element::text) + .map(text -> text.replace("Les mer", "").trim()) + .filter(text -> !text.isBlank()) + .collect(Collectors.joining("\n\n")); + + // Fallback: if no paragraphs found, get all text from section + if (description.isBlank()) { + description = section.text().trim(); + } + description = description.replace("Les mer", "").trim(); + + // Only return if we found something meaningful + if (!description.isBlank()) { + return description; + } + } + return ""; + } + /** * Fetches the logo URL for the given page by scraping the {@code div.logo img} * element. Results are cached so each URL is only fetched once. @@ -88,10 +103,9 @@ public String fetchLogoUrl(String pageUrl) { Document doc = Jsoup.connect(pageUrl) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .timeout(5000).get(); - Element img = doc.selectFirst("div.logo img"); - if (img != null) { - String logoUrl = img.absUrl("src"); + String logoUrl = parseLogoUrl(doc); + if (!logoUrl.isBlank()) { logoCache.put(pageUrl, logoUrl); return logoUrl; } @@ -100,4 +114,20 @@ public String fetchLogoUrl(String pageUrl) { } return null; } + + /** + * Parses the logo URL from a Document by extracting the image src + * from {@code div.logo img}. + * + * @param doc the Document to parse + * @return the absolute logo URL, or empty string if not found + */ + protected String parseLogoUrl(Document doc) { + Element img = doc.selectFirst("div.logo img"); + if (img != null) { + String logoUrl = img.absUrl("src"); + return logoUrl.isBlank() ? "" : logoUrl; + } + return ""; + } } diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationScraperTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationScraperTest.java index 8807e61..e78c492 100644 --- a/src/test/java/edu/group5/app/model/organization/OrganizationScraperTest.java +++ b/src/test/java/edu/group5/app/model/organization/OrganizationScraperTest.java @@ -2,8 +2,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.mockito.MockedStatic; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; class OrganizationScraperTest { @@ -15,23 +22,23 @@ void setUp() { } @Test - void fetchDescription_ReturnsNullWhenUrlIsNull() { + void fetchDescriptionReturnsNullWhenUrlIsNull() { assertNull(scraper.fetchDescription(null)); } @Test - void fetchDescription_ReturnsNullWhenUrlIsBlank() { + void fetchDescriptionReturnsNullWhenUrlIsBlank() { assertNull(scraper.fetchDescription("")); } @Test - void fetchDescription_ReturnsNullWhenUrlIsInvalid() { + void fetchDescriptionReturnsNullWhenUrlIsInvalid() { String result = scraper.fetchDescription("https://invalid-url-that-does-not-exist-xyz123.com"); assertNull(result); } @Test - void fetchDescription_CachesResultOnSecondCall() { + void fetchDescriptionCachesResultOnSecondCall() { // Mock URLs won't work, but cache still works with null returns scraper.fetchDescription("https://example.com"); scraper.fetchDescription("https://example.com"); @@ -40,23 +47,23 @@ void fetchDescription_CachesResultOnSecondCall() { } @Test - void fetchLogoUrl_ReturnsNullWhenUrlIsNull() { + void fetchLogoUrlReturnsNullWhenUrlIsNull() { assertNull(scraper.fetchLogoUrl(null)); } @Test - void fetchLogoUrl_ReturnsNullWhenUrlIsBlank() { + void fetchLogoUrlReturnsNullWhenUrlIsBlank() { assertNull(scraper.fetchLogoUrl("")); } @Test - void fetchLogoUrl_ReturnsNullWhenUrlIsInvalid() { + void fetchLogoUrlReturnsNullWhenUrlIsInvalid() { String result = scraper.fetchLogoUrl("https://invalid-url-that-does-not-exist-xyz123.com"); assertNull(result); } @Test - void fetchLogoUrl_CachesResultOnSecondCall() { + void fetchLogoUrlCachesResultOnSecondCall() { // Mock URLs won't work, but cache still works with null returns scraper.fetchLogoUrl("https://example.com"); scraper.fetchLogoUrl("https://example.com"); @@ -65,7 +72,7 @@ void fetchLogoUrl_CachesResultOnSecondCall() { } @Test - void fetchDescription_ReturnsCachedValue() { + void fetchDescriptionReturnsCachedValue() { OrganizationScraper scraper = new OrganizationScraper(); // First call - caches (but makes real request) @@ -76,4 +83,68 @@ void fetchDescription_ReturnsCachedValue() { assertEquals(result1, result2); // Same object = cached } -} + + @Test + void fetchDescriptionHandlesExceptionGracefully() { + String result = scraper.fetchDescription("https://invalid-domain-xyz.test"); + assertNull(result); + } + + @Test + void fetchLogoUrlHandlesExceptionGracefully() { + String result = scraper.fetchLogoUrl("https://invalid-domain-xyz.test"); + assertNull(result); + } + + @Test + void parseDescriptionExtractsParagraphs() { + String html = "

First paragraph

Second paragraph

"; + Document doc = Jsoup.parse(html); + assertTrue(scraper.parseDescription(doc).contains("First paragraph")); + } + + @Test + void parseDescriptionUsesFallbackWhenNoParagraphs() { + String html = "
Fallback text without paragraph tags
"; + Document doc = Jsoup.parse(html); + assertEquals("Fallback text without paragraph tags", scraper.parseDescription(doc)); + } + + @Test + void parseDescriptionRemovesExtraInfoDivs() { + String html = "

Keep this

Remove this
"; + Document doc = Jsoup.parse(html); + String result = scraper.parseDescription(doc); + assertTrue(result.contains("Keep this")); + assertFalse(result.contains("Remove this")); + } + + @Test + void parseDescriptionFiltersOutLesMer() { + String html = "

Some text Les mer More text

"; + Document doc = Jsoup.parse(html); + assertFalse(scraper.parseDescription(doc).contains("Les mer")); + } + + @Test + void parseDescriptionReturnsEmptyStringWhenNoSection() { + String html = "
No section here
"; + Document doc = Jsoup.parse(html); + assertEquals("", scraper.parseDescription(doc)); + } + + @Test + void parseLogoUrlExtractsImageUrl() { + String html = "
"; + Document doc = Jsoup.parse(html); + doc.setBaseUri("https://example.com"); + assertTrue(scraper.parseLogoUrl(doc).contains("logo.png")); + } + + @Test + void parseLogoUrlReturnsEmptyWhenNoImage() { + String html = "
"; + Document doc = Jsoup.parse(html); + assertEquals("", scraper.parseLogoUrl(doc)); + } +} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java index 104e2f1..459b635 100644 --- a/src/test/java/edu/group5/app/model/user/UserServiceTest.java +++ b/src/test/java/edu/group5/app/model/user/UserServiceTest.java @@ -71,11 +71,11 @@ void registerUserInvalidInputsReturnFalse() { } @Test - void registerUserDuplicateEmailAllowedInCurrentCode() { + void registerUserDuplicateEmailNotAllowedInCurrentCode() { boolean result = service.registerUser("Customer", "John", "Cena", "john.cena@example.com", "$2a$10$hashed"); - assertTrue(result); - assertEquals(3, repo.getUsers().size()); + assertFalse(result); + assertEquals(2, repo.getUsers().size()); } @Test