Compare commits

...

2 commits

Author SHA1 Message Date
Augusto Dwenger J. 7d7cf8148a Reformat header section 2022-09-07 14:57:07 +02:00
Augusto Dwenger J. 306f3c25bc Rework persistence
- Rename exceptions to be more descriptive
- Add tests for the FileSystemDB
- Add Mockito extension JUnit
2022-09-07 14:54:35 +02:00
9 changed files with 284 additions and 45 deletions

10
pom.xml
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@ -49,6 +49,12 @@
<version>4.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.7.0</version>
<scope>test</scope>
</dependency>
<!-- to test my json implementation -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>

View file

@ -13,6 +13,7 @@ import java.util.Optional;
import java.util.UUID;
public final class FileSystemDB implements PersistPlayer, FindPlayer {
private final File saveDirectory;
private final SerializationFactory serializationFactory;
@ -22,29 +23,32 @@ public final class FileSystemDB implements PersistPlayer, FindPlayer {
}
@Override
public void flush(final Player player) throws PersistException {
public void flush(final Player player) throws WritePlayerException {
final var playerFile = new File(saveDirectory, player.uuid().toString());
final String serializedPlayer = serializationFactory.createSerializer(player).serialize();
try (final var writer = new FileWriter(playerFile)) {
final String jsonPlayer = serializationFactory.createSerializer(player).serialize();
writer.write(jsonPlayer);
writer.write(serializedPlayer);
// ensure everything is written
writer.flush();
} catch (IOException e) {
throw new PersistException("Could not persist player data with id: " + player.uuid(), e);
throw new WritePlayerException("Could not persist player data from player: " + player.uuid(), e);
}
}
@Override
public Optional<Player> findById(final UUID uuid) throws FindPlayerException {
public Optional<Player> findById(final UUID uuid) throws ReadPlayerException {
final File[] files = getFiles();
for (final var file : files) {
if (file.getName().equals(uuid.toString())) {
try {
final String fileContent = Files.readString(file.toPath());
final Player player = serializationFactory.createDeserializer(fileContent);
return Optional.of(player);
} catch (IOException e) {
throw new FindPlayerException("Player not found with the uuid: " + uuid, e);
}
if (!file.getName().equals(uuid.toString())) {
continue;
}
try {
final String fileContent = Files.readString(file.toPath());
final Player player = serializationFactory.createDeserializer(fileContent);
return Optional.of(player);
} catch (IOException e) {
throw new ReadPlayerException("Player not found with the uuid: " + uuid, e);
}
}
@ -52,7 +56,7 @@ public final class FileSystemDB implements PersistPlayer, FindPlayer {
}
@Override
public Optional<Player> findByName(final String name) throws FindPlayerException {
public Optional<Player> findByName(final String name) throws ReadPlayerException {
final File[] files = getFiles();
for (final var file : files) {
try {
@ -62,7 +66,7 @@ public final class FileSystemDB implements PersistPlayer, FindPlayer {
return Optional.of(player);
}
} catch (IOException e) {
throw new FindPlayerException("Player not found with the name: " + name, e);
throw new ReadPlayerException("Player not found with the name: " + name, e);
}
}
@ -70,7 +74,7 @@ public final class FileSystemDB implements PersistPlayer, FindPlayer {
}
@Override
public List<Player> findAll() throws FindPlayerException {
public List<Player> findAll() throws ReadPlayerException {
final File[] files = getFiles();
final var playerList = new ArrayList<Player>();
for (final var file : files) {
@ -79,7 +83,7 @@ public final class FileSystemDB implements PersistPlayer, FindPlayer {
final Player player = serializationFactory.createDeserializer(fileContent);
playerList.add(player);
} catch (IOException e) {
throw new FindPlayerException("Player not found with the path: " + file.getAbsolutePath(), e);
throw new ReadPlayerException("Player not found with the path: " + file.getAbsolutePath(), e);
}
}

View file

@ -7,9 +7,9 @@ import java.util.Optional;
import java.util.UUID;
public interface FindPlayer {
Optional<Player> findById(UUID uuid) throws FindPlayerException;
Optional<Player> findById(UUID uuid) throws ReadPlayerException;
Optional<Player> findByName(String name) throws FindPlayerException;
Optional<Player> findByName(String name) throws ReadPlayerException;
List<Player> findAll() throws FindPlayerException;
List<Player> findAll() throws ReadPlayerException;
}

View file

@ -1,11 +0,0 @@
package de.hhhammer.playtime.ng.persistence;
public final class FindPlayerException extends Exception {
public FindPlayerException(String msg) {
super(msg);
}
public FindPlayerException(final String message, final Throwable cause) {
super(message, cause);
}
}

View file

@ -1,11 +0,0 @@
package de.hhhammer.playtime.ng.persistence;
import java.io.IOException;
public final class PersistException extends Exception {
public PersistException(final String message, final IOException cause) {
super(message, cause);
}
}

View file

@ -3,5 +3,5 @@ package de.hhhammer.playtime.ng.persistence;
import de.hhhammer.playtime.ng.player.Player;
public interface PersistPlayer {
void flush(Player player) throws PersistException;
void flush(Player player) throws WritePlayerException;
}

View file

@ -0,0 +1,11 @@
package de.hhhammer.playtime.ng.persistence;
public final class ReadPlayerException extends Exception {
public ReadPlayerException(String msg) {
super(msg);
}
public ReadPlayerException(final String message, final Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,11 @@
package de.hhhammer.playtime.ng.persistence;
import java.io.IOException;
public final class WritePlayerException extends Exception {
public WritePlayerException(final String message, final IOException cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,229 @@
package de.hhhammer.playtime.ng.persistence;
import de.hhhammer.playtime.ng.player.Player;
import de.hhhammer.playtime.ng.serialization.SerializationFactory;
import de.hhhammer.playtime.ng.serialization.serializer.Serializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class FileSystemDBTest {
@Mock
private SerializationFactory mockSerializationFactory;
@Mock
private Player mockPlayer;
@Mock
private Serializer mockSerializer;
private File testDirectory;
private FileSystemDB fileSystemDB;
@BeforeEach
void setup() throws IOException {
final Path path = Files.createTempDirectory("playtime").toAbsolutePath();
this.testDirectory = path.toFile();
this.fileSystemDB = new FileSystemDB(testDirectory, mockSerializationFactory);
}
@AfterEach
void cleanup() {
testDirectory.delete();
}
@Nested
class FlushTest {
@Test
void shouldWriteFile() throws WritePlayerException {
when(mockSerializationFactory.createSerializer(any())).thenReturn(mockSerializer);
when(mockPlayer.uuid()).thenReturn(UUID.randomUUID());
when(mockSerializer.serialize()).thenReturn("");
assertEquals(0, testDirectory.listFiles().length);
fileSystemDB.flush(mockPlayer);
assertEquals(1, testDirectory.listFiles().length);
}
@Test
void shouldNameFileWithPlayerUUID() throws WritePlayerException {
final var uuid = UUID.randomUUID();
when(mockSerializationFactory.createSerializer(any())).thenReturn(mockSerializer);
when(mockPlayer.uuid()).thenReturn(uuid);
when(mockSerializer.serialize()).thenReturn("");
fileSystemDB.flush(mockPlayer);
final File[] foundFiles = testDirectory.listFiles();
assertEquals(1, foundFiles.length);
assertEquals(uuid.toString(), foundFiles[0].getName());
}
@Test
void shouldWriteContentFromSerializer() throws WritePlayerException, IOException {
when(mockSerializationFactory.createSerializer(any())).thenReturn(mockSerializer);
when(mockPlayer.uuid()).thenReturn(UUID.randomUUID());
final String content = "test content from serializer";
when(mockSerializer.serialize()).thenReturn(content);
fileSystemDB.flush(mockPlayer);
final File[] foundFiles = testDirectory.listFiles();
assertEquals(1, foundFiles.length);
final String foundContent = Files.readString(foundFiles[0].toPath());
assertEquals(content, foundContent);
}
}
@Nested
class FindById {
@Test
void shouldThrowIfDirectoryIsAFile() {
final var notADir = new File(testDirectory, "not_a_dir");
final var fileSystemDB = new FileSystemDB(notADir, mockSerializationFactory);
final Exception exception = assertThrows(RuntimeException.class, () -> fileSystemDB.findById(UUID.randomUUID()));
assertEquals("Could not find files in: " + notADir.getAbsolutePath(), exception.getMessage());
}
@Test
void shouldBeEmptyIfNoFileIsPresent() throws IOException, ReadPlayerException {
assertEquals(0, testDirectory.listFiles().length);
final Optional<Player> foundPlayer = fileSystemDB.findById(UUID.randomUUID());
assertTrue(foundPlayer.isEmpty());
}
@Test
void shouldBeEmptyIfUuidNotFound() throws IOException, ReadPlayerException {
new File(testDirectory, "not_a_uuid").createNewFile();
final Optional<Player> foundPlayer = fileSystemDB.findById(UUID.randomUUID());
assertTrue(foundPlayer.isEmpty());
}
@Test
void shouldDeserializeFileContent() throws IOException, ReadPlayerException {
final var playerUUID = UUID.randomUUID();
final var fakePlayerFile = new File(testDirectory, playerUUID.toString());
final String fileContent = "test content";
Files.write(fakePlayerFile.toPath(), fileContent.getBytes());
when(mockSerializationFactory.createDeserializer(any())).thenReturn(mockPlayer);
fileSystemDB.findById(playerUUID);
verify(mockSerializationFactory).createDeserializer(fileContent);
}
}
@Nested
class FindByName {
@Test
void shouldBeEmptyIfNoFilesExist() throws ReadPlayerException {
final Optional<Player> result = fileSystemDB.findByName("username");
assertTrue(result.isEmpty());
}
@Test
void shouldFindThroughDeserializePlayerWithOnlyOneFile() throws IOException, ReadPlayerException {
final var username = "username";
when(mockPlayer.name()).thenReturn(username);
final var fakePlayerFile = new File(testDirectory, "fake_player_file");
Files.write(fakePlayerFile.toPath(), "".getBytes());
when(mockSerializationFactory.createDeserializer(anyString())).thenReturn(mockPlayer);
final Optional<Player> result = fileSystemDB.findByName(username);
assertTrue(result.isPresent());
}
@Test
void shouldNotFindThroughDeserializePlayerWithOnlyOneFile() throws IOException, ReadPlayerException {
final var username = "username";
when(mockPlayer.name()).thenReturn(username);
final var fakePlayerFile = new File(testDirectory, "fake_player_file");
Files.write(fakePlayerFile.toPath(), "".getBytes());
when(mockSerializationFactory.createDeserializer(anyString())).thenReturn(mockPlayer);
final Optional<Player> result = fileSystemDB.findByName("not_a_username");
assertTrue(result.isEmpty());
}
@Test
void shouldFindThroughDeserializePlayerOnMultipleFiles() throws IOException, ReadPlayerException {
final var wantedUsername = "username";
final var notWantedUsername = "not_wanted_username";
final var rightPlayer = mock(Player.class);
final var wrongPlayer = mock(Player.class);
when(rightPlayer.name()).thenReturn(wantedUsername);
when(wrongPlayer.name()).thenReturn(notWantedUsername);
final var rightPlayerFileContent = "right";
final var wrongPlayerFileContent = "wrong";
final var rightPlayerFile = new File(testDirectory, "right_player_file");
final var wrongPlayerFile = new File(testDirectory, "wrong_player_file");
Files.write(rightPlayerFile.toPath(), rightPlayerFileContent.getBytes());
Files.write(wrongPlayerFile.toPath(), wrongPlayerFileContent.getBytes());
when(mockSerializationFactory.createDeserializer(rightPlayerFileContent)).thenReturn(rightPlayer);
when(mockSerializationFactory.createDeserializer(wrongPlayerFileContent)).thenReturn(wrongPlayer);
final Optional<Player> result = fileSystemDB.findByName(wantedUsername);
assertTrue(result.isPresent());
}
}
@Nested
class FindAllTest {
@Test
void shouldBeEmptyIfNoFilesExist() throws ReadPlayerException {
final List<Player> result = fileSystemDB.findAll();
assertEquals(0, result.size());
}
@Test
void shouldFindOneFile() throws IOException, ReadPlayerException {
final var fakePlayerFile = new File(testDirectory, "fake_player_file");
Files.write(fakePlayerFile.toPath(), "test content".getBytes());
when(mockSerializationFactory.createDeserializer(anyString())).thenReturn(mockPlayer);
final List<Player> result = fileSystemDB.findAll();
assertEquals(1, result.size());
}
@Test
void shouldFindTwoFile() throws IOException, ReadPlayerException {
final var fakePlayerFile = new File(testDirectory, "fake_player_file");
Files.write(fakePlayerFile.toPath(), "test content".getBytes());
final var fakePlayerFile2 = new File(testDirectory, "fake_player_file_2");
Files.write(fakePlayerFile2.toPath(), "test content".getBytes());
when(mockSerializationFactory.createDeserializer(anyString())).thenReturn(mockPlayer);
final List<Player> result = fileSystemDB.findAll();
assertEquals(2, result.size());
}
}
}