From 1182c83642c1962a1233bd449343515bdc2a1942 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 12:50:14 +0000 Subject: [PATCH] Add comprehensive unit tests to reach 80% coverage threshold - Add GraphQL datafetcher tests: ArticleDatafetcher, CommentDatafetcher, MeDatafetcher, ProfileDatafetcher, TagDatafetcher - Add GraphQL mutation tests: ArticleMutation, CommentMutation, UserMutation, RelationMutation - Add SecurityUtil test - Add GraphQLCustomizeExceptionHandler test - Add ArticleCommandService test - Add CursorPager and pagination utility tests - Add data class tests (ArticleData, CommentData, ProfileData, etc.) - Add generated DGS types test (builder/setter/equals coverage) - Add CoreDomain test (Article, User, Comment, Tag, AuthorizationService) Coverage improvement: 33% -> 87% (instructions), 34% -> 89% (lines) All tests pass, no production code modified. --- .../spring/application/CursorPagerTest.java | 138 ++++ .../article/ArticleCommandServiceTest.java | 73 +++ .../application/data/DataClassTest.java | 232 +++++++ .../java/io/spring/core/CoreDomainTest.java | 188 ++++++ .../graphql/ArticleDatafetcherTest.java | 353 ++++++++++ .../spring/graphql/ArticleMutationTest.java | 222 +++++++ .../graphql/CommentDatafetcherTest.java | 197 ++++++ .../spring/graphql/CommentMutationTest.java | 155 +++++ .../io/spring/graphql/MeDatafetcherTest.java | 89 +++ .../graphql/ProfileDatafetcherTest.java | 143 ++++ .../spring/graphql/RelationMutationTest.java | 121 ++++ .../io/spring/graphql/SecurityUtilTest.java | 54 ++ .../io/spring/graphql/TagDatafetcherTest.java | 26 + .../io/spring/graphql/UserMutationTest.java | 147 +++++ .../GraphQLCustomizeExceptionHandlerTest.java | 137 ++++ .../graphql/types/GraphQLTypesTest.java | 615 ++++++++++++++++++ 16 files changed, 2890 insertions(+) create mode 100644 src/test/java/io/spring/application/CursorPagerTest.java create mode 100644 src/test/java/io/spring/application/article/ArticleCommandServiceTest.java create mode 100644 src/test/java/io/spring/application/data/DataClassTest.java create mode 100644 src/test/java/io/spring/core/CoreDomainTest.java create mode 100644 src/test/java/io/spring/graphql/ArticleDatafetcherTest.java create mode 100644 src/test/java/io/spring/graphql/ArticleMutationTest.java create mode 100644 src/test/java/io/spring/graphql/CommentDatafetcherTest.java create mode 100644 src/test/java/io/spring/graphql/CommentMutationTest.java create mode 100644 src/test/java/io/spring/graphql/MeDatafetcherTest.java create mode 100644 src/test/java/io/spring/graphql/ProfileDatafetcherTest.java create mode 100644 src/test/java/io/spring/graphql/RelationMutationTest.java create mode 100644 src/test/java/io/spring/graphql/SecurityUtilTest.java create mode 100644 src/test/java/io/spring/graphql/TagDatafetcherTest.java create mode 100644 src/test/java/io/spring/graphql/UserMutationTest.java create mode 100644 src/test/java/io/spring/graphql/exception/GraphQLCustomizeExceptionHandlerTest.java create mode 100644 src/test/java/io/spring/graphql/types/GraphQLTypesTest.java diff --git a/src/test/java/io/spring/application/CursorPagerTest.java b/src/test/java/io/spring/application/CursorPagerTest.java new file mode 100644 index 000000000..043a3557d --- /dev/null +++ b/src/test/java/io/spring/application/CursorPagerTest.java @@ -0,0 +1,138 @@ +package io.spring.application; + +import static org.junit.jupiter.api.Assertions.*; + +import io.spring.application.CursorPager.Direction; +import io.spring.application.data.ArticleData; +import io.spring.application.data.ProfileData; +import java.util.Arrays; +import java.util.Collections; +import org.joda.time.DateTime; +import org.junit.jupiter.api.Test; + +public class CursorPagerTest { + + private ArticleData buildArticle(String slug) { + return new ArticleData( + "id", + slug, + "title", + "desc", + "body", + false, + 0, + new DateTime(), + new DateTime(), + null, + new ProfileData("uid", "user", "", "", false)); + } + + @Test + void should_have_next_when_direction_is_next_and_has_extra() { + CursorPager pager = + new CursorPager<>(Arrays.asList(buildArticle("s1")), Direction.NEXT, true); + + assertTrue(pager.hasNext()); + assertFalse(pager.hasPrevious()); + assertEquals(1, pager.getData().size()); + } + + @Test + void should_not_have_next_when_direction_is_next_and_no_extra() { + CursorPager pager = + new CursorPager<>(Arrays.asList(buildArticle("s1")), Direction.NEXT, false); + + assertFalse(pager.hasNext()); + assertFalse(pager.hasPrevious()); + } + + @Test + void should_have_previous_when_direction_is_prev_and_has_extra() { + CursorPager pager = + new CursorPager<>(Arrays.asList(buildArticle("s1")), Direction.PREV, true); + + assertFalse(pager.hasNext()); + assertTrue(pager.hasPrevious()); + } + + @Test + void should_not_have_previous_when_direction_is_prev_and_no_extra() { + CursorPager pager = + new CursorPager<>(Arrays.asList(buildArticle("s1")), Direction.PREV, false); + + assertFalse(pager.hasNext()); + assertFalse(pager.hasPrevious()); + } + + @Test + void should_return_null_cursors_for_empty_data() { + CursorPager pager = + new CursorPager<>(Collections.emptyList(), Direction.NEXT, false); + + assertNull(pager.getStartCursor()); + assertNull(pager.getEndCursor()); + } + + @Test + void should_return_correct_start_and_end_cursors() { + ArticleData a1 = buildArticle("s1"); + ArticleData a2 = buildArticle("s2"); + CursorPager pager = + new CursorPager<>(Arrays.asList(a1, a2), Direction.NEXT, false); + + assertNotNull(pager.getStartCursor()); + assertNotNull(pager.getEndCursor()); + assertEquals(a1.getCursor().toString(), pager.getStartCursor().toString()); + assertEquals(a2.getCursor().toString(), pager.getEndCursor().toString()); + } + + @Test + void should_create_cursor_page_parameter() { + DateTime cursor = new DateTime(); + CursorPageParameter param = new CursorPageParameter<>(cursor, 10, Direction.NEXT); + + assertEquals(cursor, param.getCursor()); + assertEquals(10, param.getLimit()); + assertEquals(11, param.getQueryLimit()); + assertEquals(Direction.NEXT, param.getDirection()); + assertTrue(param.isNext()); + } + + @Test + void should_cap_limit_at_max() { + CursorPageParameter param = new CursorPageParameter<>(null, 2000, Direction.NEXT); + assertEquals(1000, param.getLimit()); + } + + @Test + void should_use_default_limit_for_zero_or_negative() { + CursorPageParameter param = new CursorPageParameter<>(null, 0, Direction.PREV); + assertEquals(20, param.getLimit()); + assertFalse(param.isNext()); + } + + @Test + void should_create_default_cursor_page_parameter() { + CursorPageParameter param = new CursorPageParameter<>(); + assertEquals(20, param.getLimit()); + assertNull(param.getCursor()); + } + + @Test + void should_parse_date_time_cursor() { + DateTime now = new DateTime(); + DateTimeCursor cursor = new DateTimeCursor(now); + + String str = cursor.toString(); + assertNotNull(str); + + DateTime parsed = DateTimeCursor.parse(str); + assertNotNull(parsed); + assertEquals(now.getMillis(), parsed.getMillis()); + } + + @Test + void should_return_null_for_null_cursor_parse() { + assertNull(DateTimeCursor.parse(null)); + } +} diff --git a/src/test/java/io/spring/application/article/ArticleCommandServiceTest.java b/src/test/java/io/spring/application/article/ArticleCommandServiceTest.java new file mode 100644 index 000000000..905258d13 --- /dev/null +++ b/src/test/java/io/spring/application/article/ArticleCommandServiceTest.java @@ -0,0 +1,73 @@ +package io.spring.application.article; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import io.spring.core.article.Article; +import io.spring.core.article.ArticleRepository; +import io.spring.core.user.User; +import java.util.Arrays; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ArticleCommandServiceTest { + + private ArticleRepository articleRepository; + private ArticleCommandService articleCommandService; + private User user; + + @BeforeEach + void setUp() { + articleRepository = mock(ArticleRepository.class); + articleCommandService = new ArticleCommandService(articleRepository); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + } + + @Test + void should_create_article() { + NewArticleParam param = + NewArticleParam.builder() + .title("Test Title") + .description("A description") + .body("Article body") + .tagList(Arrays.asList("java", "spring")) + .build(); + + Article result = articleCommandService.createArticle(param, user); + + assertNotNull(result); + assertEquals("test-title", result.getSlug()); + assertEquals("Test Title", result.getTitle()); + assertEquals("A description", result.getDescription()); + assertEquals("Article body", result.getBody()); + assertEquals(user.getId(), result.getUserId()); + verify(articleRepository).save(result); + } + + @Test + void should_update_article() { + Article article = new Article("Original Title", "desc", "body", Arrays.asList(), user.getId()); + UpdateArticleParam param = new UpdateArticleParam("New Title", "new body", "new desc"); + + Article result = articleCommandService.updateArticle(article, param); + + assertEquals("New Title", result.getTitle()); + assertEquals("new-title", result.getSlug()); + assertEquals("new body", result.getBody()); + assertEquals("new desc", result.getDescription()); + verify(articleRepository).save(article); + } + + @Test + void should_update_article_with_partial_params() { + Article article = new Article("Original Title", "desc", "body", Arrays.asList(), user.getId()); + UpdateArticleParam param = new UpdateArticleParam("New Title", "", ""); + + Article result = articleCommandService.updateArticle(article, param); + + assertEquals("New Title", result.getTitle()); + assertEquals("body", result.getBody()); + assertEquals("desc", result.getDescription()); + verify(articleRepository).save(article); + } +} diff --git a/src/test/java/io/spring/application/data/DataClassTest.java b/src/test/java/io/spring/application/data/DataClassTest.java new file mode 100644 index 000000000..c3886fce7 --- /dev/null +++ b/src/test/java/io/spring/application/data/DataClassTest.java @@ -0,0 +1,232 @@ +package io.spring.application.data; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import org.joda.time.DateTime; +import org.junit.jupiter.api.Test; + +public class DataClassTest { + + @Test + void should_create_article_data_with_all_args() { + DateTime now = new DateTime(); + ProfileData profile = new ProfileData("pid", "user", "bio", "img", true); + ArticleData data = + new ArticleData( + "id1", + "slug", + "title", + "desc", + "body", + true, + 5, + now, + now, + Arrays.asList("java"), + profile); + + assertEquals("id1", data.getId()); + assertEquals("slug", data.getSlug()); + assertEquals("title", data.getTitle()); + assertEquals("desc", data.getDescription()); + assertEquals("body", data.getBody()); + assertTrue(data.isFavorited()); + assertEquals(5, data.getFavoritesCount()); + assertEquals(now, data.getCreatedAt()); + assertEquals(now, data.getUpdatedAt()); + assertEquals(Arrays.asList("java"), data.getTagList()); + assertEquals(profile, data.getProfileData()); + assertNotNull(data.getCursor()); + assertNotNull(data.toString()); + assertNotNull(data.hashCode()); + } + + @Test + void should_create_article_data_with_no_args_and_setters() { + ArticleData data = new ArticleData(); + DateTime now = new DateTime(); + data.setId("id2"); + data.setSlug("slug2"); + data.setTitle("title2"); + data.setDescription("desc2"); + data.setBody("body2"); + data.setFavorited(false); + data.setFavoritesCount(3); + data.setCreatedAt(now); + data.setUpdatedAt(now); + data.setTagList(Arrays.asList("spring")); + data.setProfileData(new ProfileData("pid", "u", "b", "i", false)); + + assertEquals("id2", data.getId()); + assertEquals("slug2", data.getSlug()); + assertEquals("title2", data.getTitle()); + assertEquals("desc2", data.getDescription()); + assertEquals("body2", data.getBody()); + assertFalse(data.isFavorited()); + assertEquals(3, data.getFavoritesCount()); + } + + @Test + void should_test_article_data_equals() { + DateTime now = new DateTime(); + ArticleData data1 = + new ArticleData("id", "slug", "t", "d", "b", false, 0, now, now, null, null); + ArticleData data2 = + new ArticleData("id", "slug", "t", "d", "b", false, 0, now, now, null, null); + ArticleData data3 = + new ArticleData("other", "slug2", "t2", "d2", "b2", true, 1, now, now, null, null); + + assertEquals(data1, data2); + assertNotEquals(data1, data3); + assertEquals(data1, data1); + assertNotEquals(data1, null); + assertNotEquals(data1, "string"); + } + + @Test + void should_create_comment_data() { + DateTime now = new DateTime(); + ProfileData profile = new ProfileData("pid", "user", "bio", "img", false); + CommentData data = new CommentData("c1", "body", "artId", now, now, profile); + + assertEquals("c1", data.getId()); + assertEquals("body", data.getBody()); + assertEquals("artId", data.getArticleId()); + assertEquals(now, data.getCreatedAt()); + assertEquals(now, data.getUpdatedAt()); + assertEquals(profile, data.getProfileData()); + assertNotNull(data.getCursor()); + assertNotNull(data.toString()); + assertNotNull(data.hashCode()); + } + + @Test + void should_create_comment_data_with_setters() { + CommentData data = new CommentData(); + DateTime now = new DateTime(); + data.setId("c2"); + data.setBody("new body"); + data.setArticleId("art2"); + data.setCreatedAt(now); + data.setUpdatedAt(now); + data.setProfileData(null); + + assertEquals("c2", data.getId()); + assertEquals("new body", data.getBody()); + assertEquals("art2", data.getArticleId()); + } + + @Test + void should_test_comment_data_equals() { + DateTime now = new DateTime(); + CommentData data1 = new CommentData("c1", "body", "art", now, now, null); + CommentData data2 = new CommentData("c1", "body", "art", now, now, null); + assertEquals(data1, data2); + } + + @Test + void should_create_profile_data() { + ProfileData data = new ProfileData("id", "user", "bio", "img", true); + + assertEquals("id", data.getId()); + assertEquals("user", data.getUsername()); + assertEquals("bio", data.getBio()); + assertEquals("img", data.getImage()); + assertTrue(data.isFollowing()); + assertNotNull(data.toString()); + assertNotNull(data.hashCode()); + } + + @Test + void should_create_profile_data_with_setters() { + ProfileData data = new ProfileData(); + data.setId("id2"); + data.setUsername("user2"); + data.setBio("bio2"); + data.setImage("img2"); + data.setFollowing(false); + + assertEquals("id2", data.getId()); + assertEquals("user2", data.getUsername()); + assertFalse(data.isFollowing()); + } + + @Test + void should_test_profile_data_equals() { + ProfileData data1 = new ProfileData("id", "user", "bio", "img", true); + ProfileData data2 = new ProfileData("id", "user", "bio", "img", true); + ProfileData data3 = new ProfileData("other", "user2", "bio2", "img2", false); + assertEquals(data1, data2); + assertNotEquals(data1, data3); + } + + @Test + void should_create_user_data() { + UserData data = new UserData("id", "email@test.com", "user", "bio", "img"); + + assertEquals("id", data.getId()); + assertEquals("email@test.com", data.getEmail()); + assertEquals("user", data.getUsername()); + assertEquals("bio", data.getBio()); + assertEquals("img", data.getImage()); + assertNotNull(data.toString()); + assertNotNull(data.hashCode()); + } + + @Test + void should_create_user_data_with_setters() { + UserData data = new UserData(); + data.setId("id2"); + data.setEmail("e2@test.com"); + data.setUsername("u2"); + data.setBio("b2"); + data.setImage("i2"); + assertEquals("id2", data.getId()); + } + + @Test + void should_test_user_data_equals() { + UserData data1 = new UserData("id", "e", "u", "b", "i"); + UserData data2 = new UserData("id", "e", "u", "b", "i"); + assertEquals(data1, data2); + } + + @Test + void should_create_user_with_token() { + UserData userData = new UserData("id", "email@test.com", "user", "bio", "img"); + UserWithToken uwt = new UserWithToken(userData, "mytoken"); + + assertEquals("email@test.com", uwt.getEmail()); + assertEquals("user", uwt.getUsername()); + assertEquals("bio", uwt.getBio()); + assertEquals("img", uwt.getImage()); + assertEquals("mytoken", uwt.getToken()); + } + + @Test + void should_create_article_data_list() { + DateTime now = new DateTime(); + ArticleData article = + new ArticleData("id", "slug", "t", "d", "b", false, 0, now, now, null, null); + List articles = Arrays.asList(article); + ArticleDataList list = new ArticleDataList(articles, 1); + + assertEquals(1, list.getCount()); + assertEquals(articles, list.getArticleDatas()); + } + + @Test + void should_create_article_favorite_count() { + ArticleFavoriteCount afc = new ArticleFavoriteCount("artId", 10); + + assertEquals("artId", afc.getId()); + assertEquals(10, afc.getCount()); + assertNotNull(afc.toString()); + assertNotNull(afc.hashCode()); + + ArticleFavoriteCount afc2 = new ArticleFavoriteCount("artId", 10); + assertEquals(afc, afc2); + } +} diff --git a/src/test/java/io/spring/core/CoreDomainTest.java b/src/test/java/io/spring/core/CoreDomainTest.java new file mode 100644 index 000000000..d02b52452 --- /dev/null +++ b/src/test/java/io/spring/core/CoreDomainTest.java @@ -0,0 +1,188 @@ +package io.spring.core; + +import static org.junit.jupiter.api.Assertions.*; + +import io.spring.Util; +import io.spring.core.article.Article; +import io.spring.core.article.Tag; +import io.spring.core.comment.Comment; +import io.spring.core.favorite.ArticleFavorite; +import io.spring.core.service.AuthorizationService; +import io.spring.core.user.FollowRelation; +import io.spring.core.user.User; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +public class CoreDomainTest { + + @Test + void should_create_user_and_verify_properties() { + User user = new User("test@mail.com", "testuser", "pass123", "mybio", "myimg"); + assertNotNull(user.getId()); + assertEquals("test@mail.com", user.getEmail()); + assertEquals("testuser", user.getUsername()); + assertEquals("pass123", user.getPassword()); + assertEquals("mybio", user.getBio()); + assertEquals("myimg", user.getImage()); + } + + @Test + void should_update_user_selectively() { + User user = new User("old@mail.com", "olduser", "oldpass", "oldbio", "oldimg"); + user.update("new@mail.com", "", "", "newbio", ""); + assertEquals("new@mail.com", user.getEmail()); + assertEquals("olduser", user.getUsername()); + assertEquals("oldpass", user.getPassword()); + assertEquals("newbio", user.getBio()); + assertEquals("oldimg", user.getImage()); + } + + @Test + void should_update_user_all_fields() { + User user = new User("a@b.com", "u", "p", "b", "i"); + user.update("x@y.com", "nu", "np", "nb", "ni"); + assertEquals("x@y.com", user.getEmail()); + assertEquals("nu", user.getUsername()); + assertEquals("np", user.getPassword()); + assertEquals("nb", user.getBio()); + assertEquals("ni", user.getImage()); + } + + @Test + void should_test_user_equals_and_hashcode() { + User u1 = new User("a@b.com", "u", "p", "b", "i"); + User u2 = new User("a@b.com", "u", "p", "b", "i"); + assertEquals(u1, u1); + assertNotEquals(u1, u2); + assertNotEquals(u1, null); + assertNotEquals(u1, "string"); + assertNotEquals(u1.hashCode(), u2.hashCode()); + } + + @Test + void should_create_article_with_slug() { + Article article = new Article("Hello World", "desc", "body", Arrays.asList("tag1"), "userId"); + assertEquals("hello-world", article.getSlug()); + assertEquals("Hello World", article.getTitle()); + assertEquals("desc", article.getDescription()); + assertEquals("body", article.getBody()); + assertEquals("userId", article.getUserId()); + assertNotNull(article.getId()); + assertNotNull(article.getCreatedAt()); + assertNotNull(article.getUpdatedAt()); + assertEquals(1, article.getTags().size()); + } + + @Test + void should_update_article_fields() { + Article article = new Article("Original", "desc", "body", Arrays.asList(), "userId"); + article.update("Updated Title", "new desc", "new body"); + assertEquals("updated-title", article.getSlug()); + assertEquals("Updated Title", article.getTitle()); + assertEquals("new desc", article.getDescription()); + assertEquals("new body", article.getBody()); + } + + @Test + void should_not_update_article_with_empty_fields() { + Article article = new Article("Original", "desc", "body", Arrays.asList(), "userId"); + article.update("", "", ""); + assertEquals("original", article.getSlug()); + assertEquals("Original", article.getTitle()); + assertEquals("desc", article.getDescription()); + assertEquals("body", article.getBody()); + } + + @Test + void should_generate_article_slug_correctly() { + assertEquals("hello-world", Article.toSlug("Hello World")); + assertEquals("test-article", Article.toSlug("Test Article")); + assertEquals("special-chars", Article.toSlug("Special & Chars")); + } + + @Test + void should_test_article_equals_and_hashcode() { + Article a1 = new Article("T", "d", "b", Arrays.asList(), "u"); + assertEquals(a1, a1); + assertNotEquals(a1, null); + assertNotEquals(a1, "string"); + } + + @Test + void should_create_tag() { + Tag tag = new Tag("java"); + assertEquals("java", tag.getName()); + assertNotNull(tag.getId()); + } + + @Test + void should_create_comment() { + Comment comment = new Comment("body text", "userId", "articleId"); + assertNotNull(comment.getId()); + assertEquals("body text", comment.getBody()); + assertEquals("userId", comment.getUserId()); + assertEquals("articleId", comment.getArticleId()); + assertNotNull(comment.getCreatedAt()); + } + + @Test + void should_test_comment_equals() { + Comment c1 = new Comment("body", "uid", "aid"); + assertEquals(c1, c1); + assertNotEquals(c1, null); + } + + @Test + void should_create_article_favorite() { + ArticleFavorite fav = new ArticleFavorite("artId", "userId"); + assertEquals("artId", fav.getArticleId()); + assertEquals("userId", fav.getUserId()); + } + + @Test + void should_test_article_favorite_equals() { + ArticleFavorite f1 = new ArticleFavorite("a", "u"); + ArticleFavorite f2 = new ArticleFavorite("a", "u"); + assertEquals(f1, f2); + assertEquals(f1.hashCode(), f2.hashCode()); + } + + @Test + void should_create_follow_relation() { + FollowRelation relation = new FollowRelation("userId", "targetId"); + assertEquals("userId", relation.getUserId()); + assertEquals("targetId", relation.getTargetId()); + } + + @Test + void should_test_authorization_service_can_write_article() { + User user = new User("e@t.com", "u", "p", "", ""); + Article article = new Article("T", "d", "b", Arrays.asList(), user.getId()); + assertTrue(AuthorizationService.canWriteArticle(user, article)); + + User otherUser = new User("o@t.com", "o", "p", "", ""); + assertFalse(AuthorizationService.canWriteArticle(otherUser, article)); + } + + @Test + void should_test_authorization_service_can_write_comment() { + User articleOwner = new User("e@t.com", "owner", "p", "", ""); + User commenter = new User("c@t.com", "commenter", "p", "", ""); + User stranger = new User("s@t.com", "stranger", "p", "", ""); + + Article article = new Article("T", "d", "b", Arrays.asList(), articleOwner.getId()); + Comment comment = new Comment("text", commenter.getId(), article.getId()); + + assertTrue(AuthorizationService.canWriteComment(articleOwner, article, comment)); + assertTrue(AuthorizationService.canWriteComment(commenter, article, comment)); + assertFalse(AuthorizationService.canWriteComment(stranger, article, comment)); + } + + @Test + void should_test_util_is_empty() { + assertTrue(Util.isEmpty(null)); + assertTrue(Util.isEmpty("")); + assertFalse(Util.isEmpty("hello")); + assertFalse(Util.isEmpty(" ")); + } +} diff --git a/src/test/java/io/spring/graphql/ArticleDatafetcherTest.java b/src/test/java/io/spring/graphql/ArticleDatafetcherTest.java new file mode 100644 index 000000000..d0304646b --- /dev/null +++ b/src/test/java/io/spring/graphql/ArticleDatafetcherTest.java @@ -0,0 +1,353 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import com.netflix.graphql.dgs.DgsDataFetchingEnvironment; +import graphql.execution.DataFetcherResult; +import graphql.schema.DataFetchingEnvironment; +import io.spring.application.ArticleQueryService; +import io.spring.application.CursorPager; +import io.spring.application.CursorPager.Direction; +import io.spring.application.data.ArticleData; +import io.spring.application.data.CommentData; +import io.spring.application.data.ProfileData; +import io.spring.core.user.User; +import io.spring.core.user.UserRepository; +import io.spring.graphql.types.Article; +import io.spring.graphql.types.ArticlesConnection; +import io.spring.graphql.types.Profile; +import java.util.*; +import org.joda.time.DateTime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; + +public class ArticleDatafetcherTest { + + private ArticleQueryService articleQueryService; + private UserRepository userRepository; + private ArticleDatafetcher articleDatafetcher; + private User user; + + @BeforeEach + void setUp() { + articleQueryService = mock(ArticleQueryService.class); + userRepository = mock(UserRepository.class); + articleDatafetcher = new ArticleDatafetcher(articleQueryService, userRepository); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + private ArticleData buildArticleData(String slug) { + return new ArticleData( + "id1", + slug, + "Title", + "description", + "body", + false, + 0, + new DateTime(), + new DateTime(), + Arrays.asList("java"), + new ProfileData("authorId", "author", "bio", "img", false)); + } + + @Test + void should_get_feed_with_first_parameter() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + ArticleData articleData = buildArticleData("test-slug"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.NEXT, false); + when(articleQueryService.findUserFeedWithCursor(any(), any())).thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.getFeed(10, null, null, null, dfe); + + assertNotNull(result); + assertNotNull(result.getData()); + assertEquals(1, result.getData().getEdges().size()); + assertEquals("test-slug", result.getData().getEdges().get(0).getNode().getSlug()); + } + + @Test + void should_get_feed_with_last_parameter() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + ArticleData articleData = buildArticleData("test-slug"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.PREV, false); + when(articleQueryService.findUserFeedWithCursor(any(), any())).thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.getFeed(null, null, 5, null, dfe); + + assertNotNull(result); + assertEquals(1, result.getData().getEdges().size()); + } + + @Test + void should_throw_when_first_and_last_both_null_in_feed() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + assertThrows( + IllegalArgumentException.class, + () -> articleDatafetcher.getFeed(null, null, null, null, dfe)); + } + + @Test + void should_get_user_feed() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("testuser").build(); + when(dfe.getSource()).thenReturn(profile); + when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(user)); + + ArticleData articleData = buildArticleData("user-article"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.NEXT, true); + when(articleQueryService.findUserFeedWithCursor(any(), any())).thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.userFeed(10, null, null, null, dfe); + + assertNotNull(result); + assertTrue(result.getData().getPageInfo().isHasNextPage()); + } + + @Test + void should_get_user_feed_with_last_param() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("testuser").build(); + when(dfe.getSource()).thenReturn(profile); + when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(user)); + + ArticleData articleData = buildArticleData("user-article"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.PREV, true); + when(articleQueryService.findUserFeedWithCursor(any(), any())).thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.userFeed(null, null, 5, null, dfe); + + assertNotNull(result); + assertTrue(result.getData().getPageInfo().isHasPreviousPage()); + } + + @Test + void should_throw_when_first_and_last_both_null_in_user_feed() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("testuser").build(); + when(dfe.getSource()).thenReturn(profile); + when(userRepository.findByUsername("testuser")).thenReturn(Optional.of(user)); + + assertThrows( + IllegalArgumentException.class, + () -> articleDatafetcher.userFeed(null, null, null, null, dfe)); + } + + @Test + void should_get_user_favorites_with_first() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("testuser").build(); + when(dfe.getSource()).thenReturn(profile); + + ArticleData articleData = buildArticleData("fav-article"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.NEXT, false); + when(articleQueryService.findRecentArticlesWithCursor(any(), any(), any(), any(), any())) + .thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.userFavorites(10, null, null, null, dfe); + + assertNotNull(result); + assertEquals(1, result.getData().getEdges().size()); + } + + @Test + void should_get_user_favorites_with_last() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("testuser").build(); + when(dfe.getSource()).thenReturn(profile); + + ArticleData articleData = buildArticleData("fav-article"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.PREV, false); + when(articleQueryService.findRecentArticlesWithCursor(any(), any(), any(), any(), any())) + .thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.userFavorites(null, null, 5, null, dfe); + + assertNotNull(result); + assertEquals(1, result.getData().getEdges().size()); + } + + @Test + void should_throw_when_first_and_last_both_null_in_user_favorites() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + assertThrows( + IllegalArgumentException.class, + () -> articleDatafetcher.userFavorites(null, null, null, null, dfe)); + } + + @Test + void should_get_user_articles_with_first() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("author").build(); + when(dfe.getSource()).thenReturn(profile); + + ArticleData articleData = buildArticleData("my-article"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.NEXT, false); + when(articleQueryService.findRecentArticlesWithCursor(any(), eq("author"), any(), any(), any())) + .thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.userArticles(10, null, null, null, dfe); + + assertNotNull(result); + assertEquals(1, result.getData().getEdges().size()); + } + + @Test + void should_get_user_articles_with_last() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Profile profile = Profile.newBuilder().username("author").build(); + when(dfe.getSource()).thenReturn(profile); + + ArticleData articleData = buildArticleData("my-article"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.PREV, false); + when(articleQueryService.findRecentArticlesWithCursor(any(), eq("author"), any(), any(), any())) + .thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.userArticles(null, null, 5, null, dfe); + + assertNotNull(result); + } + + @Test + void should_throw_when_first_and_last_both_null_in_user_articles() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + assertThrows( + IllegalArgumentException.class, + () -> articleDatafetcher.userArticles(null, null, null, null, dfe)); + } + + @Test + void should_get_articles_with_first_parameter() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + ArticleData articleData = buildArticleData("article-1"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.NEXT, false); + when(articleQueryService.findRecentArticlesWithCursor(any(), any(), any(), any(), any())) + .thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.getArticles(10, null, null, null, "author", "fav", "java", dfe); + + assertNotNull(result); + assertEquals(1, result.getData().getEdges().size()); + } + + @Test + void should_get_articles_with_last_parameter() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + ArticleData articleData = buildArticleData("article-1"); + CursorPager pager = + new CursorPager<>(Arrays.asList(articleData), Direction.PREV, false); + when(articleQueryService.findRecentArticlesWithCursor(any(), any(), any(), any(), any())) + .thenReturn(pager); + + DataFetcherResult result = + articleDatafetcher.getArticles(null, null, 5, null, null, null, null, dfe); + + assertNotNull(result); + } + + @Test + void should_throw_when_first_and_last_both_null_in_get_articles() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + assertThrows( + IllegalArgumentException.class, + () -> articleDatafetcher.getArticles(null, null, null, null, null, null, null, dfe)); + } + + @Test + void should_get_article_from_payload() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + io.spring.core.article.Article coreArticle = + new io.spring.core.article.Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(dfe.getLocalContext()).thenReturn(coreArticle); + + ArticleData articleData = buildArticleData("title"); + when(articleQueryService.findById(eq(coreArticle.getId()), any())) + .thenReturn(Optional.of(articleData)); + + DataFetcherResult
result = articleDatafetcher.getArticle(dfe); + + assertNotNull(result); + assertEquals("title", result.getData().getSlug()); + } + + @Test + void should_get_comment_article() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + CommentData commentData = + new CommentData("cid", "body", "articleId", new DateTime(), new DateTime(), null); + when(dfe.getLocalContext()).thenReturn(commentData); + + ArticleData articleData = buildArticleData("slug"); + when(articleQueryService.findById(eq("articleId"), any())).thenReturn(Optional.of(articleData)); + + DataFetcherResult
result = articleDatafetcher.getCommentArticle(dfe); + + assertNotNull(result); + assertEquals("slug", result.getData().getSlug()); + } + + @Test + void should_find_article_by_slug() { + ArticleData articleData = buildArticleData("my-slug"); + when(articleQueryService.findBySlug(eq("my-slug"), any())).thenReturn(Optional.of(articleData)); + + DataFetcherResult
result = articleDatafetcher.findArticleBySlug("my-slug"); + + assertNotNull(result); + assertEquals("my-slug", result.getData().getSlug()); + assertEquals("Title", result.getData().getTitle()); + assertEquals("description", result.getData().getDescription()); + assertEquals("body", result.getData().getBody()); + assertFalse(result.getData().getFavorited()); + assertEquals(0, result.getData().getFavoritesCount()); + assertNotNull(result.getData().getCreatedAt()); + assertNotNull(result.getData().getUpdatedAt()); + assertEquals(Arrays.asList("java"), result.getData().getTagList()); + } + + @Test + void should_handle_empty_pager_results() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + CursorPager emptyPager = new CursorPager<>(Arrays.asList(), Direction.NEXT, false); + when(articleQueryService.findUserFeedWithCursor(any(), any())).thenReturn(emptyPager); + + DataFetcherResult result = + articleDatafetcher.getFeed(10, null, null, null, dfe); + + assertNotNull(result); + assertTrue(result.getData().getEdges().isEmpty()); + assertNull(result.getData().getPageInfo().getStartCursor()); + assertNull(result.getData().getPageInfo().getEndCursor()); + } +} diff --git a/src/test/java/io/spring/graphql/ArticleMutationTest.java b/src/test/java/io/spring/graphql/ArticleMutationTest.java new file mode 100644 index 000000000..2ccd6ad09 --- /dev/null +++ b/src/test/java/io/spring/graphql/ArticleMutationTest.java @@ -0,0 +1,222 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import graphql.execution.DataFetcherResult; +import io.spring.api.exception.NoAuthorizationException; +import io.spring.api.exception.ResourceNotFoundException; +import io.spring.application.article.ArticleCommandService; +import io.spring.core.article.Article; +import io.spring.core.article.ArticleRepository; +import io.spring.core.favorite.ArticleFavorite; +import io.spring.core.favorite.ArticleFavoriteRepository; +import io.spring.core.user.User; +import io.spring.graphql.exception.AuthenticationException; +import io.spring.graphql.types.ArticlePayload; +import io.spring.graphql.types.CreateArticleInput; +import io.spring.graphql.types.DeletionStatus; +import io.spring.graphql.types.UpdateArticleInput; +import java.util.Arrays; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; + +public class ArticleMutationTest { + + private ArticleCommandService articleCommandService; + private ArticleFavoriteRepository articleFavoriteRepository; + private ArticleRepository articleRepository; + private ArticleMutation articleMutation; + private User user; + + @BeforeEach + void setUp() { + articleCommandService = mock(ArticleCommandService.class); + articleFavoriteRepository = mock(ArticleFavoriteRepository.class); + articleRepository = mock(ArticleRepository.class); + articleMutation = + new ArticleMutation(articleCommandService, articleFavoriteRepository, articleRepository); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_create_article_successfully() { + CreateArticleInput input = + CreateArticleInput.newBuilder() + .title("Test Title") + .description("desc") + .body("body") + .tagList(Arrays.asList("java", "spring")) + .build(); + + Article article = + new Article("Test Title", "desc", "body", Arrays.asList("java", "spring"), user.getId()); + when(articleCommandService.createArticle(any(), eq(user))).thenReturn(article); + + DataFetcherResult result = articleMutation.createArticle(input); + + assertNotNull(result); + assertNotNull(result.getData()); + assertEquals(article, result.getLocalContext()); + verify(articleCommandService).createArticle(any(), eq(user)); + } + + @Test + void should_create_article_with_null_tag_list() { + CreateArticleInput input = + CreateArticleInput.newBuilder() + .title("Test Title") + .description("desc") + .body("body") + .build(); + + Article article = new Article("Test Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleCommandService.createArticle(any(), eq(user))).thenReturn(article); + + DataFetcherResult result = articleMutation.createArticle(input); + + assertNotNull(result); + verify(articleCommandService).createArticle(any(), eq(user)); + } + + @Test + void should_throw_when_creating_article_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + CreateArticleInput input = + CreateArticleInput.newBuilder().title("Test").description("d").body("b").build(); + + assertThrows(AuthenticationException.class, () -> articleMutation.createArticle(input)); + } + + @Test + void should_update_article_successfully() { + Article article = new Article("Old Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("old-title")).thenReturn(Optional.of(article)); + when(articleCommandService.updateArticle(eq(article), any())).thenReturn(article); + + UpdateArticleInput params = + UpdateArticleInput.newBuilder() + .title("New Title") + .description("new desc") + .body("new body") + .build(); + + DataFetcherResult result = articleMutation.updateArticle("old-title", params); + + assertNotNull(result); + verify(articleCommandService).updateArticle(eq(article), any()); + } + + @Test + void should_throw_when_updating_nonexistent_article() { + when(articleRepository.findBySlug("nonexistent")).thenReturn(Optional.empty()); + UpdateArticleInput params = UpdateArticleInput.newBuilder().title("New").build(); + + assertThrows( + ResourceNotFoundException.class, + () -> articleMutation.updateArticle("nonexistent", params)); + } + + @Test + void should_throw_when_updating_article_without_authorization() { + User otherUser = new User("other@test.com", "other", "pass", "", ""); + Article article = new Article("Title", "desc", "body", Arrays.asList(), otherUser.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + UpdateArticleInput params = UpdateArticleInput.newBuilder().title("New").build(); + + assertThrows( + NoAuthorizationException.class, () -> articleMutation.updateArticle("title", params)); + } + + @Test + void should_favorite_article_successfully() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + + DataFetcherResult result = articleMutation.favoriteArticle("title"); + + assertNotNull(result); + assertEquals(article, result.getLocalContext()); + verify(articleFavoriteRepository).save(any(ArticleFavorite.class)); + } + + @Test + void should_throw_when_favoriting_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + assertThrows(AuthenticationException.class, () -> articleMutation.favoriteArticle("slug")); + } + + @Test + void should_unfavorite_article_successfully() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + ArticleFavorite favorite = new ArticleFavorite(article.getId(), user.getId()); + when(articleFavoriteRepository.find(article.getId(), user.getId())) + .thenReturn(Optional.of(favorite)); + + DataFetcherResult result = articleMutation.unfavoriteArticle("title"); + + assertNotNull(result); + verify(articleFavoriteRepository).remove(favorite); + } + + @Test + void should_unfavorite_when_no_existing_favorite() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + when(articleFavoriteRepository.find(article.getId(), user.getId())) + .thenReturn(Optional.empty()); + + DataFetcherResult result = articleMutation.unfavoriteArticle("title"); + + assertNotNull(result); + verify(articleFavoriteRepository, never()).remove(any()); + } + + @Test + void should_delete_article_successfully() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + + DeletionStatus result = articleMutation.deleteArticle("title"); + + assertTrue(result.getSuccess()); + verify(articleRepository).remove(article); + } + + @Test + void should_throw_when_deleting_without_authorization() { + User otherUser = new User("other@test.com", "other", "pass", "", ""); + Article article = new Article("Title", "desc", "body", Arrays.asList(), otherUser.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + + assertThrows(NoAuthorizationException.class, () -> articleMutation.deleteArticle("title")); + } + + @Test + void should_throw_when_deleting_nonexistent_article() { + when(articleRepository.findBySlug("nope")).thenReturn(Optional.empty()); + assertThrows(ResourceNotFoundException.class, () -> articleMutation.deleteArticle("nope")); + } +} diff --git a/src/test/java/io/spring/graphql/CommentDatafetcherTest.java b/src/test/java/io/spring/graphql/CommentDatafetcherTest.java new file mode 100644 index 000000000..be16b2130 --- /dev/null +++ b/src/test/java/io/spring/graphql/CommentDatafetcherTest.java @@ -0,0 +1,197 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import com.netflix.graphql.dgs.DgsDataFetchingEnvironment; +import graphql.execution.DataFetcherResult; +import io.spring.application.CommentQueryService; +import io.spring.application.CursorPager; +import io.spring.application.CursorPager.Direction; +import io.spring.application.data.ArticleData; +import io.spring.application.data.CommentData; +import io.spring.application.data.ProfileData; +import io.spring.core.user.User; +import io.spring.graphql.types.Article; +import io.spring.graphql.types.Comment; +import io.spring.graphql.types.CommentsConnection; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.joda.time.DateTime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; + +public class CommentDatafetcherTest { + + private CommentQueryService commentQueryService; + private CommentDatafetcher commentDatafetcher; + private User user; + + @BeforeEach + void setUp() { + commentQueryService = mock(CommentQueryService.class); + commentDatafetcher = new CommentDatafetcher(commentQueryService); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_get_comment_from_payload() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + CommentData commentData = + new CommentData( + "comment1", + "body text", + "articleId", + new DateTime(), + new DateTime(), + new ProfileData("uid", "author", "bio", "img", false)); + when(dfe.getLocalContext()).thenReturn(commentData); + + DataFetcherResult result = commentDatafetcher.getComment(dfe); + + assertNotNull(result); + assertEquals("comment1", result.getData().getId()); + assertEquals("body text", result.getData().getBody()); + assertNotNull(result.getData().getCreatedAt()); + assertNotNull(result.getData().getUpdatedAt()); + } + + @Test + void should_get_article_comments_with_first() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Article article = Article.newBuilder().slug("test-article").build(); + when(dfe.getSource()).thenReturn(article); + + ArticleData articleData = + new ArticleData( + "artId", + "test-article", + "Title", + "desc", + "body", + false, + 0, + new DateTime(), + new DateTime(), + Arrays.asList(), + new ProfileData("uid", "author", "", "", false)); + Map map = new HashMap<>(); + map.put("test-article", articleData); + when(dfe.getLocalContext()).thenReturn(map); + + CommentData commentData = + new CommentData( + "c1", + "comment body", + "artId", + new DateTime(), + new DateTime(), + new ProfileData("uid", "commenter", "", "", false)); + CursorPager pager = + new CursorPager<>(Arrays.asList(commentData), Direction.NEXT, true); + when(commentQueryService.findByArticleIdWithCursor(any(), any(), any())).thenReturn(pager); + + DataFetcherResult result = + commentDatafetcher.articleComments(10, null, null, null, dfe); + + assertNotNull(result); + assertEquals(1, result.getData().getEdges().size()); + assertEquals("c1", result.getData().getEdges().get(0).getNode().getId()); + assertTrue(result.getData().getPageInfo().isHasNextPage()); + } + + @Test + void should_get_article_comments_with_last() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Article article = Article.newBuilder().slug("test-article").build(); + when(dfe.getSource()).thenReturn(article); + + ArticleData articleData = + new ArticleData( + "artId", + "test-article", + "Title", + "desc", + "body", + false, + 0, + new DateTime(), + new DateTime(), + Arrays.asList(), + new ProfileData("uid", "author", "", "", false)); + Map map = new HashMap<>(); + map.put("test-article", articleData); + when(dfe.getLocalContext()).thenReturn(map); + + CommentData commentData = + new CommentData( + "c1", + "comment body", + "artId", + new DateTime(), + new DateTime(), + new ProfileData("uid", "commenter", "", "", false)); + CursorPager pager = + new CursorPager<>(Arrays.asList(commentData), Direction.PREV, true); + when(commentQueryService.findByArticleIdWithCursor(any(), any(), any())).thenReturn(pager); + + DataFetcherResult result = + commentDatafetcher.articleComments(null, null, 5, null, dfe); + + assertNotNull(result); + assertTrue(result.getData().getPageInfo().isHasPreviousPage()); + } + + @Test + void should_throw_when_first_and_last_both_null() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + assertThrows( + IllegalArgumentException.class, + () -> commentDatafetcher.articleComments(null, null, null, null, dfe)); + } + + @Test + void should_handle_empty_comments() { + DgsDataFetchingEnvironment dfe = mock(DgsDataFetchingEnvironment.class); + Article article = Article.newBuilder().slug("test-article").build(); + when(dfe.getSource()).thenReturn(article); + + ArticleData articleData = + new ArticleData( + "artId", + "test-article", + "Title", + "desc", + "body", + false, + 0, + new DateTime(), + new DateTime(), + Arrays.asList(), + new ProfileData("uid", "author", "", "", false)); + Map map = new HashMap<>(); + map.put("test-article", articleData); + when(dfe.getLocalContext()).thenReturn(map); + + CursorPager pager = new CursorPager<>(Arrays.asList(), Direction.NEXT, false); + when(commentQueryService.findByArticleIdWithCursor(any(), any(), any())).thenReturn(pager); + + DataFetcherResult result = + commentDatafetcher.articleComments(10, null, null, null, dfe); + + assertNotNull(result); + assertTrue(result.getData().getEdges().isEmpty()); + } +} diff --git a/src/test/java/io/spring/graphql/CommentMutationTest.java b/src/test/java/io/spring/graphql/CommentMutationTest.java new file mode 100644 index 000000000..1f841984d --- /dev/null +++ b/src/test/java/io/spring/graphql/CommentMutationTest.java @@ -0,0 +1,155 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import graphql.execution.DataFetcherResult; +import io.spring.api.exception.NoAuthorizationException; +import io.spring.api.exception.ResourceNotFoundException; +import io.spring.application.CommentQueryService; +import io.spring.application.data.CommentData; +import io.spring.application.data.ProfileData; +import io.spring.core.article.Article; +import io.spring.core.article.ArticleRepository; +import io.spring.core.comment.Comment; +import io.spring.core.comment.CommentRepository; +import io.spring.core.user.User; +import io.spring.graphql.exception.AuthenticationException; +import io.spring.graphql.types.CommentPayload; +import io.spring.graphql.types.DeletionStatus; +import java.util.Arrays; +import java.util.Optional; +import org.joda.time.DateTime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; + +public class CommentMutationTest { + + private ArticleRepository articleRepository; + private CommentRepository commentRepository; + private CommentQueryService commentQueryService; + private CommentMutation commentMutation; + private User user; + + @BeforeEach + void setUp() { + articleRepository = mock(ArticleRepository.class); + commentRepository = mock(CommentRepository.class); + commentQueryService = mock(CommentQueryService.class); + commentMutation = + new CommentMutation(articleRepository, commentRepository, commentQueryService); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_create_comment_successfully() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + + CommentData commentData = + new CommentData( + "cid", + "Great article!", + article.getId(), + new DateTime(), + new DateTime(), + new ProfileData(user.getId(), "testuser", "bio", "image", false)); + when(commentQueryService.findById(any(), eq(user))).thenReturn(Optional.of(commentData)); + + DataFetcherResult result = + commentMutation.createComment("title", "Great article!"); + + assertNotNull(result); + assertNotNull(result.getData()); + assertEquals(commentData, result.getLocalContext()); + verify(commentRepository).save(any(Comment.class)); + } + + @Test + void should_throw_when_creating_comment_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + assertThrows( + AuthenticationException.class, () -> commentMutation.createComment("slug", "body")); + } + + @Test + void should_throw_when_creating_comment_on_nonexistent_article() { + when(articleRepository.findBySlug("nope")).thenReturn(Optional.empty()); + assertThrows( + ResourceNotFoundException.class, () -> commentMutation.createComment("nope", "body")); + } + + @Test + void should_delete_comment_successfully() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + + Comment comment = new Comment("body", user.getId(), article.getId()); + when(commentRepository.findById(article.getId(), comment.getId())) + .thenReturn(Optional.of(comment)); + + DeletionStatus result = commentMutation.removeComment("title", comment.getId()); + + assertTrue(result.getSuccess()); + verify(commentRepository).remove(comment); + } + + @Test + void should_throw_when_deleting_comment_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + assertThrows(AuthenticationException.class, () -> commentMutation.removeComment("slug", "cid")); + } + + @Test + void should_throw_when_deleting_comment_on_nonexistent_article() { + when(articleRepository.findBySlug("nope")).thenReturn(Optional.empty()); + assertThrows( + ResourceNotFoundException.class, () -> commentMutation.removeComment("nope", "cid")); + } + + @Test + void should_throw_when_deleting_nonexistent_comment() { + Article article = new Article("Title", "desc", "body", Arrays.asList(), user.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + when(commentRepository.findById(article.getId(), "nonexistent")).thenReturn(Optional.empty()); + + assertThrows( + ResourceNotFoundException.class, + () -> commentMutation.removeComment("title", "nonexistent")); + } + + @Test + void should_throw_when_deleting_comment_without_authorization() { + User otherUser = new User("other@test.com", "other", "pass", "", ""); + Article article = new Article("Title", "desc", "body", Arrays.asList(), otherUser.getId()); + when(articleRepository.findBySlug("title")).thenReturn(Optional.of(article)); + + Comment comment = new Comment("body", otherUser.getId(), article.getId()); + when(commentRepository.findById(article.getId(), comment.getId())) + .thenReturn(Optional.of(comment)); + + assertThrows( + NoAuthorizationException.class, + () -> commentMutation.removeComment("title", comment.getId())); + } +} diff --git a/src/test/java/io/spring/graphql/MeDatafetcherTest.java b/src/test/java/io/spring/graphql/MeDatafetcherTest.java new file mode 100644 index 000000000..3e06fe94e --- /dev/null +++ b/src/test/java/io/spring/graphql/MeDatafetcherTest.java @@ -0,0 +1,89 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import graphql.execution.DataFetcherResult; +import graphql.schema.DataFetchingEnvironment; +import io.spring.application.UserQueryService; +import io.spring.application.data.UserData; +import io.spring.core.service.JwtService; +import io.spring.core.user.User; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; + +public class MeDatafetcherTest { + + private UserQueryService userQueryService; + private JwtService jwtService; + private MeDatafetcher meDatafetcher; + private User user; + + @BeforeEach + void setUp() { + userQueryService = mock(UserQueryService.class); + jwtService = mock(JwtService.class); + meDatafetcher = new MeDatafetcher(userQueryService, jwtService); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_get_me_successfully() { + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + + UserData userData = new UserData(user.getId(), "test@test.com", "testuser", "bio", "image"); + when(userQueryService.findById(user.getId())).thenReturn(Optional.of(userData)); + + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + + DataFetcherResult result = + meDatafetcher.getMe("Token mytoken", dfe); + + assertNotNull(result); + assertEquals("test@test.com", result.getData().getEmail()); + assertEquals("testuser", result.getData().getUsername()); + assertEquals("mytoken", result.getData().getToken()); + } + + @Test + void should_return_null_when_anonymous() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + + DataFetcherResult result = + meDatafetcher.getMe("Token whatever", dfe); + + assertNull(result); + } + + @Test + void should_get_user_payload_user() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + when(dfe.getLocalContext()).thenReturn(user); + when(jwtService.toToken(user)).thenReturn("generated-token"); + + DataFetcherResult result = meDatafetcher.getUserPayloadUser(dfe); + + assertNotNull(result); + assertEquals("test@test.com", result.getData().getEmail()); + assertEquals("testuser", result.getData().getUsername()); + assertEquals("generated-token", result.getData().getToken()); + assertEquals(user, result.getLocalContext()); + } +} diff --git a/src/test/java/io/spring/graphql/ProfileDatafetcherTest.java b/src/test/java/io/spring/graphql/ProfileDatafetcherTest.java new file mode 100644 index 000000000..f4ec9d6cc --- /dev/null +++ b/src/test/java/io/spring/graphql/ProfileDatafetcherTest.java @@ -0,0 +1,143 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import graphql.schema.DataFetchingEnvironment; +import io.spring.application.ProfileQueryService; +import io.spring.application.data.ArticleData; +import io.spring.application.data.CommentData; +import io.spring.application.data.ProfileData; +import io.spring.core.user.User; +import io.spring.graphql.types.Article; +import io.spring.graphql.types.Comment; +import io.spring.graphql.types.Profile; +import io.spring.graphql.types.ProfilePayload; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.joda.time.DateTime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; + +public class ProfileDatafetcherTest { + + private ProfileQueryService profileQueryService; + private ProfileDatafetcher profileDatafetcher; + private User user; + + @BeforeEach + void setUp() { + profileQueryService = mock(ProfileQueryService.class); + profileDatafetcher = new ProfileDatafetcher(profileQueryService); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_get_user_profile() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + when(dfe.getLocalContext()).thenReturn(user); + + ProfileData profileData = new ProfileData(user.getId(), "testuser", "bio", "image", true); + when(profileQueryService.findByUsername(eq("testuser"), any())) + .thenReturn(Optional.of(profileData)); + + Profile result = profileDatafetcher.getUserProfile(dfe); + + assertNotNull(result); + assertEquals("testuser", result.getUsername()); + assertEquals("bio", result.getBio()); + assertEquals("image", result.getImage()); + assertTrue(result.getFollowing()); + } + + @Test + void should_get_article_author() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + Article article = Article.newBuilder().slug("test-slug").build(); + when(dfe.getSource()).thenReturn(article); + + ArticleData articleData = + new ArticleData( + "id", + "test-slug", + "Title", + "desc", + "body", + false, + 0, + new DateTime(), + new DateTime(), + null, + new ProfileData("uid", "authoruser", "bio", "img", false)); + Map map = new HashMap<>(); + map.put("test-slug", articleData); + when(dfe.getLocalContext()).thenReturn(map); + + ProfileData profileData = new ProfileData("uid", "authoruser", "bio", "img", false); + when(profileQueryService.findByUsername(eq("authoruser"), any())) + .thenReturn(Optional.of(profileData)); + + Profile result = profileDatafetcher.getAuthor(dfe); + + assertNotNull(result); + assertEquals("authoruser", result.getUsername()); + } + + @Test + void should_get_comment_author() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + Comment comment = Comment.newBuilder().id("c1").body("text").build(); + when(dfe.getSource()).thenReturn(comment); + + CommentData commentData = + new CommentData( + "c1", + "text", + "artId", + new DateTime(), + new DateTime(), + new ProfileData("uid", "commenter", "bio", "img", false)); + Map map = new HashMap<>(); + map.put("c1", commentData); + when(dfe.getLocalContext()).thenReturn(map); + + ProfileData profileData = new ProfileData("uid", "commenter", "bio", "img", false); + when(profileQueryService.findByUsername(eq("commenter"), any())) + .thenReturn(Optional.of(profileData)); + + Profile result = profileDatafetcher.getCommentAuthor(dfe); + + assertNotNull(result); + assertEquals("commenter", result.getUsername()); + } + + @Test + void should_query_profile_by_username() { + DataFetchingEnvironment dfe = mock(DataFetchingEnvironment.class); + when(dfe.getArgument("username")).thenReturn("someuser"); + + ProfileData profileData = new ProfileData("uid", "someuser", "bio", "img", true); + when(profileQueryService.findByUsername(eq("someuser"), any())) + .thenReturn(Optional.of(profileData)); + + ProfilePayload result = profileDatafetcher.queryProfile("someuser", dfe); + + assertNotNull(result); + assertNotNull(result.getProfile()); + assertEquals("someuser", result.getProfile().getUsername()); + assertTrue(result.getProfile().getFollowing()); + } +} diff --git a/src/test/java/io/spring/graphql/RelationMutationTest.java b/src/test/java/io/spring/graphql/RelationMutationTest.java new file mode 100644 index 000000000..b7fbfd541 --- /dev/null +++ b/src/test/java/io/spring/graphql/RelationMutationTest.java @@ -0,0 +1,121 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import io.spring.api.exception.ResourceNotFoundException; +import io.spring.application.ProfileQueryService; +import io.spring.application.data.ProfileData; +import io.spring.core.user.FollowRelation; +import io.spring.core.user.User; +import io.spring.core.user.UserRepository; +import io.spring.graphql.exception.AuthenticationException; +import io.spring.graphql.types.ProfilePayload; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; + +public class RelationMutationTest { + + private UserRepository userRepository; + private ProfileQueryService profileQueryService; + private RelationMutation relationMutation; + private User user; + private User targetUser; + + @BeforeEach + void setUp() { + userRepository = mock(UserRepository.class); + profileQueryService = mock(ProfileQueryService.class); + relationMutation = new RelationMutation(userRepository, profileQueryService); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + targetUser = new User("target@test.com", "targetuser", "password", "bio", "img"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_follow_user_successfully() { + when(userRepository.findByUsername("targetuser")).thenReturn(Optional.of(targetUser)); + ProfileData profileData = new ProfileData(targetUser.getId(), "targetuser", "bio", "img", true); + when(profileQueryService.findByUsername(eq("targetuser"), any())) + .thenReturn(Optional.of(profileData)); + + ProfilePayload result = relationMutation.follow("targetuser"); + + assertNotNull(result); + assertNotNull(result.getProfile()); + assertEquals("targetuser", result.getProfile().getUsername()); + assertTrue(result.getProfile().getFollowing()); + verify(userRepository).saveRelation(any(FollowRelation.class)); + } + + @Test + void should_throw_when_following_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + assertThrows(AuthenticationException.class, () -> relationMutation.follow("someone")); + } + + @Test + void should_throw_when_following_nonexistent_user() { + when(userRepository.findByUsername("nobody")).thenReturn(Optional.empty()); + assertThrows(ResourceNotFoundException.class, () -> relationMutation.follow("nobody")); + } + + @Test + void should_unfollow_user_successfully() { + when(userRepository.findByUsername("targetuser")).thenReturn(Optional.of(targetUser)); + FollowRelation relation = new FollowRelation(user.getId(), targetUser.getId()); + when(userRepository.findRelation(user.getId(), targetUser.getId())) + .thenReturn(Optional.of(relation)); + ProfileData profileData = + new ProfileData(targetUser.getId(), "targetuser", "bio", "img", false); + when(profileQueryService.findByUsername(eq("targetuser"), any())) + .thenReturn(Optional.of(profileData)); + + ProfilePayload result = relationMutation.unfollow("targetuser"); + + assertNotNull(result); + assertFalse(result.getProfile().getFollowing()); + verify(userRepository).removeRelation(relation); + } + + @Test + void should_throw_when_unfollowing_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + assertThrows(AuthenticationException.class, () -> relationMutation.unfollow("someone")); + } + + @Test + void should_throw_when_unfollowing_nonexistent_user() { + when(userRepository.findByUsername("nobody")).thenReturn(Optional.empty()); + assertThrows(ResourceNotFoundException.class, () -> relationMutation.unfollow("nobody")); + } + + @Test + void should_throw_when_no_relation_exists_for_unfollow() { + when(userRepository.findByUsername("targetuser")).thenReturn(Optional.of(targetUser)); + when(userRepository.findRelation(user.getId(), targetUser.getId())) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, () -> relationMutation.unfollow("targetuser")); + } +} diff --git a/src/test/java/io/spring/graphql/SecurityUtilTest.java b/src/test/java/io/spring/graphql/SecurityUtilTest.java new file mode 100644 index 000000000..bf94d7321 --- /dev/null +++ b/src/test/java/io/spring/graphql/SecurityUtilTest.java @@ -0,0 +1,54 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; + +import io.spring.core.user.User; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; + +public class SecurityUtilTest { + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_return_current_user_when_authenticated() { + User user = new User("test@test.com", "testuser", "pass", "bio", "img"); + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + + Optional result = SecurityUtil.getCurrentUser(); + + assertTrue(result.isPresent()); + assertEquals("testuser", result.get().getUsername()); + } + + @Test + void should_return_empty_when_anonymous() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + + Optional result = SecurityUtil.getCurrentUser(); + + assertTrue(result.isEmpty()); + } + + @Test + void should_return_empty_when_principal_is_null() { + TestingAuthenticationToken auth = new TestingAuthenticationToken(null, null); + SecurityContextHolder.getContext().setAuthentication(auth); + + Optional result = SecurityUtil.getCurrentUser(); + + assertTrue(result.isEmpty()); + } +} diff --git a/src/test/java/io/spring/graphql/TagDatafetcherTest.java b/src/test/java/io/spring/graphql/TagDatafetcherTest.java new file mode 100644 index 000000000..f557395ff --- /dev/null +++ b/src/test/java/io/spring/graphql/TagDatafetcherTest.java @@ -0,0 +1,26 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import io.spring.application.TagsQueryService; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TagDatafetcherTest { + + @Test + void should_return_all_tags() { + TagsQueryService tagsQueryService = mock(TagsQueryService.class); + TagDatafetcher tagDatafetcher = new TagDatafetcher(tagsQueryService); + when(tagsQueryService.allTags()).thenReturn(Arrays.asList("java", "spring", "kotlin")); + + List result = tagDatafetcher.getTags(); + + assertEquals(3, result.size()); + assertTrue(result.contains("java")); + assertTrue(result.contains("spring")); + assertTrue(result.contains("kotlin")); + } +} diff --git a/src/test/java/io/spring/graphql/UserMutationTest.java b/src/test/java/io/spring/graphql/UserMutationTest.java new file mode 100644 index 000000000..9f7122d3d --- /dev/null +++ b/src/test/java/io/spring/graphql/UserMutationTest.java @@ -0,0 +1,147 @@ +package io.spring.graphql; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import graphql.execution.DataFetcherResult; +import io.spring.api.exception.InvalidAuthenticationException; +import io.spring.application.user.UserService; +import io.spring.core.user.User; +import io.spring.core.user.UserRepository; +import io.spring.graphql.types.CreateUserInput; +import io.spring.graphql.types.UpdateUserInput; +import io.spring.graphql.types.UserPayload; +import io.spring.graphql.types.UserResult; +import java.util.HashSet; +import java.util.Optional; +import javax.validation.ConstraintViolationException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; + +public class UserMutationTest { + + private UserRepository userRepository; + private PasswordEncoder encryptService; + private UserService userService; + private UserMutation userMutation; + private User user; + + @BeforeEach + void setUp() { + userRepository = mock(UserRepository.class); + encryptService = mock(PasswordEncoder.class); + userService = mock(UserService.class); + userMutation = new UserMutation(userRepository, encryptService, userService); + user = new User("test@test.com", "testuser", "password", "bio", "image"); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void should_create_user_successfully() { + CreateUserInput input = + CreateUserInput.newBuilder() + .email("new@test.com") + .username("newuser") + .password("pass123") + .build(); + + when(userService.createUser(any())).thenReturn(user); + + DataFetcherResult result = userMutation.createUser(input); + + assertNotNull(result); + assertNotNull(result.getData()); + assertEquals(user, result.getLocalContext()); + verify(userService).createUser(any()); + } + + @Test + void should_return_error_when_create_user_has_validation_error() { + CreateUserInput input = + CreateUserInput.newBuilder().email("bad").username("").password("").build(); + + ConstraintViolationException cve = new ConstraintViolationException(new HashSet<>()); + when(userService.createUser(any())).thenThrow(cve); + + DataFetcherResult result = userMutation.createUser(input); + + assertNotNull(result); + assertNotNull(result.getData()); + assertNull(result.getLocalContext()); + } + + @Test + void should_login_successfully() { + when(userRepository.findByEmail("test@test.com")).thenReturn(Optional.of(user)); + when(encryptService.matches("password", "password")).thenReturn(true); + + DataFetcherResult result = userMutation.login("password", "test@test.com"); + + assertNotNull(result); + assertEquals(user, result.getLocalContext()); + } + + @Test + void should_throw_when_login_with_wrong_password() { + when(userRepository.findByEmail("test@test.com")).thenReturn(Optional.of(user)); + when(encryptService.matches("wrong", "password")).thenReturn(false); + + assertThrows( + InvalidAuthenticationException.class, () -> userMutation.login("wrong", "test@test.com")); + } + + @Test + void should_throw_when_login_with_nonexistent_email() { + when(userRepository.findByEmail("nope@test.com")).thenReturn(Optional.empty()); + + assertThrows( + InvalidAuthenticationException.class, () -> userMutation.login("pass", "nope@test.com")); + } + + @Test + void should_update_user_successfully() { + TestingAuthenticationToken auth = new TestingAuthenticationToken(user, null); + SecurityContextHolder.getContext().setAuthentication(auth); + + UpdateUserInput input = + UpdateUserInput.newBuilder() + .email("updated@test.com") + .username("updateduser") + .bio("new bio") + .image("new image") + .password("newpass") + .build(); + + DataFetcherResult result = userMutation.updateUser(input); + + assertNotNull(result); + assertEquals(user, result.getLocalContext()); + verify(userService).updateUser(any()); + } + + @Test + void should_return_null_when_updating_user_without_auth() { + AnonymousAuthenticationToken anon = + new AnonymousAuthenticationToken( + "key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + SecurityContextHolder.getContext().setAuthentication(anon); + + UpdateUserInput input = UpdateUserInput.newBuilder().email("x@y.com").build(); + + DataFetcherResult result = userMutation.updateUser(input); + + assertNull(result); + verify(userService, never()).updateUser(any()); + } +} diff --git a/src/test/java/io/spring/graphql/exception/GraphQLCustomizeExceptionHandlerTest.java b/src/test/java/io/spring/graphql/exception/GraphQLCustomizeExceptionHandlerTest.java new file mode 100644 index 000000000..e1dc13390 --- /dev/null +++ b/src/test/java/io/spring/graphql/exception/GraphQLCustomizeExceptionHandlerTest.java @@ -0,0 +1,137 @@ +package io.spring.graphql.exception; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import graphql.execution.DataFetcherExceptionHandlerParameters; +import graphql.execution.DataFetcherExceptionHandlerResult; +import graphql.execution.ResultPath; +import io.spring.api.exception.InvalidAuthenticationException; +import io.spring.graphql.types.Error; +import java.lang.annotation.Annotation; +import java.util.HashSet; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Path; +import javax.validation.metadata.ConstraintDescriptor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class GraphQLCustomizeExceptionHandlerTest { + + private GraphQLCustomizeExceptionHandler handler; + + @BeforeEach + void setUp() { + handler = new GraphQLCustomizeExceptionHandler(); + } + + @Test + void should_handle_invalid_authentication_exception() { + DataFetcherExceptionHandlerParameters params = + mock(DataFetcherExceptionHandlerParameters.class); + InvalidAuthenticationException ex = new InvalidAuthenticationException(); + when(params.getException()).thenReturn(ex); + when(params.getPath()).thenReturn(ResultPath.rootPath()); + + DataFetcherExceptionHandlerResult result = handler.onException(params); + + assertNotNull(result); + assertFalse(result.getErrors().isEmpty()); + } + + @Test + void should_handle_constraint_violation_exception() { + DataFetcherExceptionHandlerParameters params = + mock(DataFetcherExceptionHandlerParameters.class); + + ConstraintViolationException cve = + buildConstraintViolationException("createUser.param.email", "can't be empty"); + when(params.getException()).thenReturn(cve); + when(params.getPath()).thenReturn(ResultPath.rootPath()); + + DataFetcherExceptionHandlerResult result = handler.onException(params); + + assertNotNull(result); + assertFalse(result.getErrors().isEmpty()); + } + + @Test + void should_delegate_to_default_handler_for_other_exceptions() { + DataFetcherExceptionHandlerParameters params = + mock(DataFetcherExceptionHandlerParameters.class); + RuntimeException ex = new RuntimeException("something else"); + when(params.getException()).thenReturn(ex); + when(params.getPath()).thenReturn(ResultPath.rootPath()); + + DataFetcherExceptionHandlerResult result = handler.onException(params); + + assertNotNull(result); + } + + @Test + void should_get_errors_as_data() { + ConstraintViolationException cve = + buildConstraintViolationException("createUser.param.email", "can't be empty"); + + Error error = GraphQLCustomizeExceptionHandler.getErrorsAsData(cve); + + assertNotNull(error); + assertEquals("BAD_REQUEST", error.getMessage()); + assertFalse(error.getErrors().isEmpty()); + assertEquals("email", error.getErrors().get(0).getKey()); + assertTrue(error.getErrors().get(0).getValue().contains("can't be empty")); + } + + @Test + void should_handle_simple_path() { + ConstraintViolationException cve = buildConstraintViolationException("simplefield", "invalid"); + + Error error = GraphQLCustomizeExceptionHandler.getErrorsAsData(cve); + + assertNotNull(error); + assertEquals("simplefield", error.getErrors().get(0).getKey()); + } + + private ConstraintViolationException buildConstraintViolationException( + String pathStr, String message) { + Set> violations = new HashSet<>(); + ConstraintViolation violation = mock(ConstraintViolation.class); + when(violation.getRootBeanClass()).thenReturn(String.class); + Path path = mock(Path.class); + when(path.toString()).thenReturn(pathStr); + when(violation.getPropertyPath()).thenReturn(path); + when(violation.getMessage()).thenReturn(message); + + ConstraintDescriptor descriptor = mock(ConstraintDescriptor.class); + Annotation annotation = + new javax.validation.constraints.NotBlank() { + @Override + public String message() { + return ""; + } + + @Override + public Class[] groups() { + return new Class[0]; + } + + @Override + public Class[] payload() { + return new Class[0]; + } + + @Override + public Class annotationType() { + return javax.validation.constraints.NotBlank.class; + } + }; + when(descriptor.getAnnotation()).thenReturn(annotation); + when(violation.getConstraintDescriptor()).thenReturn(descriptor); + violations.add(violation); + + return new ConstraintViolationException(violations); + } +} diff --git a/src/test/java/io/spring/graphql/types/GraphQLTypesTest.java b/src/test/java/io/spring/graphql/types/GraphQLTypesTest.java new file mode 100644 index 000000000..6e9ac2ab5 --- /dev/null +++ b/src/test/java/io/spring/graphql/types/GraphQLTypesTest.java @@ -0,0 +1,615 @@ +package io.spring.graphql.types; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class GraphQLTypesTest { + + @Test + void should_build_article_type() { + Profile author = Profile.newBuilder().username("author").build(); + Article article = + Article.newBuilder() + .slug("my-article") + .title("My Article") + .description("desc") + .body("body content") + .favorited(true) + .favoritesCount(5) + .createdAt("2023-01-01") + .updatedAt("2023-01-02") + .tagList(Arrays.asList("java", "spring")) + .author(author) + .build(); + + assertEquals("my-article", article.getSlug()); + assertEquals("My Article", article.getTitle()); + assertEquals("desc", article.getDescription()); + assertEquals("body content", article.getBody()); + assertTrue(article.getFavorited()); + assertEquals(5, article.getFavoritesCount()); + assertEquals("2023-01-01", article.getCreatedAt()); + assertEquals("2023-01-02", article.getUpdatedAt()); + assertEquals(Arrays.asList("java", "spring"), article.getTagList()); + assertEquals(author, article.getAuthor()); + assertNotNull(article.toString()); + assertNotNull(article.hashCode()); + } + + @Test + void should_test_article_setters() { + Article article = new Article(); + Profile author = Profile.newBuilder().username("u").build(); + article.setSlug("slug"); + article.setTitle("title"); + article.setDescription("desc"); + article.setBody("body"); + article.setFavorited(false); + article.setFavoritesCount(3); + article.setCreatedAt("created"); + article.setUpdatedAt("updated"); + article.setTagList(Arrays.asList("tag")); + article.setAuthor(author); + article.setComments(null); + + assertEquals("slug", article.getSlug()); + assertEquals("title", article.getTitle()); + assertEquals("desc", article.getDescription()); + assertEquals("body", article.getBody()); + assertFalse(article.getFavorited()); + assertEquals(3, article.getFavoritesCount()); + assertNull(article.getComments()); + } + + @Test + void should_test_article_equals() { + Article a1 = Article.newBuilder().slug("s").title("t").build(); + Article a2 = Article.newBuilder().slug("s").title("t").build(); + Article a3 = Article.newBuilder().slug("other").build(); + + assertEquals(a1, a2); + assertEquals(a1, a1); + assertNotEquals(a1, a3); + assertNotEquals(a1, null); + assertNotEquals(a1, "string"); + } + + @Test + void should_build_article_edge() { + Article article = Article.newBuilder().slug("s").build(); + ArticleEdge edge = ArticleEdge.newBuilder().node(article).cursor("cursor1").build(); + + assertEquals(article, edge.getNode()); + assertEquals("cursor1", edge.getCursor()); + assertNotNull(edge.toString()); + } + + @Test + void should_test_article_edge_setters_and_equals() { + ArticleEdge edge = new ArticleEdge(); + edge.setNode(Article.newBuilder().slug("x").build()); + edge.setCursor("c"); + assertEquals("c", edge.getCursor()); + assertNotNull(edge.getNode()); + + ArticleEdge edge2 = ArticleEdge.newBuilder().node(edge.getNode()).cursor("c").build(); + assertEquals(edge, edge2); + assertEquals(edge.hashCode(), edge2.hashCode()); + } + + @Test + void should_build_articles_connection() { + Article article = Article.newBuilder().slug("s").build(); + ArticleEdge edge = ArticleEdge.newBuilder().node(article).cursor("c").build(); + ArticlesConnection conn = ArticlesConnection.newBuilder().edges(Arrays.asList(edge)).build(); + + assertEquals(1, conn.getEdges().size()); + assertNotNull(conn.toString()); + } + + @Test + void should_test_articles_connection_setters_and_equals() { + ArticlesConnection conn = new ArticlesConnection(); + conn.setEdges(Arrays.asList()); + assertNotNull(conn.getEdges()); + + ArticlesConnection conn2 = new ArticlesConnection(); + conn2.setEdges(Arrays.asList()); + assertEquals(conn, conn2); + assertEquals(conn.hashCode(), conn2.hashCode()); + } + + @Test + void should_build_comment_type() { + Comment comment = + Comment.newBuilder() + .id("c1") + .body("comment body") + .createdAt("2023-01-01") + .updatedAt("2023-01-02") + .build(); + + assertEquals("c1", comment.getId()); + assertEquals("comment body", comment.getBody()); + assertEquals("2023-01-01", comment.getCreatedAt()); + assertEquals("2023-01-02", comment.getUpdatedAt()); + assertNotNull(comment.toString()); + } + + @Test + void should_test_comment_setters_and_equals() { + Comment c = new Comment(); + c.setId("id"); + c.setBody("body"); + c.setCreatedAt("cr"); + c.setUpdatedAt("up"); + c.setArticle(null); + c.setAuthor(null); + assertEquals("id", c.getId()); + assertNull(c.getArticle()); + assertNull(c.getAuthor()); + + Comment c2 = Comment.newBuilder().id("id").body("body").createdAt("cr").updatedAt("up").build(); + assertEquals(c, c2); + assertEquals(c.hashCode(), c2.hashCode()); + } + + @Test + void should_build_comment_edge() { + Comment comment = Comment.newBuilder().id("c1").build(); + CommentEdge edge = CommentEdge.newBuilder().node(comment).cursor("cur").build(); + + assertEquals(comment, edge.getNode()); + assertEquals("cur", edge.getCursor()); + assertNotNull(edge.toString()); + } + + @Test + void should_test_comment_edge_setters_and_equals() { + CommentEdge edge = new CommentEdge(); + edge.setNode(Comment.newBuilder().id("x").build()); + edge.setCursor("c"); + assertEquals("c", edge.getCursor()); + + CommentEdge edge2 = CommentEdge.newBuilder().node(edge.getNode()).cursor("c").build(); + assertEquals(edge, edge2); + assertEquals(edge.hashCode(), edge2.hashCode()); + } + + @Test + void should_build_comments_connection() { + Comment comment = Comment.newBuilder().id("c1").build(); + CommentEdge edge = CommentEdge.newBuilder().node(comment).cursor("c").build(); + CommentsConnection conn = CommentsConnection.newBuilder().edges(Arrays.asList(edge)).build(); + + assertEquals(1, conn.getEdges().size()); + assertNotNull(conn.toString()); + } + + @Test + void should_test_comments_connection_setters_and_equals() { + CommentsConnection conn = new CommentsConnection(); + conn.setEdges(Arrays.asList()); + assertNotNull(conn.getEdges()); + + CommentsConnection conn2 = new CommentsConnection(); + conn2.setEdges(Arrays.asList()); + assertEquals(conn, conn2); + assertEquals(conn.hashCode(), conn2.hashCode()); + } + + @Test + void should_build_page_info() { + PageInfo pi = + PageInfo.newBuilder() + .hasNextPage(true) + .hasPreviousPage(false) + .startCursor("start") + .endCursor("end") + .build(); + + assertTrue(pi.getHasNextPage()); + assertFalse(pi.getHasPreviousPage()); + assertEquals("start", pi.getStartCursor()); + assertEquals("end", pi.getEndCursor()); + assertNotNull(pi.toString()); + } + + @Test + void should_test_page_info_setters_and_equals() { + PageInfo pi = new PageInfo(); + pi.setHasNextPage(true); + pi.setHasPreviousPage(true); + pi.setStartCursor("s"); + pi.setEndCursor("e"); + assertTrue(pi.getHasNextPage()); + assertTrue(pi.getHasPreviousPage()); + + PageInfo pi2 = + PageInfo.newBuilder() + .hasNextPage(true) + .hasPreviousPage(true) + .startCursor("s") + .endCursor("e") + .build(); + assertEquals(pi, pi2); + assertEquals(pi.hashCode(), pi2.hashCode()); + } + + @Test + void should_build_page_info_all_args() { + PageInfo pi = new PageInfo("end", true, false, "start"); + assertTrue(pi.getHasNextPage()); + assertFalse(pi.getHasPreviousPage()); + assertEquals("start", pi.getStartCursor()); + assertEquals("end", pi.getEndCursor()); + } + + @Test + void should_build_profile() { + Profile profile = + Profile.newBuilder() + .username("user1") + .bio("my bio") + .image("http://img.com/a.jpg") + .following(true) + .build(); + + assertEquals("user1", profile.getUsername()); + assertEquals("my bio", profile.getBio()); + assertEquals("http://img.com/a.jpg", profile.getImage()); + assertTrue(profile.getFollowing()); + assertNotNull(profile.toString()); + } + + @Test + void should_test_profile_setters_and_equals() { + Profile p = new Profile(); + p.setUsername("u"); + p.setBio("b"); + p.setImage("i"); + p.setFollowing(false); + p.setArticles(null); + p.setFavorites(null); + p.setFeed(null); + assertEquals("u", p.getUsername()); + assertNull(p.getArticles()); + assertNull(p.getFavorites()); + assertNull(p.getFeed()); + + Profile p2 = Profile.newBuilder().username("u").bio("b").image("i").following(false).build(); + assertEquals(p, p2); + assertEquals(p.hashCode(), p2.hashCode()); + } + + @Test + void should_build_profile_payload() { + Profile profile = Profile.newBuilder().username("u").build(); + ProfilePayload payload = ProfilePayload.newBuilder().profile(profile).build(); + + assertEquals(profile, payload.getProfile()); + assertNotNull(payload.toString()); + } + + @Test + void should_test_profile_payload_setters_and_equals() { + ProfilePayload pp = new ProfilePayload(); + pp.setProfile(Profile.newBuilder().username("u").build()); + assertNotNull(pp.getProfile()); + + ProfilePayload pp2 = ProfilePayload.newBuilder().profile(pp.getProfile()).build(); + assertEquals(pp, pp2); + assertEquals(pp.hashCode(), pp2.hashCode()); + } + + @Test + void should_build_user_type() { + User user = User.newBuilder().email("e@t.com").username("user1").token("token123").build(); + + assertEquals("e@t.com", user.getEmail()); + assertEquals("user1", user.getUsername()); + assertEquals("token123", user.getToken()); + assertNotNull(user.toString()); + } + + @Test + void should_test_user_setters_and_equals() { + User u = new User(); + u.setEmail("e"); + u.setUsername("u"); + u.setToken("t"); + u.setProfile(null); + assertEquals("e", u.getEmail()); + assertNull(u.getProfile()); + + User u2 = User.newBuilder().email("e").username("u").token("t").build(); + assertEquals(u, u2); + assertEquals(u.hashCode(), u2.hashCode()); + } + + @Test + void should_build_user_payload() { + User user = User.newBuilder().email("e").build(); + UserPayload payload = UserPayload.newBuilder().user(user).build(); + + assertEquals(user, payload.getUser()); + assertNotNull(payload.toString()); + } + + @Test + void should_test_user_payload_setters_and_equals() { + UserPayload up = new UserPayload(); + up.setUser(User.newBuilder().email("e").build()); + assertNotNull(up.getUser()); + + UserPayload up2 = UserPayload.newBuilder().user(up.getUser()).build(); + assertEquals(up, up2); + assertEquals(up.hashCode(), up2.hashCode()); + } + + @Test + void should_test_user_result_is_interface() { + UserPayload payload = UserPayload.newBuilder().user(User.newBuilder().build()).build(); + assertTrue(payload instanceof UserResult); + + Error error = Error.newBuilder().message("err").build(); + assertTrue(error instanceof UserResult); + } + + @Test + void should_build_deletion_status() { + DeletionStatus status = DeletionStatus.newBuilder().success(true).build(); + assertTrue(status.getSuccess()); + assertNotNull(status.toString()); + } + + @Test + void should_test_deletion_status_setters_and_equals() { + DeletionStatus ds = new DeletionStatus(); + ds.setSuccess(false); + assertFalse(ds.getSuccess()); + + DeletionStatus ds2 = DeletionStatus.newBuilder().success(false).build(); + assertEquals(ds, ds2); + assertEquals(ds.hashCode(), ds2.hashCode()); + } + + @Test + void should_build_create_article_input() { + CreateArticleInput input = + CreateArticleInput.newBuilder() + .title("title") + .description("desc") + .body("body") + .tagList(Arrays.asList("java")) + .build(); + + assertEquals("title", input.getTitle()); + assertEquals("desc", input.getDescription()); + assertEquals("body", input.getBody()); + assertEquals(Arrays.asList("java"), input.getTagList()); + assertNotNull(input.toString()); + } + + @Test + void should_test_create_article_input_setters_and_equals() { + CreateArticleInput i = new CreateArticleInput(); + i.setTitle("t"); + i.setDescription("d"); + i.setBody("b"); + i.setTagList(Arrays.asList("x")); + assertEquals("t", i.getTitle()); + + CreateArticleInput i2 = + CreateArticleInput.newBuilder() + .title("t") + .description("d") + .body("b") + .tagList(Arrays.asList("x")) + .build(); + assertEquals(i, i2); + assertEquals(i.hashCode(), i2.hashCode()); + } + + @Test + void should_build_update_article_input() { + UpdateArticleInput input = + UpdateArticleInput.newBuilder() + .title("new title") + .description("new desc") + .body("new body") + .build(); + + assertEquals("new title", input.getTitle()); + assertEquals("new desc", input.getDescription()); + assertEquals("new body", input.getBody()); + assertNotNull(input.toString()); + } + + @Test + void should_test_update_article_input_setters_and_equals() { + UpdateArticleInput i = new UpdateArticleInput(); + i.setTitle("t"); + i.setDescription("d"); + i.setBody("b"); + assertEquals("t", i.getTitle()); + + UpdateArticleInput i2 = + UpdateArticleInput.newBuilder().title("t").description("d").body("b").build(); + assertEquals(i, i2); + assertEquals(i.hashCode(), i2.hashCode()); + } + + @Test + void should_build_create_user_input() { + CreateUserInput input = + CreateUserInput.newBuilder().email("e@t.com").username("user").password("pass").build(); + + assertEquals("e@t.com", input.getEmail()); + assertEquals("user", input.getUsername()); + assertEquals("pass", input.getPassword()); + assertNotNull(input.toString()); + } + + @Test + void should_test_create_user_input_setters_and_equals() { + CreateUserInput i = new CreateUserInput(); + i.setEmail("e"); + i.setUsername("u"); + i.setPassword("p"); + assertEquals("e", i.getEmail()); + + CreateUserInput i2 = + CreateUserInput.newBuilder().email("e").username("u").password("p").build(); + assertEquals(i, i2); + assertEquals(i.hashCode(), i2.hashCode()); + } + + @Test + void should_build_update_user_input() { + UpdateUserInput input = + UpdateUserInput.newBuilder() + .email("e@t.com") + .username("user") + .password("pass") + .bio("bio") + .image("img") + .build(); + + assertEquals("e@t.com", input.getEmail()); + assertEquals("user", input.getUsername()); + assertEquals("pass", input.getPassword()); + assertEquals("bio", input.getBio()); + assertEquals("img", input.getImage()); + assertNotNull(input.toString()); + } + + @Test + void should_test_update_user_input_setters_and_equals() { + UpdateUserInput i = new UpdateUserInput(); + i.setEmail("e"); + i.setUsername("u"); + i.setPassword("p"); + i.setBio("b"); + i.setImage("i"); + assertEquals("e", i.getEmail()); + + UpdateUserInput i2 = + UpdateUserInput.newBuilder() + .email("e") + .username("u") + .password("p") + .bio("b") + .image("i") + .build(); + assertEquals(i, i2); + assertEquals(i.hashCode(), i2.hashCode()); + } + + @Test + void should_build_error() { + ErrorItem item = ErrorItem.newBuilder().key("field").value(Arrays.asList("err1")).build(); + Error error = Error.newBuilder().message("BAD_REQUEST").errors(Arrays.asList(item)).build(); + + assertEquals("BAD_REQUEST", error.getMessage()); + assertEquals(1, error.getErrors().size()); + assertNotNull(error.toString()); + } + + @Test + void should_test_error_setters_and_equals() { + Error e = new Error(); + e.setMessage("msg"); + e.setErrors(Arrays.asList()); + assertEquals("msg", e.getMessage()); + + Error e2 = Error.newBuilder().message("msg").errors(Arrays.asList()).build(); + assertEquals(e, e2); + assertEquals(e.hashCode(), e2.hashCode()); + } + + @Test + void should_build_error_item() { + List values = Arrays.asList("v1", "v2"); + ErrorItem item = ErrorItem.newBuilder().key("field").value(values).build(); + + assertEquals("field", item.getKey()); + assertEquals(values, item.getValue()); + assertNotNull(item.toString()); + } + + @Test + void should_test_error_item_setters_and_equals() { + ErrorItem ei = new ErrorItem(); + ei.setKey("k"); + ei.setValue(Arrays.asList("v")); + assertEquals("k", ei.getKey()); + + ErrorItem ei2 = ErrorItem.newBuilder().key("k").value(Arrays.asList("v")).build(); + assertEquals(ei, ei2); + assertEquals(ei.hashCode(), ei2.hashCode()); + } + + @Test + void should_build_article_payload() { + Article article = Article.newBuilder().slug("s").build(); + ArticlePayload payload = ArticlePayload.newBuilder().article(article).build(); + + assertEquals(article, payload.getArticle()); + assertNotNull(payload.toString()); + } + + @Test + void should_test_article_payload_setters_and_equals() { + ArticlePayload ap = new ArticlePayload(); + ap.setArticle(Article.newBuilder().slug("s").build()); + assertNotNull(ap.getArticle()); + + ArticlePayload ap2 = ArticlePayload.newBuilder().article(ap.getArticle()).build(); + assertEquals(ap, ap2); + assertEquals(ap.hashCode(), ap2.hashCode()); + } + + @Test + void should_build_comment_payload() { + Comment comment = Comment.newBuilder().id("c1").build(); + CommentPayload payload = CommentPayload.newBuilder().comment(comment).build(); + + assertEquals(comment, payload.getComment()); + assertNotNull(payload.toString()); + } + + @Test + void should_test_comment_payload_setters_and_equals() { + CommentPayload cp = new CommentPayload(); + cp.setComment(Comment.newBuilder().id("c1").build()); + assertNotNull(cp.getComment()); + + CommentPayload cp2 = CommentPayload.newBuilder().comment(cp.getComment()).build(); + assertEquals(cp, cp2); + assertEquals(cp.hashCode(), cp2.hashCode()); + } + + @Test + void should_build_article_with_all_args_constructor() { + Profile author = Profile.newBuilder().username("a").build(); + CommentsConnection cc = CommentsConnection.newBuilder().edges(Arrays.asList()).build(); + Article article = + new Article( + author, "body", cc, "cr", "desc", true, 3, "slug", Arrays.asList("t"), "title", "up"); + + assertEquals(author, article.getAuthor()); + assertEquals("body", article.getBody()); + assertEquals(cc, article.getComments()); + assertEquals("cr", article.getCreatedAt()); + assertEquals("desc", article.getDescription()); + assertTrue(article.getFavorited()); + assertEquals(3, article.getFavoritesCount()); + assertEquals("slug", article.getSlug()); + assertEquals("title", article.getTitle()); + assertEquals("up", article.getUpdatedAt()); + } +}