Skip to content

Add tests for the five lowest-coverage modules and enforce the coverage gate - #293

Open
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1786454251-test-coverage
Open

devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1786454251-test-coverage

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Baseline ./gradlew test jacocoTestReport showed 34.1% line coverage (738/2167). The five lowest-coverage hand-written packages were:

package before after
io.spring.graphql (datafetchers/mutations) 1.9% (9/476) 95.0%
io.spring.graphql.exception 3.2% (2/62) 100%
io.spring.application.article (ArticleCommandService) 27.6% (8/29) 100%
io.spring.infrastructure.mybatis (DateTimeHandler) 63.6% (7/11) 100%
io.spring.core.service / io.spring.application (ArticleQueryService cursor paths) 66.7% / 76.4% 100% / 96.8%

21 new test classes, 208 tests total (was 172), all green. ./gradlew build now passes with the 80% JaCoCo gate enabled — line coverage 98.4%, instruction 98.7%, branch 90.2%.

Patterns follow the existing suite: @WebMvcTest + RestAssuredMockMvc for controllers (TagsApiTest), standalone MockMvc with setControllerAdvice for CustomizeExceptionHandler, Mockito @Mock/@InjectMocks for services and GraphQL datafetchers, and DbTestBase (@MybatisTest) for repository/read-service integration (CommentQueryServiceCursorTest).

GraphQL security state is driven directly through the security context, via a shared base class:

// GraphQLTestBase
protected void authenticate(User user) { SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, List.of())); }
protected void anonymous() { ... new AnonymousAuthenticationToken(...) }
@AfterEach void clearSecurityContext() { SecurityContextHolder.clearContext(); }

Negative cases covered throughout: anonymous access to every mutation, non-owner update/delete (NoAuthorizationException), unknown slug/username/comment/relation (ResourceNotFoundException), wrong password / unknown email (InvalidAuthenticationException), registration constraint violations mapped to BAD_REQUEST GraphQL errors, invalid pagination (neither first nor lastIllegalArgumentException), and empty result pages.

Two non-test changes

  1. Bug found while verifying the running app: unauthenticated GraphQL mutations were blocked but surfaced as errorType: INTERNAL, because the datafetchers throw io.spring.graphql.exception.AuthenticationException while the handler only mapped io.spring.api.exception.InvalidAuthenticationException:

    -if (ex instanceof InvalidAuthenticationException) {
    +if (ex instanceof InvalidAuthenticationException || ex instanceof AuthenticationException) {
    +  // AuthenticationException carries no message
       TypedGraphQLError.newBuilder().errorType(ErrorType.UNAUTHENTICATED)
    -      .message(ex.getMessage())
    +      .message(ex.getMessage() == null ? "unauthenticated" : ex.getMessage())
  2. Coverage gate scoping: DGS codegen output (io/spring/graphql/types/**, DgsConstants) is excluded from the JaCoCo report and verification, and lombok.config sets lombok.addLombokGeneratedAnnotation = true so JaCoCo ignores Lombok-generated getters/equals/builders. Without these, the gate measures generated code rather than the code under test.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/9e1ca0417d374553b2b7eb5e5f7c2ea5

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Runtime verification (test-only PR)

Booted the branch (java -jar build/libs/...-SNAPSHOT.jar, Flyway OK, :8080), drove the REST API through the bundled Next.js Conduit frontend, and exercised the DGS layer through GraphiQL. No production code is touched by this PR and no regression attributable to it was found.

Unit suite: ./gradlew test --rerun-tasks -x jacocoTestCoverageVerification -x spotlessJava207 tests, 0 failures, 0 errors, 0 skipped.

Recording of the run

REST golden path (register → article → tags → favorite → comment)

Registered a user, published "PR293 Verification Article" with tag pr293; it appears in the Global Feed and pr293 appears in Popular Tags. Favorite toggled 0 → 1 → 0, comment posted then deleted.

Global feed with new article and pr293 tag

GraphQL (DGS) layer

{ tags } returns the live tag list including the freshly created pr293; articles(first:3) paginates correctly with hasNextPage: true; authenticated createArticle works and the resulting article renders in the REST UI.

GraphQL tags query
Authenticated createArticle mutation

Negative paths
  • Unauthenticated POST /articles → 401
  • GET /articles/no-such-slug-xyz → 404
  • Blank email registration → 422, UI shows "email can't be empty"
  • Duplicate email → 422 {"errors":{"email":["duplicated email"]}}
  • Wrong password → 422 {"message":"invalid email or password"}
Pre-existing issues found (not introduced by this PR)
  1. Unauthenticated createArticle over GraphQL is blocked but returns extensions.errorType: "INTERNAL" instead of UNAUTHENTICATEDGraphQLCustomizeExceptionHandler maps io.spring.api.exception.InvalidAuthenticationException, while the datafetchers throw io.spring.graphql.exception.AuthenticationException. The new handler test therefore covers a branch the live server doesn't hit on this path.

    Unauthenticated mutation errorType INTERNAL

  2. /graphiql renders blank (its CDN React UMD URL now resolves to React 19, which ships no UMD build).

  3. frontend/ bugs: Follow button label never flips (backend follow/unfollow verified correct via API), and a failed login crashes ListErrors.tsx on the {message} response shape.

@devin-ai-integration devin-ai-integration Bot changed the title Add tests for the five lowest-coverage modules Add tests for the five lowest-coverage modules and enforce the coverage gate Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants