summaryrefslogtreecommitdiff
path: root/pse-server/src/test
diff options
context:
space:
mode:
authorOrangerot <purple@orangerot.dev>2024-06-19 00:14:49 +0200
committerOrangerot <purple@orangerot.dev>2024-06-27 12:11:14 +0200
commit5b8851b6c268d0e93c158908fbfae9f8473db5ff (patch)
tree7010eb85d86fa2da06ea4ffbcdb01a685d502ae8 /pse-server/src/test
Initial commitHEADmain
Diffstat (limited to 'pse-server/src/test')
-rw-r--r--pse-server/src/test/java/org/psesquared/server/BaseTest.java163
-rw-r--r--pse-server/src/test/java/org/psesquared/server/ServerApplicationTests.java11
-rw-r--r--pse-server/src/test/java/org/psesquared/server/TestAsyncConfig.java16
-rw-r--r--pse-server/src/test/java/org/psesquared/server/authentication/api/data/access/AuthenticationDaoTest.java51
-rw-r--r--pse-server/src/test/java/org/psesquared/server/authentication/api/service/AuthenticationServiceTest.java222
-rw-r--r--pse-server/src/test/java/org/psesquared/server/authentication/api/service/EmailServiceTests.java36
-rw-r--r--pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeActionDaoTests.java20
-rw-r--r--pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeDaoTests.java29
-rw-r--r--pse-server/src/test/java/org/psesquared/server/episode/actions/api/service/EpisodeActionServiceTests.java368
-rw-r--r--pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionActionDaoTests.java89
-rw-r--r--pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionDaoTests.java36
-rw-r--r--pse-server/src/test/java/org/psesquared/server/subscriptions/api/service/SubscriptionServiceTests.java128
-rw-r--r--pse-server/src/test/java/org/psesquared/server/util/RssParserTests.java135
13 files changed, 1304 insertions, 0 deletions
diff --git a/pse-server/src/test/java/org/psesquared/server/BaseTest.java b/pse-server/src/test/java/org/psesquared/server/BaseTest.java
new file mode 100644
index 0000000..0d200e6
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/BaseTest.java
@@ -0,0 +1,163 @@
+package org.psesquared.server;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Optional;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.psesquared.server.authentication.api.data.access.AuthenticationDao;
+import org.psesquared.server.episode.actions.api.data.access.EpisodeActionDao;
+import org.psesquared.server.episode.actions.api.data.access.EpisodeDao;
+import org.psesquared.server.model.Action;
+import org.psesquared.server.model.Episode;
+import org.psesquared.server.model.EpisodeAction;
+import org.psesquared.server.model.Role;
+import org.psesquared.server.model.Subscription;
+import org.psesquared.server.model.SubscriptionAction;
+import org.psesquared.server.model.User;
+import org.psesquared.server.subscriptions.api.data.access.SubscriptionActionDao;
+import org.psesquared.server.subscriptions.api.data.access.SubscriptionDao;
+import org.psesquared.server.util.RssParser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
+public abstract class BaseTest {
+
+ @Autowired
+ public AuthenticationDao authenticationDao;
+
+ @Autowired
+ public SubscriptionDao subscriptionDao;
+
+ @Autowired
+ public EpisodeDao episodeDao;
+
+ @Autowired
+ public SubscriptionActionDao subscriptionActionDao;
+
+ @Autowired
+ public EpisodeActionDao episodeActionDao;
+
+ @Autowired
+ public RssParser rssParser;
+
+ public static int numberOfUsers = 1;
+ public static int numberOfSubscriptionsPerUser = 2;
+ public static int numberOfEpisodesPerSubscription = 3;
+
+ @BeforeEach
+ @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
+ public void setUp() {
+ setUpUsers(numberOfUsers, numberOfSubscriptionsPerUser, numberOfEpisodesPerSubscription);
+ }
+
+ // URL Scheme:
+ // Subscriptions: file:/"your project path"/testfeeds/testPodcast0.xml
+ // Episodes: /testfeeds/testPodcast0/episode0
+ private void setUpUsers(int userCount, int subCount, int epCount) {
+ for (int i = 0; i < userCount; i++) {
+ User user = new User();
+ user.setUsername("testUser" + i);
+ user.setPassword("testPassword123!" + i);
+ user.setEmail(user.getUsername() + "@mail.de");
+ user.setRole(Role.USER);
+
+ // Store user in database
+ authenticationDao.save(user);
+
+ // Check if the user exists in the database
+ Optional<User> savedUser = authenticationDao.findByUsername("testuser" + i);
+ assertNotNull(savedUser);
+
+ setUpSubscriptionsAndEpisodes(subCount, epCount, user);
+ }
+ }
+
+ private void setUpSubscriptionsAndEpisodes(int subCount, int epCount, User user) {
+ for (int i = 0; i < subCount; i++) {
+ Subscription subscription = new Subscription();
+ subscription.setTitle("testPodcast" + i);
+ String url = new File(String.format("testfeeds/testPodcast%d.xml", i)).toURI().toString();
+ subscription.setUrl(url);
+ subscription.setTimestamp(i * 1000000);
+
+ // Save the Subscription in the database
+ subscriptionDao.save(subscription);
+
+ // Check if the Subscription exists in the database
+ Optional<Subscription> savedSubscription = subscriptionDao
+ .findByUrl(url);
+ assertNotNull(savedSubscription);
+
+ // create SubscriptionAction for User and Subscription
+ SubscriptionAction subscriptionAction = new SubscriptionAction();
+ subscriptionAction.setAdded(true);
+ // subscriptionAction.setAdded(i % 2 == 0); (every other Action is marked
+ // as inactive/removed)
+ subscriptionAction.setUser(user);
+ subscriptionAction.setTimestamp(i * 1000000);
+ subscriptionAction.setSubscription(subscription);
+
+ // save SubscriptionAction to the database
+ subscriptionActionDao.save(subscriptionAction);
+
+ // Check if the SubscriptionAction exists in the database
+ Optional<SubscriptionAction> savedSubscriptionAction = subscriptionActionDao.findByUserAndSubscription(user,
+ subscription);
+ assertNotNull(savedSubscriptionAction);
+
+ for (int j = 0; j < epCount; j++) {
+ Episode episode = new Episode();
+ episode.setSubscription(subscription);
+
+ String episodeUrl = String.format("/testfeeds/testPodcast%d/episode%d.mp3", i, j);
+ episode.setUrl(episodeUrl);
+ episode.setTotal((j + 1) * 100);
+ episode.setTitle("testEpisode" + j);
+
+ // save the Episdoe in the database
+ episodeDao.save(episode);
+
+ // Check if the Episode exists in the database
+ Optional<Episode> savedEpisode = episodeDao.findByUrl(episodeUrl);
+ assertNotNull(savedEpisode);
+
+ // create EpisodeAction for User and Episode
+ EpisodeAction episodeAction = new EpisodeAction();
+ episodeAction.setEpisode(episode);
+ episodeAction.setAction(Action.PLAY);
+ episodeAction.setUser(user);
+ episodeAction.setTimestamp(LocalDateTime.ofEpochSecond(j * 1000000,
+ 0,
+ ZoneOffset.UTC));
+ episodeAction.setStarted((j + 1));
+ episodeAction.setPosition((j + 1) * 10);
+
+ // save EpisodeAction in the Database
+ episodeActionDao.save(episodeAction);
+
+ // Check if the EpisodeAction exists in the database
+ Optional<EpisodeAction> savedEpisodeAction = episodeActionDao.findById(episodeAction.getId());
+ assertNotNull(savedEpisodeAction);
+ }
+
+ }
+ }
+
+ @AfterEach
+ public void cleanUp() {
+ authenticationDao.deleteAll();
+ episodeDao.deleteAll();
+ episodeActionDao.deleteAll();
+ subscriptionDao.deleteAll();
+ subscriptionActionDao.deleteAll();
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/ServerApplicationTests.java b/pse-server/src/test/java/org/psesquared/server/ServerApplicationTests.java
new file mode 100644
index 0000000..68a6993
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/ServerApplicationTests.java
@@ -0,0 +1,11 @@
+package org.psesquared.server;
+
+import org.junit.jupiter.api.Test;
+
+class ServerApplicationTests extends BaseTest {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/TestAsyncConfig.java b/pse-server/src/test/java/org/psesquared/server/TestAsyncConfig.java
new file mode 100644
index 0000000..6fe9803
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/TestAsyncConfig.java
@@ -0,0 +1,16 @@
+package org.psesquared.server;
+
+import java.util.concurrent.Executor;
+
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.task.SyncTaskExecutor;
+
+@TestConfiguration
+public class TestAsyncConfig {
+
+ @Bean
+ public Executor taskExecutor() {
+ return new SyncTaskExecutor();
+ }
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/authentication/api/data/access/AuthenticationDaoTest.java b/pse-server/src/test/java/org/psesquared/server/authentication/api/data/access/AuthenticationDaoTest.java
new file mode 100644
index 0000000..c6325ad
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/authentication/api/data/access/AuthenticationDaoTest.java
@@ -0,0 +1,51 @@
+package org.psesquared.server.authentication.api.data.access;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.model.Role;
+import org.psesquared.server.model.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@DataJpaTest
+@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
+public class AuthenticationDaoTest {
+
+ @Autowired
+ private AuthenticationDao authenticationDao;
+
+ @BeforeEach
+ public void init() {
+ var user = User.builder()
+ .username("username")
+ .email("email")
+ .password("password")
+ .enabled(false)
+ .createdAt(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC))
+ .role(Role.USER)
+ .build();
+ authenticationDao.save(user);
+ }
+
+ @Test
+ public void updateUser() {
+ var user = authenticationDao.findByUsername("username")
+ .orElseThrow();
+ user.setEnabled(true);
+ }
+
+ @AfterEach
+ public void assertUpdated() {
+ var foundUser = authenticationDao.findByUsername("username")
+ .orElseThrow();
+ assertTrue(foundUser.isEnabled());
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/authentication/api/service/AuthenticationServiceTest.java b/pse-server/src/test/java/org/psesquared/server/authentication/api/service/AuthenticationServiceTest.java
new file mode 100644
index 0000000..c8f10b6
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/authentication/api/service/AuthenticationServiceTest.java
@@ -0,0 +1,222 @@
+package org.psesquared.server.authentication.api.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.authentication.api.controller.ChangePasswordRequest;
+import org.psesquared.server.authentication.api.controller.PasswordRequest;
+import org.psesquared.server.authentication.api.controller.UserInfoRequest;
+import org.psesquared.server.config.JwtService;
+import org.psesquared.server.model.Subscription;
+import org.psesquared.server.model.SubscriptionAction;
+import org.psesquared.server.model.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+public class AuthenticationServiceTest extends BaseTest {
+
+ private static final String recipient = "pse-squared@outlook.com";
+
+ @Autowired
+ public AuthenticationService authenticationService;
+
+ @Autowired
+ public JwtService jwtService;
+
+ @Autowired
+ public EncryptionService encryptionService;
+
+ @Autowired
+ PasswordEncoder passwordEncoder;
+
+ @Test
+ public void testRegisterUser() {
+ UserInfoRequest newUserInfo = new UserInfoRequest("newUsername", "newUserMail@test.com", "123abcABC!");
+ HttpStatus registrationStatus = authenticationService.registerUser(newUserInfo);
+ assertEquals(HttpStatus.OK, registrationStatus);
+
+ UserInfoRequest wrongEmail = new UserInfoRequest("newUsername", "wrongNewUserMail@test.com", "123abcABC!");
+ HttpStatus wrongEmailStatus = authenticationService.registerUser(wrongEmail);
+ assertEquals(HttpStatus.BAD_REQUEST, wrongEmailStatus);
+
+ UserInfoRequest wrongPassword = new UserInfoRequest("newUsername", "newUserMail@test.com", "wrong123abcABC!");
+ HttpStatus wrongPasswordStatus = authenticationService.registerUser(wrongPassword);
+ assertEquals(HttpStatus.BAD_REQUEST, wrongPasswordStatus);
+
+ UserInfoRequest userInfo = new UserInfoRequest("testUser0", "testUser0@mail.de", "testPassword123!0");
+ HttpStatus status = authenticationService.registerUser(userInfo);
+ assertEquals(HttpStatus.BAD_REQUEST, status);
+ }
+
+ @Test
+ public void testInvalidVerifyRegistration() {
+ HttpStatus status = authenticationService.verifyRegistration("notARegisteredUser", "notAValidToken");
+ assertEquals(HttpStatus.NOT_FOUND, status);
+ status = authenticationService.verifyRegistration("testUser0", "stillNotAValidToken");
+ assertEquals(HttpStatus.UNAUTHORIZED, status);
+ User user = authenticationDao.findByUsername("testUser0").orElseThrow();
+ user.setEnabled(true);
+ authenticationDao.save(user);
+ status = authenticationService.verifyRegistration("testUser0", "stillNotAValidToken");
+ assertEquals(HttpStatus.BAD_REQUEST, status);
+ }
+
+ @Test
+ public void testVerifyRegistration() {
+ User user = authenticationDao.findByUsername("testUser0").orElseThrow();
+ String token = jwtService.generateUrlTokenString(user);
+ HttpStatus status = authenticationService.verifyRegistration("testUser0", token);
+ assertEquals(HttpStatus.OK, status);
+ Assertions.assertTrue(authenticationDao.findByUsername("testUser0").orElseThrow().isEnabled());
+ }
+
+ @Test
+ public void testLogin() {
+ HttpServletResponse response = new MockHttpServletResponse();
+ HttpStatus status = authenticationService.login("notARegisteredUser", response);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+ status = authenticationService.login("testUser0", response);
+ assertEquals(HttpStatus.OK, status);
+ }
+
+ @Test
+ public void testLogout() {
+ HttpServletResponse response = new MockHttpServletResponse();
+ HttpStatus status = authenticationService.logout("notARegisteredUser", response);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+ status = authenticationService.logout("testUser0", response);
+ assertEquals(HttpStatus.OK, status);
+ }
+
+ @Test
+ public void testForgotPassword() {
+ final String email = "testUser0@mail.de";
+ HttpStatus status = authenticationService.forgotPassword(email);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+
+ User user = authenticationDao.findByUsername("testUser0").orElseThrow();
+ user.setEmail(encryptionService.saltAndHashEmail(user.getEmail()));
+ authenticationDao.save(user);
+
+ final String saltedAndHashedEmail = user.getEmail();
+
+ status = authenticationService.forgotPassword(saltedAndHashedEmail);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+ status = authenticationService.forgotPassword(email);
+ assertEquals(HttpStatus.OK, status);
+ }
+
+ @Test
+ public void testResetPassword() {
+ User user = authenticationDao.findByUsername("testUser0").orElseThrow();
+ String token = "";
+ PasswordRequest passwordRequest = new PasswordRequest("");
+ HttpStatus status = authenticationService.resetPassword("notAValidUser", token, passwordRequest);
+ assertEquals(HttpStatus.BAD_REQUEST, status);
+
+ final String password = "abcAbc123!";
+ passwordRequest = new PasswordRequest(password);
+ status = authenticationService.resetPassword("notAValidUser", token, passwordRequest);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+
+ status = authenticationService.resetPassword(user.getUsername(), token, passwordRequest);
+ assertEquals(HttpStatus.UNAUTHORIZED, status);
+
+ token = jwtService.generateUrlTokenString(user);
+ status = authenticationService.resetPassword(user.getUsername(), token, passwordRequest);
+ assertEquals(HttpStatus.OK, status);
+
+ user = authenticationDao.findByUsername("testUser0").orElseThrow();
+ assertTrue(passwordEncoder.matches(password, user.getPassword()));
+ }
+
+ @Test
+ public void testChangePassword() {
+ User user = authenticationDao.findByUsername("testUser0").orElseThrow();
+ ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest("", "");
+ HttpStatus status = authenticationService.changePassword("notAValidUser", changePasswordRequest);
+ assertEquals(HttpStatus.BAD_REQUEST, status);
+
+ final String newPassword = "abcAbc123!";
+ changePasswordRequest = new ChangePasswordRequest("", newPassword);
+ status = authenticationService.changePassword("notAValidUser", changePasswordRequest);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+
+ changePasswordRequest = new ChangePasswordRequest("notTheRightPassword", newPassword);
+ status = authenticationService.changePassword(user.getUsername(), changePasswordRequest);
+ assertEquals(HttpStatus.BAD_REQUEST, status);
+
+ changePasswordRequest = new ChangePasswordRequest(user.getPassword(), newPassword);
+ user.setPassword(passwordEncoder.encode(user.getPassword()));
+ authenticationDao.save(user);
+ status = authenticationService.changePassword(user.getUsername(), changePasswordRequest);
+ assertEquals(HttpStatus.OK, status);
+ }
+
+ @Test
+ public void testDeleteUser() {
+ PasswordRequest passwordRequest = new PasswordRequest("");
+ HttpStatus status = authenticationService.deleteUser("notAValidUser", passwordRequest);
+ assertEquals(HttpStatus.NOT_FOUND, status);
+
+ User user = authenticationDao.findByUsername("testUser0").orElseThrow();
+
+ passwordRequest = new PasswordRequest("notTheRightPassword");
+ status = authenticationService.deleteUser(user.getUsername(), passwordRequest);
+ assertEquals(HttpStatus.BAD_REQUEST, status);
+
+ passwordRequest = new PasswordRequest(user.getPassword());
+ user.setPassword(passwordEncoder.encode(user.getPassword()));
+ authenticationDao.save(user);
+
+ status = authenticationService.deleteUser(user.getUsername(), passwordRequest);
+ assertEquals(HttpStatus.OK, status);
+ }
+
+ @Test
+ public void testCascadeDelete() {
+ subscriptionActionDao.deleteAll();
+ UserInfoRequest userInfo = new UserInfoRequest("username", recipient, "123abcABC!");
+ authenticationService.registerUser(userInfo);
+
+ var user = authenticationDao.findByUsername(userInfo.username())
+ .orElseThrow();
+
+ var sub = Subscription.builder()
+ .url("url")
+ .title("title")
+ .timestamp(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC))
+ .build();
+ subscriptionDao.save(sub);
+
+ var subAction1 = SubscriptionAction.builder()
+ .user(user)
+ .timestamp(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC))
+ .subscription(sub)
+ .added(true)
+ .build();
+ subscriptionActionDao.save(subAction1);
+ var subAction2 = SubscriptionAction.builder()
+ .user(user)
+ .timestamp(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC))
+ .subscription(sub)
+ .added(false)
+ .build();
+ subscriptionActionDao.save(subAction2);
+
+ authenticationService.deleteUser(userInfo.username(), new PasswordRequest(userInfo.password()));
+
+ assertEquals(0L, subscriptionActionDao.count());
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/authentication/api/service/EmailServiceTests.java b/pse-server/src/test/java/org/psesquared/server/authentication/api/service/EmailServiceTests.java
new file mode 100644
index 0000000..ed1061e
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/authentication/api/service/EmailServiceTests.java
@@ -0,0 +1,36 @@
+package org.psesquared.server.authentication.api.service;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.model.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+public class EmailServiceTests {
+
+ @Autowired
+ private EmailServiceImpl emailService;
+
+ private static final String recipient = "pse-squared@outlook.com";
+
+ private User user;
+
+ @BeforeEach
+ void beforeEach() {
+ user = User.builder()
+ .username("Jeff")
+ .email(recipient)
+ .build();
+ }
+
+ @Test
+ void sendValidationMail() {
+ emailService.sendVerification(user.getEmail(), user);
+ }
+
+ @Test
+ void sendPasswordResetMail() {
+ emailService.sendPasswordReset(user.getEmail(), user);
+ }
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeActionDaoTests.java b/pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeActionDaoTests.java
new file mode 100644
index 0000000..3448330
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeActionDaoTests.java
@@ -0,0 +1,20 @@
+package org.psesquared.server.episode.actions.api.data.access;
+
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.model.User;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EpisodeActionDaoTests extends BaseTest {
+
+ @Test
+ public void deleteUserTest() {
+ String username = "testUser0";
+ User user = authenticationDao.findByUsername(username).orElseThrow();
+ assertEquals(numberOfEpisodesPerSubscription * numberOfSubscriptionsPerUser, episodeActionDao.findByUserUsername(username).size());
+ authenticationDao.delete(user);
+ assertEquals(0, episodeActionDao.findByUserUsername(username).size());
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeDaoTests.java b/pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeDaoTests.java
new file mode 100644
index 0000000..6c1d70f
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeDaoTests.java
@@ -0,0 +1,29 @@
+package org.psesquared.server.episode.actions.api.data.access;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.model.Subscription;
+
+public class EpisodeDaoTests extends BaseTest {
+
+ @Test
+ public void deleteCascadeTest() {
+ String username = "testUser0";
+ assertDoesNotThrow(() -> authenticationDao.findByUsername(username).orElseThrow());
+ String subscriptionsUrl = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ Subscription subscription = subscriptionDao.findByUrl(subscriptionsUrl).orElseThrow();
+ assertDoesNotThrow(() -> subscription.getEpisodes());
+ Assertions.assertEquals(numberOfEpisodesPerSubscription,
+ episodeActionDao.findByUserUsernameAndEpisodeSubscriptionUrl(username, subscriptionsUrl).size());
+ episodeDao.deleteAll(subscription.getEpisodes());
+ Assertions.assertEquals(0,
+ episodeActionDao.findByUserUsernameAndEpisodeSubscriptionUrl(username, subscriptionsUrl).size());
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/episode/actions/api/service/EpisodeActionServiceTests.java b/pse-server/src/test/java/org/psesquared/server/episode/actions/api/service/EpisodeActionServiceTests.java
new file mode 100644
index 0000000..75dd110
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/episode/actions/api/service/EpisodeActionServiceTests.java
@@ -0,0 +1,368 @@
+package org.psesquared.server.episode.actions.api.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.episode.actions.api.controller.EpisodeActionPost;
+import org.psesquared.server.model.Action;
+import org.psesquared.server.model.Episode;
+import org.psesquared.server.model.EpisodeAction;
+import org.psesquared.server.model.Subscription;
+import org.psesquared.server.model.User;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class EpisodeActionServiceTests extends BaseTest {
+
+ @Autowired
+ private EpisodeActionService episodeActionService;
+
+ @Test
+ public void addEpisodeActionsTest1() {
+ // episodeActionPosts zu Subscriptions und Episoden hinzufügen, die noch nicht
+ // existieren
+ List<EpisodeActionPost> episodeActionPosts = new ArrayList<>();
+ String username = "testUser0";
+ Assertions.assertEquals(numberOfEpisodesPerSubscription * numberOfSubscriptionsPerUser,
+ episodeActionDao.findByUserUsername(username).size());
+ int numberOfNewEpisodes = 2;
+ for (int i = 0; i < numberOfNewEpisodes; i++) {
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(new File("testfeeds/newTestSubscription1.xml").toURI().toString())
+ .episodeUrl(String.format("/testfeeds/newTestSubscription1/episode%d.mp3", i))
+ .title("testEpisode" + i)
+ .guid(UUID.randomUUID().toString())
+ .total(10 * i)
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.now())
+ .action(Action.PLAY)
+ .started(i)
+ .position(i + 3)
+ .build())
+ .build());
+ }
+ episodeActionService.addEpisodeActions(username, episodeActionPosts);
+
+ Assertions.assertEquals(numberOfSubscriptionsPerUser * numberOfEpisodesPerSubscription + numberOfNewEpisodes,
+ episodeActionDao.findByUserUsername(username).size());
+ }
+
+ @Test
+ public void addEpisodeActionsTest2() {
+ // Alte EpisodeAction durch neuere Überschreiben
+
+ final String username = "testUser0";
+ final String episodeUrl = "/testfeeds/testPodcast0/episode0.mp3";
+ final User user = authenticationDao.findByUsername(username).orElseThrow();
+
+ EpisodeAction oldEpisodeActionTest = episodeActionDao
+ .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY).orElseThrow();
+ List<EpisodeActionPost> episodeActionPosts = new ArrayList<>();
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(oldEpisodeActionTest.getEpisode().getSubscription().getUrl())
+ .episodeUrl(oldEpisodeActionTest.getEpisode().getUrl())
+ .title(oldEpisodeActionTest.getEpisode().getTitle())
+ .guid(oldEpisodeActionTest.getEpisode().getGuid())
+ .total(oldEpisodeActionTest.getEpisode().getTotal())
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ oldEpisodeActionTest.getTimestamp().toEpochSecond(ZoneOffset.UTC) + 1,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.PLAY)
+ .started(oldEpisodeActionTest.getStarted())
+ .position(oldEpisodeActionTest.getPosition())
+ .build())
+ .build());
+
+ LocalDateTime oldTimestamp = episodeActionDao.findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ episodeActionService.addEpisodeActions(user.getUsername(), episodeActionPosts);
+ LocalDateTime newTimestamp = episodeActionDao.findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ assertNotEquals(oldTimestamp, newTimestamp);
+ }
+
+ @Test
+ public void addEpisodeActionTest3() {
+ // EpisodeActions nicht nach Timestamp sortiert
+ // Überschreibe EpisodeAction durch neueste Action
+
+ final String username = "testUser0";
+ final String episodeUrl = "/testfeeds/testPodcast0/episode0.mp3";
+ final User user = authenticationDao.findByUsername(username).orElseThrow();
+
+ EpisodeAction oldEpisodeActionTest = episodeActionDao
+ .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY).orElseThrow();
+ List<EpisodeActionPost> episodeActionPosts = new ArrayList<>();
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(oldEpisodeActionTest.getEpisode().getSubscription().getUrl())
+ .episodeUrl(oldEpisodeActionTest.getEpisode().getUrl())
+ .title(oldEpisodeActionTest.getEpisode().getTitle())
+ .guid(oldEpisodeActionTest.getEpisode().getGuid())
+ .total(oldEpisodeActionTest.getEpisode().getTotal())
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ oldEpisodeActionTest.getTimestamp().toEpochSecond(ZoneOffset.UTC) + 2,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.PLAY)
+ .started(oldEpisodeActionTest.getStarted())
+ .position(oldEpisodeActionTest.getPosition())
+ .build())
+ .build());
+
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(oldEpisodeActionTest.getEpisode().getSubscription().getUrl())
+ .episodeUrl(oldEpisodeActionTest.getEpisode().getUrl())
+ .title(oldEpisodeActionTest.getEpisode().getTitle())
+ .guid(oldEpisodeActionTest.getEpisode().getGuid())
+ .total(oldEpisodeActionTest.getEpisode().getTotal())
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ oldEpisodeActionTest.getTimestamp().toEpochSecond(ZoneOffset.UTC) + 1,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.PLAY)
+ .started(oldEpisodeActionTest.getStarted())
+ .position(oldEpisodeActionTest.getPosition())
+ .build())
+ .build());
+
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(oldEpisodeActionTest.getEpisode().getSubscription().getUrl())
+ .episodeUrl(oldEpisodeActionTest.getEpisode().getUrl())
+ .title(oldEpisodeActionTest.getEpisode().getTitle())
+ .guid(oldEpisodeActionTest.getEpisode().getGuid())
+ .total(oldEpisodeActionTest.getEpisode().getTotal())
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ oldEpisodeActionTest.getTimestamp().toEpochSecond(ZoneOffset.UTC) + 3,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.PLAY)
+ .started(oldEpisodeActionTest.getStarted())
+ .position(oldEpisodeActionTest.getPosition())
+ .build())
+ .build());
+
+ LocalDateTime oldTimestamp = episodeActionDao.findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ episodeActionService.addEpisodeActions(user.getUsername(), episodeActionPosts);
+ LocalDateTime newTimestamp = episodeActionDao.findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ assertTrue(newTimestamp.isAfter(oldTimestamp));
+ assertEquals(3, (int) ChronoUnit.SECONDS.between(oldTimestamp, newTimestamp));
+ }
+
+ @Test
+ public void addEpisodeActionsTest4() {
+ // Ignoriere Episode Action, die nicht PLAY als Action hat
+
+ final String username = "testUser0";
+ final String episodeUrl = "/testfeeds/testPodcast0/episode0.mp3";
+ final User user = authenticationDao.findByUsername(username).orElseThrow();
+
+ EpisodeAction oldEpisodeActionTest = episodeActionDao
+ .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY).orElseThrow();
+ List<EpisodeActionPost> episodeActionPosts = new ArrayList<>();
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(oldEpisodeActionTest.getEpisode().getSubscription().getUrl())
+ .episodeUrl(oldEpisodeActionTest.getEpisode().getUrl())
+ .title(oldEpisodeActionTest.getEpisode().getTitle())
+ .guid(oldEpisodeActionTest.getEpisode().getGuid())
+ .total(oldEpisodeActionTest.getEpisode().getTotal())
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ oldEpisodeActionTest.getTimestamp().toEpochSecond(ZoneOffset.UTC) + 1,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.DOWNLOAD)
+ .started(oldEpisodeActionTest.getStarted())
+ .position(oldEpisodeActionTest.getPosition())
+ .build())
+ .build());
+
+ LocalDateTime oldTimestamp = episodeActionDao.findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ episodeActionService.addEpisodeActions(user.getUsername(), episodeActionPosts);
+ LocalDateTime newTimestamp = episodeActionDao.findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ assertEquals(oldTimestamp, newTimestamp);
+ }
+
+ @Test
+ public void addEpisodeActionsTest5() {
+ // Episode in Datenbank ohne GUID, EpisodeAction mit selbem Link mit GUID
+
+ final String username = "testUser0";
+ final String podcastName = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ final String episodeBaseUrl = "/testfeeds/testPodcast0/episode";
+ final User user = authenticationDao.findByUsername(username).orElseThrow();
+
+ final int episodeIndex = numberOfEpisodesPerSubscription;
+
+ // Setup - Episode ohne GUID einfügen
+ Optional<Subscription> savedSubscription = subscriptionDao.findByUrl(podcastName);
+ assertTrue(savedSubscription.isPresent());
+
+ final String episodeUrl = episodeBaseUrl + episodeIndex + ".mp3";
+
+ Episode newEpisode = new Episode();
+ newEpisode.setSubscription(savedSubscription.get());
+ newEpisode.setUrl(episodeUrl);
+ newEpisode.setTotal((episodeIndex + 1) * 100);
+ newEpisode.setTitle("testEpisode" + episodeIndex);
+
+ episodeDao.save(newEpisode);
+ Optional<Episode> savedEpisode = episodeDao.findByUrl(episodeUrl);
+ assertTrue(savedEpisode.isPresent());
+ assertNull(savedEpisode.get().getGuid());
+
+ EpisodeAction episodeAction = new EpisodeAction();
+ episodeAction.setEpisode(newEpisode);
+ episodeAction.setAction(Action.PLAY);
+ episodeAction.setUser(user);
+ episodeAction.setTimestamp(LocalDateTime.ofEpochSecond(
+ LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + (long) episodeIndex * 1000000,
+ 0,
+ ZoneOffset.UTC));
+ episodeAction.setStarted((episodeIndex + 1));
+ episodeAction.setPosition((episodeIndex + 1) * 10);
+
+ episodeActionDao.save(episodeAction);
+ Optional<EpisodeAction> savedEpisodeAction = episodeActionDao.findById(episodeAction.getId());
+ assertTrue(savedEpisodeAction.isPresent());
+
+ EpisodeAction oldEpisodeActionTest = episodeActionDao
+ .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY).orElseThrow();
+ List<EpisodeActionPost> episodeActionPosts = new ArrayList<>();
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(oldEpisodeActionTest.getEpisode().getSubscription().getUrl())
+ .episodeUrl(oldEpisodeActionTest.getEpisode().getUrl())
+ .title(oldEpisodeActionTest.getEpisode().getTitle())
+ .guid("Alphabet")
+ .total(oldEpisodeActionTest.getEpisode().getTotal())
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ oldEpisodeActionTest.getTimestamp().toEpochSecond(ZoneOffset.UTC) + 1,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.PLAY)
+ .started(oldEpisodeActionTest.getStarted())
+ .position(oldEpisodeActionTest.getPosition())
+ .build())
+ .build());
+
+ LocalDateTime oldTimestamp = episodeActionDao
+ .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ episodeActionService.addEpisodeActions(user.getUsername(), episodeActionPosts);
+ LocalDateTime newTimestamp = episodeActionDao
+ .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY)
+ .orElseThrow().getTimestamp();
+ assertNotEquals(oldTimestamp, newTimestamp);
+ savedEpisode = episodeDao.findByUrl(episodeUrl);
+ assertTrue(savedEpisode.isPresent());
+ assertEquals("Alphabet", savedEpisode.get().getGuid());
+ }
+
+ @Test
+ public void addEpisodeActionsTest6() {
+ // Episode nicht in Datenbank, EpisodeAction mit neuer Episode
+
+ final String username = "testUser0";
+ final String podcastName = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ final String episodeBaseUrl = "/testfeeds/testPodcast0/episode";
+ final User user = authenticationDao.findByUsername(username).orElseThrow();
+
+ final int episodeIndex = numberOfEpisodesPerSubscription;
+
+ // Setup - Episode ohne GUID einfügen
+ Optional<Subscription> savedSubscription = subscriptionDao.findByUrl(podcastName);
+ assertTrue(savedSubscription.isPresent());
+
+ final String episodeUrl = episodeBaseUrl + episodeIndex + ".mp3";
+
+ Optional<Episode> noEpisode = episodeDao.findByUrl(episodeUrl);
+ assertFalse(noEpisode.isPresent());
+
+ List<EpisodeActionPost> episodeActionPosts = new ArrayList<>();
+ episodeActionPosts.add(EpisodeActionPost.builder()
+ .podcastUrl(podcastName)
+ .episodeUrl(episodeUrl)
+ .title("testEpisode" + episodeIndex)
+ .guid("Alphabet")
+ .total((episodeIndex + 1) * 100)
+ .episodeAction(EpisodeAction.builder()
+ .timestamp(LocalDateTime.ofEpochSecond(
+ LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + (long) episodeIndex * 1000000,
+ 0,
+ ZoneOffset.UTC))
+ .action(Action.PLAY)
+ .started((episodeIndex + 1))
+ .position((episodeIndex + 1) * 10)
+ .build())
+ .build());
+ episodeActionService.addEpisodeActions(user.getUsername(), episodeActionPosts);
+ noEpisode = episodeDao.findByUrl(episodeUrl);
+ assertTrue(noEpisode.isPresent());
+ }
+
+ @Test
+ public void getEpisodeActionsTest() {
+ String username = "testUser0";
+ List<EpisodeActionPost> episodeActionPosts = episodeActionService.getEpisodeActions(username);
+ assertEquals(numberOfSubscriptionsPerUser * numberOfEpisodesPerSubscription, episodeActionPosts.size());
+ }
+
+ @Test
+ public void getEpisodeActionsOfPodcastTest() {
+ String username = "testUser0";
+ String subscriptionUrl = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ List<EpisodeActionPost> episodeActionPosts = episodeActionService.getEpisodeActionsOfPodcast(username,
+ subscriptionUrl);
+ assertEquals(numberOfEpisodesPerSubscription, episodeActionPosts.size());
+ }
+
+ @Test
+ public void getEpisodeActionsSinceTest() {
+ String username = "testUser0";
+ // Jede Episode in einer Subscription hat einen Timestamp von zusätzlichen
+ // 1000000 zur Zeit 0
+ int factor = 2;
+ int timeDifference = 1000000;
+ int numberOfEpisodes = numberOfSubscriptionsPerUser * numberOfEpisodesPerSubscription;
+ long since = factor * timeDifference;
+ List<EpisodeActionPost> episodeActionPosts = episodeActionService.getEpisodeActionsSince(username, since);
+ assertEquals(numberOfEpisodes - factor * numberOfSubscriptionsPerUser, episodeActionPosts.size());
+ }
+
+ @Test
+ public void getEpisodeActionsOfPodcastSinceTest() {
+ String username = "testUser0";
+ String subscriptionUrl = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ // Jede EpisodeAction in einer Subscription hat einen Timestamp von zusätzlichen
+ // 1000000 zur Zeit 0
+ int factor = 1;
+ int timeDifference = 1000000;
+ long since = factor * timeDifference;
+ List<EpisodeActionPost> episodeActionPosts = episodeActionService.getEpisodeActionsOfPodcastSince(username,
+ subscriptionUrl, since);
+ assertEquals(numberOfEpisodesPerSubscription - factor, episodeActionPosts.size());
+ }
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionActionDaoTests.java b/pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionActionDaoTests.java
new file mode 100644
index 0000000..f296a72
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionActionDaoTests.java
@@ -0,0 +1,89 @@
+package org.psesquared.server.subscriptions.api.data.access;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.model.Subscription;
+import org.psesquared.server.model.SubscriptionAction;
+import org.psesquared.server.model.User;
+
+public class SubscriptionActionDaoTests extends BaseTest {
+
+ @Test
+ public void existsByUserAndSubscriptionTest() {
+ String username = "testUser0";
+ String subscriptionUrl = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ User user = authenticationDao.findByUsername(username).orElseThrow();
+ Subscription subscription = subscriptionDao.findByUrl(subscriptionUrl).orElseThrow();
+ boolean exists = subscriptionActionDao.existsByUserAndSubscription(user, subscription);
+ assertTrue(exists);
+ }
+
+ @Test
+ public void findByUserAndSubscriptionTest() {
+ String username = "testUser0";
+ String subscriptionUrl = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ User user = authenticationDao.findByUsername(username).orElseThrow();
+ Subscription subscription = subscriptionDao.findByUrl(subscriptionUrl).orElseThrow();
+ SubscriptionAction subscriptionAction = subscriptionActionDao.findByUserAndSubscription(user, subscription)
+ .orElseThrow();
+ assertEquals(subscriptionAction.getSubscription().getUrl(), subscriptionUrl);
+ }
+
+ @Test
+ public void findByUserUsernameAndTimestampGreaterThanEqualTest() {
+ String username = "testUser0";
+ // Jede SubscriptionAction eines Nutzers hat einen Timestamp von zusätzlichen
+ // 1000000 zur Zeit 0
+ int factor = 1;
+ int timeDifference = 1000000;
+ long since = factor * timeDifference;
+ List<SubscriptionAction> subscriptionActions = subscriptionActionDao
+ .findByUserUsernameAndTimestampGreaterThanEqual(username, since);
+ assertEquals(numberOfSubscriptionsPerUser - factor, subscriptionActions.size());
+ }
+
+ @Test
+ public void findByUserUsernameAndAddedTrueTest() {
+ String username = "testUser0";
+ List<SubscriptionAction> subscriptionActions = subscriptionActionDao.findByUserUsernameAndAddedTrue(username);
+ assertEquals(subscriptionActions.size(), numberOfSubscriptionsPerUser);
+ }
+
+ @Test
+ public void findByUserUsernameAndAddedTrueAndTimestampGreaterThanEqualTest() {
+ String username = "testUser0";
+ // Jede SubscriptionAction eines Nutzers hat einen Timestamp von zusätzlichen
+ // 1000000 zur Zeit 0
+ // int puffer = 1000;
+ int factor = 1;
+ int timeDifference = 1000000;
+ long since = factor * timeDifference;
+ List<SubscriptionAction> subscriptionActions = subscriptionActionDao
+ .findByUserUsernameAndTimestampGreaterThanEqual(username, since);
+ assertEquals(numberOfSubscriptionsPerUser - factor, subscriptionActions.size());
+ // Eine EpisodeAction der gefundenen EpisodeActions auf added=false setzen
+ subscriptionActions.get(0).setAdded(false);
+ subscriptionActionDao.save(subscriptionActions.get(0));
+ List<SubscriptionAction> subscriptionActions2 = subscriptionActionDao
+ .findByUserUsernameAndAddedTrueAndTimestampGreaterThanEqual(username, since);
+ assertEquals(numberOfSubscriptionsPerUser - factor - 1, subscriptionActions2.size());
+ }
+
+ @Test
+ public void deleteUserTest() {
+ String username = "testUser0";
+ User user = authenticationDao.findByUsername(username).orElseThrow();
+ Assertions.assertEquals(numberOfSubscriptionsPerUser,
+ subscriptionActionDao.findByUserUsernameAndAddedTrue(username).size());
+ authenticationDao.delete(user);
+ Assertions.assertEquals(0, subscriptionActionDao.findByUserUsernameAndAddedTrue(username).size());
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionDaoTests.java b/pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionDaoTests.java
new file mode 100644
index 0000000..3ae0089
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionDaoTests.java
@@ -0,0 +1,36 @@
+package org.psesquared.server.subscriptions.api.data.access;
+
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.model.Subscription;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.File;
+
+public class SubscriptionDaoTests extends BaseTest {
+
+ @Test
+ public void findByUrlTest() {
+ String url = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ Subscription subscription = subscriptionDao.findByUrl(url).orElseThrow();
+ assertEquals(subscription.getUrl(), url);
+ }
+
+ @Test
+ public void existsByUrlTest() {
+ String url = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ boolean exists = subscriptionDao.existsByUrl(url);
+ assertTrue(exists);
+ boolean notExists = subscriptionDao.existsByUrl("blablabla");
+ assertFalse(notExists);
+ }
+
+ @Test
+ public void testCascadeDelete() {
+ String url = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ Subscription subscription = subscriptionDao.findByUrl(url).orElseThrow();
+ assertDoesNotThrow(() -> subscriptionDao.delete(subscription));
+ }
+
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/subscriptions/api/service/SubscriptionServiceTests.java b/pse-server/src/test/java/org/psesquared/server/subscriptions/api/service/SubscriptionServiceTests.java
new file mode 100644
index 0000000..3345185
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/subscriptions/api/service/SubscriptionServiceTests.java
@@ -0,0 +1,128 @@
+package org.psesquared.server.subscriptions.api.service;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.model.Subscription;
+import org.psesquared.server.model.SubscriptionAction;
+import org.psesquared.server.subscriptions.api.controller.SubscriptionDelta;
+import org.psesquared.server.subscriptions.api.controller.SubscriptionTitles;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class SubscriptionServiceTests extends BaseTest {
+
+ @Autowired
+ SubscriptionService subscriptionService;
+
+ @Test
+ public void uploadSubscriptionsTest() {
+ String username = "testUser0";
+ assertDoesNotThrow(() -> authenticationDao.findByUsername(username).orElseThrow());
+ String newSubscriptionUrl1 = new File("testfeeds/newTestSubscription1.xml").toURI().toString();
+ String newSubscriptionUrl2 = new File("testfeeds/newTestSubscription2.xml").toURI().toString();
+ List<String> subscriptionStrings = List.of(newSubscriptionUrl1, newSubscriptionUrl2);
+
+ subscriptionService.uploadSubscriptions(username, subscriptionStrings);
+ List<SubscriptionAction> subscriptionActions = subscriptionActionDao.findByUserUsernameAndAddedTrue(username);
+ int size = subscriptionActions.size();
+ assertEquals(numberOfSubscriptionsPerUser + 2, size);
+
+ subscriptionActions.get(0).setAdded(false);
+ subscriptionActionDao.save(subscriptionActions.get(0));
+ subscriptionActions = subscriptionActionDao.findByUserUsernameAndAddedTrue(username);
+ size = subscriptionActions.size();
+ assertEquals(numberOfSubscriptionsPerUser + 1, size);
+
+ String testPodcast0Url = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ List<String> subscriptionString = List.of(testPodcast0Url);
+ subscriptionService.uploadSubscriptions(username, subscriptionString);
+ subscriptionActions = subscriptionActionDao.findByUserUsernameAndAddedTrue(username);
+ size = subscriptionActions.size();
+ assertEquals(numberOfSubscriptionsPerUser + 2, size);
+ }
+
+ @Test
+ public void getSubscriptionsTest() {
+ String username = "testUser0";
+ List<String> subscriptionStrings = subscriptionService.getSubscriptions(username);
+ assertEquals(subscriptionStrings.size(), numberOfSubscriptionsPerUser);
+ }
+
+ @Test
+ public void applySubscriptionDeltaTest() {
+ String username = "testUser0";
+ String subscriptionString = new File("testfeeds/testPodcast0.xml").toURI().toString();;
+ // Überprüfen, ob Anzahl der Subscriptions des Users mit der definierten Anzahl
+ // übereinstimmt
+ assertEquals(subscriptionService.getSubscriptions(username).size(), numberOfSubscriptionsPerUser);
+ // Subscription mithilfe des Subscription-Deltas entfernen
+ subscriptionService.applySubscriptionDelta(username,
+ new SubscriptionDelta(List.of(), List.of(subscriptionString)));
+ assertEquals(subscriptionService.getSubscriptions(username).size(), numberOfSubscriptionsPerUser - 1);
+ // Subscription mithilfe des Subscription-Deltas wieder hinzufügen
+ subscriptionService.applySubscriptionDelta(username,
+ new SubscriptionDelta(List.of(subscriptionString), List.of()));
+ assertEquals(subscriptionService.getSubscriptions(username).size(), numberOfSubscriptionsPerUser);
+ }
+
+ @Test
+ public void getSubscriptionDeltaTest() {
+ String username = "testUser0";
+ String subscriptionString = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ long since = 0;
+ // Überprüfen, ob Anzahl der Subscriptions des Users mit der definierten Anzahl
+ // übereinstimmt
+ SubscriptionDelta subscriptionDelta = subscriptionService.getSubscriptionDelta(username, since);
+ assertEquals(subscriptionDelta.getAdd().size(), numberOfSubscriptionsPerUser);
+ assertEquals(0, subscriptionDelta.getRemove().size());
+ // Subscription mithilfe des Subscription-Deltas entfernen
+ subscriptionService.applySubscriptionDelta(username,
+ new SubscriptionDelta(List.of(), List.of(subscriptionString)));
+ subscriptionDelta = subscriptionService.getSubscriptionDelta(username, since);
+ assertEquals(subscriptionDelta.getAdd().size(), numberOfSubscriptionsPerUser - 1);
+ assertEquals(1, subscriptionDelta.getRemove().size());
+ // Subscription mithilfe des Subscription-Deltas wieder hinzufügen
+ subscriptionService.applySubscriptionDelta(username,
+ new SubscriptionDelta(List.of(subscriptionString), List.of()));
+
+ subscriptionDelta = subscriptionService.getSubscriptionDelta(username, since);
+ List<SubscriptionAction> subscriptionActions = subscriptionActionDao
+ .findByUserUsernameAndTimestampGreaterThanEqual(username, since);
+ LocalDateTime deltaTime = LocalDateTime
+ .ofEpochSecond(subscriptionDelta.getTimestamp(), 0, ZoneOffset.UTC);
+ LocalDateTime maxTime = deltaTime;
+ for (SubscriptionAction subscriptionAction : subscriptionActions) {
+
+ LocalDateTime subTime = LocalDateTime
+ .ofEpochSecond(subscriptionAction.getTimestamp(), 0, ZoneOffset.UTC);
+ if (maxTime.isBefore(subTime)) {
+ maxTime = subTime;
+ }
+ }
+ assertEquals(deltaTime, maxTime);
+ assertEquals(subscriptionDelta.getAdd().size(), numberOfSubscriptionsPerUser);
+ assertEquals(0, subscriptionDelta.getRemove().size());
+ }
+
+ @Test
+ public void getTitlesTest() {
+ String username = "testUser0";
+ List<SubscriptionTitles> subscriptionTitlesList = subscriptionService.getTitles(username);
+ assertEquals(numberOfSubscriptionsPerUser, subscriptionTitlesList.size());
+ for (SubscriptionTitles subscriptionTitles : subscriptionTitlesList) {
+ assertEquals(numberOfEpisodesPerSubscription, subscriptionTitles.episodes().size());
+ Optional<Subscription> savedSubscription =
+ subscriptionDao.findByUrl(subscriptionTitles.subscription().getUrl());
+ assertTrue(savedSubscription.isPresent());
+ }
+ }
+}
diff --git a/pse-server/src/test/java/org/psesquared/server/util/RssParserTests.java b/pse-server/src/test/java/org/psesquared/server/util/RssParserTests.java
new file mode 100644
index 0000000..73c5497
--- /dev/null
+++ b/pse-server/src/test/java/org/psesquared/server/util/RssParserTests.java
@@ -0,0 +1,135 @@
+package org.psesquared.server.util;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+import org.psesquared.server.BaseTest;
+import org.psesquared.server.TestAsyncConfig;
+import org.psesquared.server.model.Episode;
+import org.psesquared.server.model.Subscription;
+import org.springframework.test.context.ContextConfiguration;
+
+// Disable async behavior for this Test Class
+@ContextConfiguration(classes = TestAsyncConfig.class)
+public class RssParserTests extends BaseTest {
+
+ private static final String TAGESSCHAU_URL = "https://www.tagesschau.de/multimedia/podcasts/mal-angenommen-feed-101.xml";
+ private final Subscription tagesschauPodcast = Subscription.builder()
+ .url(TAGESSCHAU_URL)
+ .build();
+ private final String relativePathToTestFeeds = "./testfeeds/";
+
+ @Test
+ public void testSubscriptionInvalid() {
+ Subscription nullSubscription = null;
+ Subscription nullUrlSubscription = Subscription.builder().build();
+ Subscription invalidUrlSubscription = Subscription.builder()
+ .url("Not a url")
+ .title("Some inventive Title")
+ .build();
+
+ assertDoesNotThrow(() -> rssParser.validate(nullSubscription));
+ assertDoesNotThrow(() -> rssParser.validate(nullUrlSubscription));
+ assertDoesNotThrow(() -> rssParser.validate(invalidUrlSubscription));
+ assertFalse(subscriptionDao.findByUrl(invalidUrlSubscription.getUrl()).isPresent());
+ }
+
+ // Does currently only work in debugger by making sure the Parser has time to
+ // validate, before the Test evaluates the Assertions.
+ @Test
+ public void testValidSubscription() {
+ final String expectedURL = "https://media.tagesschau.de/audio/2023/0125/AU-20230125-1854-5200.hi.mp3";
+ final String expectedGuid = "tagesschau-podcast-mal-angenommen-tierrechte-101";
+ final String expectedTitle = "Gleiche Rechte für Tiere? Was dann?";
+ final int expectedTotal = 1442;
+ Episode expectedEpisode = Episode.builder().url(expectedURL).guid(expectedGuid).title(expectedTitle)
+ .total(expectedTotal).subscription(tagesschauPodcast).build();
+ Episode testEpisode = Episode.builder().url(expectedURL).id(expectedEpisode.getId()).build();
+ tagesschauPodcast.addEpisode(testEpisode);
+
+ assertDoesNotThrow(() -> rssParser.validate(tagesschauPodcast));
+ assertTrue(subscriptionDao.findByUrl(TAGESSCHAU_URL).isPresent());
+ }
+
+ @Test
+ public void currentDirTest() {
+ //System.out.println(System.getProperty("user.dir"));
+ final String pathToTestFile = relativePathToTestFeeds + "dirtest.txt";
+ File testFile = new File(pathToTestFile);
+ assertTrue(testFile.exists() && !testFile.isDirectory());
+ }
+
+ public void testByteHamsterEdgeCasePodcast() {
+ final String subscriptionURL = "https://tools.bytehamster.com/podcast/rss.xml";
+ final String firstEpisodeURL = "http://tools.bytehamster.com/podcast/piano.mp3?1.mp3";
+ final String lastEpisodeURL = "http://tools.bytehamster.com/podcast/piano.mp3?13.mp3";
+
+ Subscription subscription = Subscription.builder().url(subscriptionURL).build();
+ Episode firstEpisode = Episode.builder().url(firstEpisodeURL)
+ .subscription(subscription).build();
+ subscription.addEpisode(firstEpisode);
+
+ Episode lastEpisode = Episode.builder().url(lastEpisodeURL)
+ .subscription(subscription).build();
+ subscription.addEpisode(lastEpisode);
+ subscriptionDao.save(subscription);
+ episodeDao.save(firstEpisode);
+ episodeDao.save(lastEpisode);
+
+ // Feed contains an Episode that does not meet minimum requirements, so
+ // Subscription and its Episodes should be deleted
+ assertDoesNotThrow(() -> rssParser.validate(subscription));
+ assertFalse(subscriptionDao.findByUrl(subscriptionURL).isPresent());
+ assertFalse(episodeDao.findByUrl(firstEpisodeURL).isPresent());
+ assertFalse(episodeDao.findByUrl(lastEpisodeURL).isPresent());
+ }
+
+ @Test
+ public void testDeletionOfEpisodeNotInFeed() {
+ final String subscriptionURL = "https://tools.bytehamster.com/podcast/alwaysNew.php";
+ final String notIncludedURL = "http://tools.bytehamster.com/podcast/piano.mp3?2023-03-15-21:53:21.mp3";
+
+ Subscription subscription = Subscription.builder().url(subscriptionURL).build();
+ Episode episodeToDelete = Episode.builder().url(notIncludedURL)
+ .subscription(subscription).build();
+ subscription.addEpisode(episodeToDelete);
+ subscriptionDao.save(subscription);
+ episodeDao.save(episodeToDelete);
+
+ // Feed does not contain the Episode, so the Episode should be deleted, but the
+ // Subscription remains
+ assertDoesNotThrow(() -> rssParser.validate(subscription));
+ assertTrue(subscriptionDao.findByUrl(subscriptionURL).isPresent());
+ assertFalse(episodeDao.findByUrl(notIncludedURL).isPresent());
+ }
+
+ @Test
+ public void testValidateBaseTestFeed() {
+ final String testPodcastUrl = new File("testfeeds/testPodcast0.xml").toURI().toString();
+ Subscription subscription = subscriptionDao.findByUrl(testPodcastUrl).orElseThrow();
+ assertDoesNotThrow(() -> rssParser.validate(subscription));
+ assertNotNull(subscriptionDao.findByUrl(testPodcastUrl));
+ }
+
+ @Test
+ public void testTimeParsing() {
+ final String subscriptionUrl = new File("testfeeds/timeTestPodcast.xml").toURI().toString();
+ Subscription subscription = subscriptionDao.save(Subscription.builder().url(subscriptionUrl).build());
+ assertDoesNotThrow(() -> rssParser.validate(subscription));
+ assertFalse(subscriptionDao.findByUrl(subscriptionUrl).isPresent());
+ }
+
+ @Test
+ public void doubleEnclosureTest() {
+ final String subscriptionUrl = new File("testfeeds/multipleEnclosuresFeed.xml").toURI().toString();
+ Subscription subscription = subscriptionDao.save(Subscription.builder().url(subscriptionUrl).build());
+ assertDoesNotThrow(() -> rssParser.validate(subscription));
+ assertFalse(subscriptionDao.findByUrl(subscriptionUrl).isPresent());
+ }
+
+}