Skip to content

Commit ec07d8c

Browse files
authored
Add unit tests (#9)
* Improve unit testing for mapping logic * Add unit tests for UUID helper
1 parent 72777af commit ec07d8c

File tree

6 files changed

+194
-15
lines changed

6 files changed

+194
-15
lines changed

src/main/java/org/openpodcastapi/opa/helpers/UUIDHelper.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,22 @@ private UUIDHelper() {
2222
/// @param feedUrl the URL to sanitize
2323
/// @return the sanitized URL
2424
public static String sanitizeFeedUrl(String feedUrl) {
25-
return feedUrl.replaceFirst("^[a-zA-Z]+://", "").replaceAll("/+$", "");
25+
if (feedUrl == null || feedUrl.isBlank()) {
26+
throw new IllegalArgumentException("Invalid feed URL passed to function");
27+
}
28+
29+
// Reject unsupported schemes (e.g., ftp://)
30+
if (feedUrl.matches("^[a-zA-Z]+://.*") && !feedUrl.startsWith("http://") && !feedUrl.startsWith("https://")) {
31+
throw new IllegalArgumentException("Invalid feed URL passed to function");
32+
}
33+
34+
String sanitized = feedUrl.replaceFirst("^(https?://)", "").replaceAll("/+$", "");
35+
36+
if (!sanitized.contains(".")) {
37+
throw new IllegalArgumentException("Invalid feed URL passed to function");
38+
}
39+
40+
return sanitized;
2641
}
2742

2843
/// Calculates the UUID of a provided feed URL using Podcast index methodology.
@@ -34,8 +49,7 @@ public static String sanitizeFeedUrl(String feedUrl) {
3449
/// @return the calculated UUID
3550
public static UUID getFeedUUID(String feedUrl) {
3651
final String sanitizedFeedUrl = sanitizeFeedUrl(feedUrl);
37-
final UUID feedUUID = UUID.fromString(sanitizedFeedUrl);
38-
return generator.generate(feedUUID.toString());
52+
return generator.generate(sanitizedFeedUrl);
3953
}
4054

4155
/// Validates that a supplied subscription UUID has been calculated properly

src/main/java/org/openpodcastapi/opa/subscription/model/Subscription.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
package org.openpodcastapi.opa.subscription.model;
22

33
import jakarta.persistence.*;
4-
import lombok.Generated;
5-
import lombok.Getter;
6-
import lombok.NoArgsConstructor;
7-
import lombok.Setter;
4+
import lombok.*;
85

96
import java.time.Instant;
107
import java.util.Set;
118
import java.util.UUID;
129

1310
@Entity
14-
@Table(name = "subscriptions")
1511
@NoArgsConstructor
12+
@AllArgsConstructor
13+
@Builder
14+
@Table(name = "subscriptions")
1615
public class Subscription {
1716
@Id
1817
@GeneratedValue(strategy = GenerationType.IDENTITY)

src/main/java/org/openpodcastapi/opa/subscription/model/UserSubscription.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
package org.openpodcastapi.opa.subscription.model;
22

33
import jakarta.persistence.*;
4-
import lombok.Generated;
5-
import lombok.Getter;
6-
import lombok.NoArgsConstructor;
7-
import lombok.Setter;
4+
import lombok.*;
85
import org.openpodcastapi.opa.user.model.User;
96

107
import java.time.Instant;
118
import java.util.UUID;
129

1310
@Entity
1411
@NoArgsConstructor
12+
@AllArgsConstructor
13+
@Builder
1514
@Table(name = "user_subscription")
1615
public class UserSubscription {
1716
@Id
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.openpodcastapi.opa.helpers;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.UUID;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
import static org.openpodcastapi.opa.helpers.UUIDHelper.*;
9+
10+
class UUIDHelperTest {
11+
@Test
12+
void sanitizeFeedUrl_shouldSanitizeValidUrl() {
13+
final String feedUrl = "https://test.com/feed1/";
14+
final String expectedUrl = "test.com/feed1";
15+
String cleanedUrl = sanitizeFeedUrl(feedUrl);
16+
17+
assertEquals(expectedUrl, cleanedUrl);
18+
}
19+
20+
@Test
21+
void sanitizeFeedUrl_shouldSanitizeUrlWithoutScheme() {
22+
final String feedUrl = "test.com/feed1";
23+
final String expectedUrl = "test.com/feed1";
24+
String cleanedUrl = sanitizeFeedUrl(feedUrl);
25+
26+
assertEquals(expectedUrl, cleanedUrl);
27+
}
28+
29+
@Test
30+
void sanitizeFeedUrl_shouldThrowOnInvalidUrl() {
31+
final String feedUrl = "ftp://test.com/feed1";
32+
final String expectedMessage = "Invalid feed URL passed to function";
33+
34+
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> sanitizeFeedUrl(feedUrl));
35+
36+
assertTrue(exception.getMessage().contains(expectedMessage));
37+
}
38+
39+
@Test
40+
void getFeedUUID_shouldReturnGeneratedUUID() {
41+
final String feedUrl = "podnews.net/rss";
42+
final UUID expectedUUID = UUID.fromString("9b024349-ccf0-5f69-a609-6b82873eab3c");
43+
44+
UUID calculatedUUID = getFeedUUID(feedUrl);
45+
46+
assertEquals(expectedUUID, calculatedUUID);
47+
}
48+
49+
@Test
50+
void getFeedUUID_shouldReturnDeterministicUUID() {
51+
final String feedUrl = "podnews.net/rss";
52+
final UUID incorrectUUID = UUID.fromString("d5d5520d-81da-474e-928b-5fa66233a1ac");
53+
54+
UUID calculatedUUID = getFeedUUID(feedUrl);
55+
56+
assertNotEquals(incorrectUUID, calculatedUUID);
57+
}
58+
59+
@Test
60+
void validateSubscriptionUUID_shouldReturnTrueWhenValid() {
61+
final String feedUrl = "podnews.net/rss";
62+
final UUID expectedUUID = UUID.fromString("9b024349-ccf0-5f69-a609-6b82873eab3c");
63+
64+
assertTrue(validateSubscriptionUUID(feedUrl, expectedUUID));
65+
}
66+
67+
@Test
68+
void validateSubscriptionUUID_shouldReturnFalseWhenInvalid() {
69+
final String feedUrl = "podnews.net/rss";
70+
final UUID incorrectUUID = UUID.fromString("d5d5520d-81da-474e-928b-5fa66233a1ac");
71+
72+
assertFalse(validateSubscriptionUUID(feedUrl, incorrectUUID));
73+
}
74+
75+
@Test
76+
void validateUUIDString_shouldReturnTrueWhenValid() {
77+
final String validUUID = "d5d5520d-81da-474e-928b-5fa66233a1ac";
78+
79+
assertTrue(validateUUIDString(validUUID));
80+
}
81+
82+
@Test
83+
void validateUUIDString_shouldReturnFalseWhenInvalid() {
84+
final String validUUID = "not-a-uuid";
85+
86+
assertFalse(validateUUIDString(validUUID));
87+
}
88+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.openpodcastapi.opa.subscriptions;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.ExtendWith;
5+
import org.openpodcastapi.opa.subscription.dto.UserSubscriptionDto;
6+
import org.openpodcastapi.opa.subscription.mapper.UserSubscriptionMapper;
7+
import org.openpodcastapi.opa.subscription.mapper.UserSubscriptionMapperImpl;
8+
import org.openpodcastapi.opa.subscription.model.Subscription;
9+
import org.openpodcastapi.opa.subscription.model.UserSubscription;
10+
import org.openpodcastapi.opa.subscription.repository.UserSubscriptionRepository;
11+
import org.openpodcastapi.opa.user.model.User;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.test.context.ContextConfiguration;
14+
import org.springframework.test.context.bean.override.mockito.MockitoBean;
15+
import org.springframework.test.context.junit.jupiter.SpringExtension;
16+
17+
import java.time.Instant;
18+
import java.util.UUID;
19+
20+
import static org.junit.jupiter.api.Assertions.*;
21+
22+
@ExtendWith(SpringExtension.class)
23+
@ContextConfiguration(classes = UserSubscriptionMapperImpl.class)
24+
class UserSubscriptionMapperTest {
25+
@Autowired
26+
private UserSubscriptionMapper mapper;
27+
28+
@MockitoBean
29+
private UserSubscriptionRepository userSubscriptionRepository;
30+
31+
/// Tests that a [UserSubscription] entity maps to a [UserSubscriptionDto] representation
32+
@Test
33+
void testToDto() {
34+
final Instant timestamp = Instant.now();
35+
final UUID uuid = UUID.randomUUID();
36+
User user = User.builder()
37+
.uuid(UUID.randomUUID())
38+
.username("test")
39+
.email("test@test.test")
40+
.createdAt(timestamp)
41+
.updatedAt(timestamp)
42+
.build();
43+
44+
Subscription subscription = Subscription.builder()
45+
.uuid(UUID.randomUUID())
46+
.feedUrl("test.com/feed1")
47+
.createdAt(timestamp)
48+
.updatedAt(timestamp)
49+
.build();
50+
51+
UserSubscription userSubscription = UserSubscription.builder()
52+
.uuid(uuid)
53+
.user(user)
54+
.subscription(subscription)
55+
.isSubscribed(true)
56+
.createdAt(timestamp)
57+
.updatedAt(timestamp)
58+
.build();
59+
60+
UserSubscriptionDto dto = mapper.toDto(userSubscription);
61+
assertNotNull(dto);
62+
63+
// The DTO should inherit the feed URL from the Subscription
64+
assertEquals(subscription.getFeedUrl(), dto.feedUrl());
65+
66+
// The DTO should use the Subscription's UUID rather than the UserSubscription's
67+
assertEquals(subscription.getUuid(), dto.uuid());
68+
assertTrue(dto.isSubscribed());
69+
}
70+
}

src/test/java/org/openpodcastapi/opa/user/UserMapperSpringTest.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
package org.openpodcastapi.opa.user;
22

33
import org.junit.jupiter.api.Test;
4-
import org.mapstruct.factory.Mappers;
4+
import org.junit.jupiter.api.extension.ExtendWith;
55
import org.openpodcastapi.opa.user.dto.CreateUserDto;
6+
import org.openpodcastapi.opa.user.dto.UserDto;
67
import org.openpodcastapi.opa.user.mapper.UserMapper;
8+
import org.openpodcastapi.opa.user.mapper.UserMapperImpl;
79
import org.openpodcastapi.opa.user.model.User;
810
import org.openpodcastapi.opa.user.repository.UserRepository;
9-
import org.openpodcastapi.opa.user.dto.UserDto;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.test.context.ContextConfiguration;
1013
import org.springframework.test.context.bean.override.mockito.MockitoBean;
14+
import org.springframework.test.context.junit.jupiter.SpringExtension;
1115

1216
import java.time.Instant;
1317
import java.util.UUID;
1418

1519
import static org.junit.jupiter.api.Assertions.*;
1620

21+
@ExtendWith(SpringExtension.class)
22+
@ContextConfiguration(classes = UserMapperImpl.class)
1723
class UserMapperSpringTest {
18-
private final UserMapper mapper = Mappers.getMapper(UserMapper.class);
24+
@Autowired
25+
private UserMapper mapper;
1926

2027
@MockitoBean
2128
private UserRepository userRepository;
2229

30+
/// Tests that a [User] entity maps to a [UserDto] representation
2331
@Test
2432
void testToDto() {
2533
final Instant timestamp = Instant.now();
@@ -41,6 +49,7 @@ void testToDto() {
4149
assertEquals(user.getUpdatedAt(), dto.updatedAt());
4250
}
4351

52+
/// Tests that a [CreateUserDto] maps to a [User] entity
4453
@Test
4554
void testToEntity() {
4655
CreateUserDto dto = new CreateUserDto("test", "testPassword", "test@test.test");

0 commit comments

Comments
 (0)