Merge branch '65-write-tests-for-the-api-in-the-javafx-app-flashy' into 'master'

Resolve "Write tests for the API in the javafx app Flashy"

Closes #65

See merge request it1901/groups-2021/gr2141/gr2141!49
This commit is contained in:
Hanne Lindbäck Fyrand
2021-11-27 15:50:07 +01:00
13 changed files with 410 additions and 69 deletions
+9
View File
@@ -22,6 +22,15 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<!-- Wiremock -->
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.27.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
@@ -0,0 +1,105 @@
package it1901.groups2021.gr2141.core.storage;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.WireMockServer;
import it1901.groups2021.gr2141.core.models.CardDeck;
import it1901.groups2021.gr2141.core.models.Flashcard;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.givenThat;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class RemoteCardDeckStorageTest {
private RemoteCardDeckStorage remoteCardDeckStorage;
private String exampleSerializedFlashcard;
private String exampleSerializedFlashCard2;
private String exampleSerializedCardDeck;
private WireMockConfiguration config;
private WireMockServer wireMockServer;
@BeforeEach
public void startWireMockServerAndSetup() {
config = WireMockConfiguration.wireMockConfig().port(8089);
wireMockServer = new WireMockServer(config.portNumber());
wireMockServer.start();
WireMock.configureFor("localhost", config.portNumber());
remoteCardDeckStorage = new RemoteCardDeckStorage(URI.create(String.format("http://%s:%s", "localhost", wireMockServer.port())));
exampleSerializedFlashcard = "[{\"front\": {\"lines\": [\"TestCard1Front\"]},\"back\": {\"lines\": [\"TestCard1Back\"]}}";
exampleSerializedFlashCard2 = ",{\"front\": {\"lines\": [\"TestCard2Front\"]},\"back\": {\"lines\": [\"TestCard2Back\"]}}";
exampleSerializedCardDeck = "{ \"flashcards\": " + exampleSerializedFlashcard + exampleSerializedFlashCard2 + "]}";
}
@Test
public void testWriteDeckWithNullInput() {
assertThrows(IllegalArgumentException.class, () -> remoteCardDeckStorage.setServerAddress(null));
assertThrows(IllegalArgumentException.class, () -> remoteCardDeckStorage.writeDeck(null));
assertThrows(IllegalArgumentException.class, () -> remoteCardDeckStorage.writeDeck(null, 0));
}
@Test
public void testWriteWithWrongInput() {
assertThrows(IOException.class, () -> remoteCardDeckStorage.writeDeck(new CardDeck()));
assertThrows(IOException.class, () -> remoteCardDeckStorage.writeDeck(new CardDeck(), 0));
}
@Test
public void testReadDeckWithIndex() throws IOException {
givenThat(get(urlEqualTo("/flashy/deck/" + 0))
.withHeader("Accept", equalTo("application/json"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(exampleSerializedCardDeck)
)
);
List<Flashcard> flashCards = remoteCardDeckStorage.readDeck(0).getFlashcards();
assertEquals(flashCards.size(), 2);
assertEquals("TestCard1Front", flashCards.get(0).getFront().getText());
assertEquals("TestCard1Back", flashCards.get(0).getBack().getText());
assertEquals("TestCard2Front", flashCards.get(1).getFront().getText());
assertEquals("TestCard2Back", flashCards.get(1).getBack().getText());
}
@Test
public void testReadAllDecks() throws IOException {
givenThat(get(urlEqualTo("/flashy/deck/"))
.withHeader("Accept", equalTo("application/json"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(String.format("[%s,%s]", exampleSerializedCardDeck, exampleSerializedCardDeck))
)
);
List<CardDeck> cardDecksList = remoteCardDeckStorage.readAllDecks();
assertEquals(2, cardDecksList.size());
assertEquals(2, cardDecksList.get(0).getNumberOfCards());
assertEquals(2, cardDecksList.get(1).getNumberOfCards());
assertEquals("TestCard1Front", cardDecksList.get(0).getFlashcards().get(0).getFront().getText());
assertEquals("TestCard1Back", cardDecksList.get(0).getFlashcards().get(0).getBack().getText());
assertEquals("TestCard1Front", cardDecksList.get(1).getFlashcards().get(0).getFront().getText());
assertEquals("TestCard1Back", cardDecksList.get(1).getFlashcards().get(0).getBack().getText());
}
@AfterEach
public void stopWireMockServer() {
wireMockServer.stop();
}
}
@@ -1,27 +0,0 @@
// package it1901.groups2021.gr2141;
// import static org.junit.jupiter.api.Assertions.assertEquals;
// import static org.junit.jupiter.api.Assertions.assertThrows;
// import java.util.List;
// import org.junit.jupiter.api.Order;
// import org.junit.jupiter.api.Test;
// import it1901.groups2021.gr2141.core.domainlogic.FlashcardProvider;
// import it1901.groups2021.gr2141.core.models.CardDeck;
// import it1901.groups2021.gr2141.core.models.Flashcard;
// import it1901.groups2021.gr2141.state.LocalAppState;
// public class TestAppState {
// private static FlashcardProvider exampleFlashcardProvider = new FlashcardProvider(new CardDeck(List.of(Flashcard.of("a", "b"))));
// private static final LocalAppState appState = new LocalAppState();
// @Test
// public void testSetGetFlashcardProvider() {
// assertThrows(IllegalArgumentException.class, () -> appState.setFlashcardProvider(null));
// appState.setFlashcardProvider(exampleFlashcardProvider);
// assertEquals(exampleFlashcardProvider, appState.getFlashcardProvider());
// }
// }
+18
View File
@@ -113,6 +113,24 @@
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-util</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<!-- Material Icons -->
<dependency>
+5
View File
@@ -37,6 +37,11 @@
<artifactId>fxui</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>rest</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<build>
+29 -1
View File
@@ -11,7 +11,7 @@
<name>REST API</name>
<version>${project.parent.version}</version>
<description>
A REST server providing an API to interact with the backend of the application.
A REST server providing an API to interact with the backend of the application.
</description>
<dependencies>
@@ -57,7 +57,30 @@
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-util</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
@@ -76,6 +99,11 @@
<mainClass>it1901.groups2021.gr2141.restserver.Server</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</project>
@@ -1,13 +1,10 @@
package it1901.groups2021.gr2141.restapi;
import it1901.groups2021.gr2141.core.domainlogic.FlashcardProvider;
import it1901.groups2021.gr2141.core.models.CardDeck;
import it1901.groups2021.gr2141.core.storage.CardDeckStorage;
import it1901.groups2021.gr2141.core.storage.LocalCardDeckStorage;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
@@ -25,39 +22,29 @@ import java.util.List;
@Path("/flashy/deck")
@Produces(MediaType.APPLICATION_JSON)
public class FlashyModelService {
private static String path = "/flashy/deck";
private LocalCardDeckStorage localCardDeckStorage = new LocalCardDeckStorage();
public static String path = "/flashy/deck";
@Context
private LocalCardDeckStorage localCardDeckStorage;
@GET
public List<CardDeck> getCurrentCardDeck() throws IOException {
System.out.println("GET [" + path + "]");
try {
var deck = localCardDeckStorage.readAllDecks();
return deck;
} catch (IOException e) {
System.err.println(e);
}
return null;
public List<CardDeck> getAllDecks() throws IOException {
var deck = localCardDeckStorage.readAllDecks();
return deck;
}
@GET
@Path("/{index}")
public CardDeck getCurrentCardDeck(@PathParam("index") String index) throws IOException {
System.out.println("GET [" + path + "/" + index + "]");
try {
var deck = localCardDeckStorage.readDeck(Integer.parseInt(index));
return deck;
} catch (IOException e) {
System.err.println(e);
}
return null;
var deck = localCardDeckStorage.readDeck(Integer.parseInt(index));
return deck;
}
@POST
@Path("/{index}")
@Consumes(MediaType.APPLICATION_JSON)
public boolean saveCurrentCardDeck(CardDeck deck, @PathParam("index") String index) {
System.out.println("POST [" + path + "/" + index + "]");
try {
localCardDeckStorage.writeDeck(deck, Integer.parseInt(index));
return true;
@@ -70,7 +57,6 @@ public class FlashyModelService {
@Path("/new")
@Consumes(MediaType.APPLICATION_JSON)
public boolean saveCurrentCardDeck(CardDeck deck) {
System.out.println("POST [" + path + "/add]");
try {
localCardDeckStorage.writeDeck(deck);
return true;
@@ -2,6 +2,8 @@ package it1901.groups2021.gr2141.restserver;
import org.glassfish.jersey.server.ResourceConfig;
import it1901.groups2021.gr2141.core.storage.LocalCardDeckStorage;
import java.io.IOException;
import java.net.URI;
import java.util.List;
@@ -14,8 +16,10 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
* The main class of the REST server.
*/
public class Server {
private static String address = "localhost";
private static String port = "8080";
public static final LocalCardDeckStorage localCardDeckStorage = new LocalCardDeckStorage();
private static void parseArgs(List<String> args) {
if (args.contains("-a")) {
@@ -37,8 +41,8 @@ public class Server {
return URI.create(String.format("http://%s:%s/", address, port));
}
private static HttpServer startServer() {
final ResourceConfig rc = new ServerConfig();
public static HttpServer startServer() {
final ResourceConfig rc = new ServerConfig(localCardDeckStorage);
return GrizzlyHttpServerFactory.createHttpServer(makeURI(), rc);
}
@@ -1,16 +1,10 @@
package it1901.groups2021.gr2141.restserver;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import it1901.groups2021.gr2141.core.domainlogic.FlashcardProvider;
import it1901.groups2021.gr2141.core.models.CardDeck;
import it1901.groups2021.gr2141.core.models.Flashcard;
import it1901.groups2021.gr2141.core.models.serializers.ModelSerializingModule;
import it1901.groups2021.gr2141.core.storage.LocalCardDeckStorage;
import it1901.groups2021.gr2141.restapi.FlashyModelService;
/**
@@ -20,12 +14,27 @@ import it1901.groups2021.gr2141.restapi.FlashyModelService;
*/
public class ServerConfig extends ResourceConfig {
/**
* @see ServerConfig
*/
public ServerConfig() {
register(JacksonFeature.class);
register(JsonMiddleware.class);
register(FlashyModelService.class);
}
private LocalCardDeckStorage localCardDeckStorage;
public ServerConfig(LocalCardDeckStorage localCardDeckStorage) {
setLocalCardDeckStorage(localCardDeckStorage);
register(JacksonFeature.class);
register(JsonMiddleware.class);
register(FlashyModelService.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bind(ServerConfig.this.localCardDeckStorage);
}
});
}
protected LocalCardDeckStorage getLocalCardDeckStorage() {
return this.localCardDeckStorage;
}
protected void setLocalCardDeckStorage(LocalCardDeckStorage localCardDeckStorage) {
this.localCardDeckStorage = localCardDeckStorage;
}
}
+9
View File
@@ -3,6 +3,15 @@
*/
module it1901.groups2021.gr2141.rest {
requires it1901.groups2021.gr2141.core;
requires jakarta.ws.rs;
requires jersey.common;
requires jersey.server;
requires jersey.media.json.jackson;
requires org.glassfish.hk2.api;
opens it1901.groups2021.gr2141.restapi to jersey.server;
exports it1901.groups2021.gr2141.restserver;
}
@@ -0,0 +1,151 @@
package it1901.groups2021.gr2141.restserver;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import it1901.groups2021.gr2141.core.models.CardContent;
import it1901.groups2021.gr2141.core.models.CardDeck;
import it1901.groups2021.gr2141.core.models.Flashcard;
import it1901.groups2021.gr2141.core.models.serializers.ModelSerializingModule;
import it1901.groups2021.gr2141.core.storage.LocalCardDeckStorage;
import it1901.groups2021.gr2141.restapi.FlashyModelService;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.glassfish.jersey.test.JerseyTest;
public class ServerTest extends JerseyTest {
private ObjectMapper objectMapper = ModelSerializingModule.buildMapper();
private LocalCardDeckStorage localCardDeckStorage;
private CardDeck exampleCardDeck;
private Flashcard card1;
private Flashcard card2;
@TempDir
private static File tempDir;
@Override
protected ServerConfig configure() {
localCardDeckStorage = new LocalCardDeckStorage();
localCardDeckStorage.setUserDataDirectory(tempDir.toPath());
final ServerConfig sc = new ServerConfig(localCardDeckStorage);
return sc;
}
private static void moveTestDeckFileToTmpDir() throws IOException, URISyntaxException {
var testDeckResource = new File(ServerTest.class.getResource("/test-deck.json").toURI()).toPath();
Files.copy(testDeckResource, tempDir.toPath().resolve("deck-0.json"), StandardCopyOption.REPLACE_EXISTING);
}
@BeforeAll
public static void setupAll() throws IOException, URISyntaxException {
moveTestDeckFileToTmpDir();
}
@BeforeEach
public void setUpExampleDeck() {
card1 = new Flashcard(new CardContent(List.of("1", "2")), new CardContent(List.of("3")));
card2 = new Flashcard(new CardContent(List.of("why")), new CardContent(List.of("por qué")));
List<Flashcard> exampleFlashcardList = List.of(card1, card2);
exampleCardDeck = new CardDeck(exampleFlashcardList);
}
@Test
public void testGetCurrentCardDeck() {
Response response = target(FlashyModelService.path)
.path("0")
.request(MediaType.APPLICATION_JSON)
.get();
assertEquals(200, response.getStatus());
try {
CardDeck cardDeck = objectMapper.readValue(response.readEntity(String.class), CardDeck.class);
assertEquals(2, cardDeck.getFlashcards().size());
List<Flashcard> flashCards = cardDeck.getFlashcards();
assertEquals("front1", flashCards.get(0).getFront().getText());
assertEquals("back1", flashCards.get(0).getBack().getText());
assertEquals("front2", flashCards.get(1).getFront().getText());
assertEquals("back2", flashCards.get(1).getBack().getText());
} catch (JsonProcessingException e) {
System.out.println(e);
}
}
@Test
public void testAddNewCardDeck() throws InterruptedException {
Entity<CardDeck> cardDeckEntity = Entity.entity(exampleCardDeck, MediaType.APPLICATION_JSON);
target(FlashyModelService.path)
.path("new")
.request(MediaType.APPLICATION_JSON)
.put(cardDeckEntity);
Response response = target(FlashyModelService.path)
.request(MediaType.APPLICATION_JSON)
.get();
assertEquals(200, response.getStatus());
try {
List<CardDeck> cardDeckList = objectMapper.readValue(response.readEntity(String.class), new TypeReference<List<CardDeck>>(){});
assertEquals(2, cardDeckList.size());
} catch (JsonProcessingException e) {
System.out.println(e);
}
}
@Test
public void testAddNewFlashCardsToExistingDeck() {
Response response = target(FlashyModelService.path)
.path("0")
.request(MediaType.APPLICATION_JSON)
.get();
assertEquals(200, response.getStatus());
CardDeck cardDeck = new CardDeck();
try {
cardDeck = objectMapper.readValue(response.readEntity(String.class), CardDeck.class);
assertEquals(2, cardDeck.getNumberOfCards());
} catch (JsonProcessingException e) {
System.out.println(e);
}
cardDeck.add(card1);
cardDeck.add(card2);
Entity<CardDeck> cardDeckEntity = Entity.entity(cardDeck, MediaType.APPLICATION_JSON);
target(FlashyModelService.path)
.path("0")
.request(MediaType.APPLICATION_JSON)
.post(cardDeckEntity);
Response response2 = target(FlashyModelService.path)
.path("0")
.request(MediaType.APPLICATION_JSON)
.get();
assertEquals(200, response2.getStatus());
CardDeck updatedCardDeck = new CardDeck();
try {
updatedCardDeck = objectMapper.readValue(response2.readEntity(String.class), CardDeck.class);
assertEquals(4, updatedCardDeck.getNumberOfCards());
} catch (JsonProcessingException e) {
System.out.println(e);
}
}
}
@@ -0,0 +1,16 @@
open module it1901.groups2021.gr2141.rest {
requires it1901.groups2021.gr2141.core;
requires jakarta.ws.rs;
requires jersey.common;
requires jersey.server;
requires jersey.media.json.jackson;
requires org.glassfish.hk2.api;
requires org.junit.jupiter;
requires org.junit.jupiter.api;
requires org.glassfish.jersey.test;
opens it1901.groups2021.gr2141.restapi to jersey.server;
}
@@ -0,0 +1,28 @@
{
"flashcards": [
{
"front": {
"lines": [
"front1"
]
},
"back": {
"lines": [
"back1"
]
}
},
{
"front": {
"lines": [
"front2"
]
},
"back": {
"lines": [
"back2"
]
}
}
]
}