diff --git a/backend/src/test/java/se/bilhalsning/controller/VehicleControllerTest.java b/backend/src/test/java/se/bilhalsning/controller/VehicleControllerTest.java new file mode 100644 index 0000000..656b635 --- /dev/null +++ b/backend/src/test/java/se/bilhalsning/controller/VehicleControllerTest.java @@ -0,0 +1,70 @@ +package se.bilhalsning.controller; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import se.bilhalsning.dto.VehicleInfoResponse; +import se.bilhalsning.exception.VehicleNotFoundException; +import se.bilhalsning.service.VehicleLookupService; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class VehicleControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private VehicleLookupService vehicleLookupService; + + @Test + void shouldReturnVehicleInfoForValidPlate() throws Exception { + when(vehicleLookupService.lookup("ABC123")) + .thenReturn(new VehicleInfoResponse("Volvo", "V70", 2009, "Silver", "Bensin")); + + mockMvc.perform(get("/api/vehicles/ABC123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.make").value("Volvo")) + .andExpect(jsonPath("$.model").value("V70")) + .andExpect(jsonPath("$.year").value(2009)) + .andExpect(jsonPath("$.color").value("Silver")) + .andExpect(jsonPath("$.fuel").value("Bensin")); + } + + @Test + void shouldReturn404WhenVehicleNotFound() throws Exception { + when(vehicleLookupService.lookup("ZZZ999")) + .thenThrow(new VehicleNotFoundException("ZZZ999")); + + mockMvc.perform(get("/api/vehicles/ZZZ999")) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("Inget fordon hittades")); + } + + @Test + void shouldBeAccessibleWithoutAuthentication() throws Exception { + when(vehicleLookupService.lookup("ABC123")) + .thenReturn(new VehicleInfoResponse("Volvo", "V70", 2009, "Silver", "Bensin")); + + mockMvc.perform(get("/api/vehicles/ABC123")) + .andExpect(status().isOk()); + } + + @Test + void shouldReturnVehicleInfoWithFuelField() throws Exception { + when(vehicleLookupService.lookup("DEF456")) + .thenReturn(new VehicleInfoResponse("Saab", "9-3", 2005, "Röd", "Diesel")); + + mockMvc.perform(get("/api/vehicles/DEF456")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.fuel").value("Diesel")); + } +} \ No newline at end of file diff --git a/backend/src/test/java/se/bilhalsning/service/VehicleLookupServiceTest.java b/backend/src/test/java/se/bilhalsning/service/VehicleLookupServiceTest.java new file mode 100644 index 0000000..055a859 --- /dev/null +++ b/backend/src/test/java/se/bilhalsning/service/VehicleLookupServiceTest.java @@ -0,0 +1,124 @@ +package se.bilhalsning.service; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.jupiter.api.Test; +import se.bilhalsning.dto.VehicleInfoResponse; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.*; + +class VehicleLookupServiceTest { + + private final VehicleLookupService service = new VehicleLookupService(); + + @Test + void shouldParseAllFieldsFromFixture() throws Exception { + Document doc = loadFixture("biluppgifter-hdo732.html"); + + VehicleLookupService spy = new VehicleLookupService() { + @Override + Document fetchPage(String plate) { + return doc; + } + }; + + VehicleInfoResponse result = spy.lookup("hdo732"); + + assertEquals("Peugeot", result.make()); + assertEquals("107 1.0", result.model()); + assertEquals(2011, result.year()); + assertEquals("Gul", result.color()); + assertEquals("Bensin", result.fuel()); + } + + @Test + void shouldParseSummaryFields() { + Document doc = loadFixture("biluppgifter-hdo732.html"); + var fields = new java.util.LinkedHashMap(); + service.extractSummaryFields(doc, fields); + + assertEquals("Gul", fields.get("Färg")); + assertEquals("Bensin", fields.get("Bränsle")); + assertEquals("2011", fields.get("Modellår")); + assertEquals("Personbil", fields.get("Typ")); + } + + @Test + void shouldParseDataSectionFields() { + Document doc = loadFixture("biluppgifter-hdo732.html"); + var fields = new java.util.LinkedHashMap(); + service.extractDataSectionFields(doc, fields); + + assertEquals("Peugeot", fields.get("Fabrikat")); + assertEquals("107", fields.get("Modell")); + assertEquals("1.0", fields.get("Variant")); + assertEquals("2011 / 2011", fields.get("Fordonsår / Modellår")); + } + + @Test + void shouldReturnEmptyFieldsForEmptyDocument() { + Document doc = Jsoup.parse("

No data

"); + var fields = service.extractFields(doc); + + assertTrue(fields.isEmpty()); + } + + @Test + void shouldBuildModelWithoutVariant() { + Document doc = Jsoup.parse( + "

Fordonsdata

" + + "
    " + + "
  • FabrikatVolvo
  • " + + "
  • ModellV70
  • " + + "
" + ); + + VehicleLookupService spy = new VehicleLookupService() { + @Override + Document fetchPage(String plate) { + return doc; + } + }; + + var result = spy.lookup("test"); + assertEquals("V70", result.model()); + } + + @Test + void shouldFallbackToModellårWhenNoFordonsår() { + Document doc = Jsoup.parse( + "" + + "
2009Modellår
" + + "

Fordonsdata

" + + "
    " + + "
  • FabrikatVolvo
  • " + + "
" + ); + + VehicleLookupService spy = new VehicleLookupService() { + @Override + Document fetchPage(String plate) { + return doc; + } + }; + + var result = spy.lookup("test"); + assertEquals(2009, result.year()); + } + + private Document loadFixture(String name) { + try { + String path = "/fixtures/" + name; + var stream = getClass().getResourceAsStream(path); + assertNotNull(stream, "Fixture not found: " + path); + String html = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + return Jsoup.parse(html); + } catch (IOException e) { + throw new RuntimeException("Failed to load fixture: " + name, e); + } + } +} \ No newline at end of file diff --git a/backend/src/test/resources/fixtures/biluppgifter-hdo732.html b/backend/src/test/resources/fixtures/biluppgifter-hdo732.html new file mode 100644 index 0000000..9aa661d --- /dev/null +++ b/backend/src/test/resources/fixtures/biluppgifter-hdo732.html @@ -0,0 +1,69 @@ + + + + +
+ +
+
+ +
+

Fordonsdata

+
+ +
+
+
+
    +
  • + Fabrikat + Peugeot +
  • +
  • + Modell + 107 +
  • +
  • + Variant + 1.0 +
  • +
+
    +
  • + Fordonsår / Modellår + 2011 / 2011 +
  • +
+
+
+ +