From 5b8851b6c268d0e93c158908fbfae9f8473db5ff Mon Sep 17 00:00:00 2001 From: Orangerot Date: Wed, 19 Jun 2024 00:14:49 +0200 Subject: Initial commit --- .../test/java/org/psesquared/server/BaseTest.java | 163 +++++++++ .../psesquared/server/ServerApplicationTests.java | 11 + .../org/psesquared/server/TestAsyncConfig.java | 16 + .../api/data/access/AuthenticationDaoTest.java | 51 +++ .../api/service/AuthenticationServiceTest.java | 222 +++++++++++++ .../api/service/EmailServiceTests.java | 36 ++ .../api/data/access/EpisodeActionDaoTests.java | 20 ++ .../actions/api/data/access/EpisodeDaoTests.java | 29 ++ .../api/service/EpisodeActionServiceTests.java | 368 +++++++++++++++++++++ .../data/access/SubscriptionActionDaoTests.java | 89 +++++ .../api/data/access/SubscriptionDaoTests.java | 36 ++ .../api/service/SubscriptionServiceTests.java | 128 +++++++ .../org/psesquared/server/util/RssParserTests.java | 135 ++++++++ 13 files changed, 1304 insertions(+) create mode 100644 pse-server/src/test/java/org/psesquared/server/BaseTest.java create mode 100644 pse-server/src/test/java/org/psesquared/server/ServerApplicationTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/TestAsyncConfig.java create mode 100644 pse-server/src/test/java/org/psesquared/server/authentication/api/data/access/AuthenticationDaoTest.java create mode 100644 pse-server/src/test/java/org/psesquared/server/authentication/api/service/AuthenticationServiceTest.java create mode 100644 pse-server/src/test/java/org/psesquared/server/authentication/api/service/EmailServiceTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeActionDaoTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/episode/actions/api/data/access/EpisodeDaoTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/episode/actions/api/service/EpisodeActionServiceTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionActionDaoTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/subscriptions/api/data/access/SubscriptionDaoTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/subscriptions/api/service/SubscriptionServiceTests.java create mode 100644 pse-server/src/test/java/org/psesquared/server/util/RssParserTests.java (limited to 'pse-server/src/test/java/org/psesquared') 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 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 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 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 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 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 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 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 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 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 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 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 savedEpisodeAction = episodeActionDao.findById(episodeAction.getId()); + assertTrue(savedEpisodeAction.isPresent()); + + EpisodeAction oldEpisodeActionTest = episodeActionDao + .findByUserAndEpisodeUrlAndAction(user, episodeUrl, Action.PLAY).orElseThrow(); + List 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 savedSubscription = subscriptionDao.findByUrl(podcastName); + assertTrue(savedSubscription.isPresent()); + + final String episodeUrl = episodeBaseUrl + episodeIndex + ".mp3"; + + Optional noEpisode = episodeDao.findByUrl(episodeUrl); + assertFalse(noEpisode.isPresent()); + + List 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 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 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 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 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 subscriptionActions = subscriptionActionDao + .findByUserUsernameAndTimestampGreaterThanEqual(username, since); + assertEquals(numberOfSubscriptionsPerUser - factor, subscriptionActions.size()); + } + + @Test + public void findByUserUsernameAndAddedTrueTest() { + String username = "testUser0"; + List 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 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 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 subscriptionStrings = List.of(newSubscriptionUrl1, newSubscriptionUrl2); + + subscriptionService.uploadSubscriptions(username, subscriptionStrings); + List 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 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 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 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 subscriptionTitlesList = subscriptionService.getTitles(username); + assertEquals(numberOfSubscriptionsPerUser, subscriptionTitlesList.size()); + for (SubscriptionTitles subscriptionTitles : subscriptionTitlesList) { + assertEquals(numberOfEpisodesPerSubscription, subscriptionTitles.episodes().size()); + Optional 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()); + } + +} -- cgit v1.2.3