MTM-5: REST<->GraphQL bookmark parity audit + cross-surface equivalence/E2E tests#185
Merged
devin-ai-integration[bot] merged 1 commit intoJun 26, 2026
Conversation
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final wave of the Reading List epic (MTM-1). This is a verification/parity story, not a redesign — the bookmark feature already exists end-to-end on both surfaces (MTM-2/3/4/6). This PR:
BookmarkCrossSurfaceTest, that drives the same application stack (real repositories + DB) through both REST (MockMvc) and GraphQL (DgsQueryExecutor) and asserts equivalent behavior.Parity audit (MTM-22)
POST /articles/{slug}/bookmark(ArticleBookmarkApi)bookmarkArticle(slug)(ArticleMutation)DELETE /articles/{slug}/bookmarkunbookmarkArticle(slug)GET /articles/bookmarked(offset/limit) →findUserBookmarksQuery.bookmarkedArticles(first/after/last/before)(cursor) →findUserBookmarksWithCursorArticleData.bookmarked(JSONbookmarked)Article.bookmarked: Boolean!article_bookmarks.created_atdesc)WebSecurityConfigrequires auth onGET /articles/bookmarked;@AuthenticationPrincipal)AuthenticationException(SecurityUtil.getCurrentUser().orElseThrow(...))ArticleBookmarksReadServicefilters byuser_id)article_bookmarks; neverfavorited/favoritesCountBoth surfaces resolve the
bookmarkedflag and reading-list membership through the sameArticleQueryService/ArticleBookmarksReadService, so the underlying data, membership, ordering, and privacy rules are shared by construction.D4 (intentional, accepted): REST uses offset/limit; GraphQL uses Relay cursor connections. "Consistent/equivalent" means same underlying data, membership, ordering, auth/privacy, and per-article flag — expressed via each surface's native paging idiom. This divergence is preserved, not "fixed".
Tests (MTM-23)
src/test/java/io/spring/BookmarkCrossSurfaceTest—@SpringBootTest @AutoConfigureMockMvc @Transactional(real stack, rollback per test, matching the repo's transactional test convention). Thearticle_bookmarkstable is created in@BeforeEachbecause thetestprofile runs only Flyway V1 (flyway.target=1), mirroring the existing bookmark infra tests. Ordering is made deterministic by stamping controlledcreated_atvalues after each write.7 cases:
Article.bookmarked=true+ inbookmarkedArticles); remove via REST → gone on GraphQL. And the reverse (bookmark via GraphQL mutation → visible via RESTGET /articles/bookmarkedand RESTbookmarked=true; remove via GraphQL → gone on REST).[a3, a2, a1]).favorited/favoritesCountunchanged on both; favoriting leavesbookmarkedfalse and the article absent from the reading list on both.Verification gate (Java 11) — green
Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/c8f2c351466f4736ad7c012a3dcda9f2