diff options
| author | Orangerot <purple@orangerot.dev> | 2024-06-19 00:14:49 +0200 | 
|---|---|---|
| committer | Orangerot <purple@orangerot.dev> | 2024-06-27 12:11:14 +0200 | 
| commit | 5b8851b6c268d0e93c158908fbfae9f8473db5ff (patch) | |
| tree | 7010eb85d86fa2da06ea4ffbcdb01a685d502ae8 /pse-server/src/test/java | |
Diffstat (limited to 'pse-server/src/test/java')
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()); +    } + +}  | 
