Repository navigation
refactor: update keploy sdk version in pom - #11
Merged
Merged
Conversation
11 tasks done
nehagup
pushed a commit
that referenced
this pull request
Sep 18, 2026
…L) (#149) * Add spring-boot-product-catalog sample (Spring Boot + PostgreSQL) A product-catalog REST API demonstrating Keploy replacing hand-written API tests. Ships a committed test set of 57 cases and 190 Postgres mocks under keploy/products-crud/, plus an app-only docker-compose.keploy.yml that replays the full suite green with no database present. - Full CRUD, category filters, inventory summary, stock adjustment - Multi-stage Dockerfile (Temurin 21), docker-compose for record/replay - Paths-scoped CI workflow: Maven build + end-to-end smoke test - Listed as sample #11 in the root README Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Align 404 message with the recorded contract The not-found message was 'Product not found with id: <id>', but the committed GET/PUT/DELETE 404 tests assert body.message as 'Product <id> not found' (only header.Date is noise). Restore the recorded wording so the suite replays green as the README states, and add a guard comment so the contract-sensitive string isn't reworded again. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Address review: healthcheck, seed coverage, fail-loud readiness, atomic stock - docker-compose.yml: add an app healthcheck (Actuator /health via curl) so 'docker compose up --wait' blocks until the app is actually ready. Left the DB-absent docker-compose.keploy.yml alone, where a db health check would fail. - seed.sh: exercise GET /summary and PATCH /{id}/stock (409 + 404) so the README's 're-record picks them up' claim holds; fail loudly if the app never becomes ready instead of silently proceeding and exiting 0. - ProductService/ProductRepository: make adjustStock an atomic guarded UPDATE so concurrent PATCHes can't lose an update or bypass the >= 0 guard. Scoped to the (un-recorded) stock path via a @Modifying query rather than @Version, which would change every INSERT/UPDATE and break the committed Postgres mocks. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Use Actuator readiness probe for the app healthcheck The aggregate /actuator/health endpoint runs the JDBC db indicator on every poll (interval: 5s), which Keploy captures as extra Postgres mocks during keploy record and bloats mocks.yaml. The readiness group (already enabled via management.endpoint.health.probes.enabled) doesn't touch the DB, so it still gives 'docker compose up --wait' a real ready-to-serve signal without polluting the recorded capture. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Address review: non-root image, workflow ordering, config + entity cleanup - Dockerfile: run the JVM as a non-root 'spring' user. - workflow: smoke now 'needs: build', so it doesn't rebuild and burn its timeout when the build has already failed. - keploy.yml: drop the unused mongoPassword default (this app is Postgres-only). - Product: remove the setCreatedAt setter; createdAt is @PrePersist-assigned on an updatable=false column, so a setter is silently dropped on save and misleads callers. - seed.sh: fail loudly in create() when the POST fails or returns no id, instead of appending an empty id and corrupting later phases. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Adopt classic spring-boot-starter-web / -test; harden seed.sh with set -e - pom.xml: use spring-boot-starter-web and the aggregate spring-boot-starter-test instead of the 4.x modular starters. Both resolve on 4.1.0 (verified), but the classic starters are the conventional choice for a sample; -web still brings embedded Tomcat (tomcat-embed-core 11.0.22) + spring-webmvc. - seed.sh: enable 'set -e' so a failure in any phase (not just create()) aborts loudly. Hardened id_of to 'grep -m1' (no downstream head -> no SIGPIPE) so the pipeline stays clean under -e + pipefail, and guarded the id extraction with '|| true' to preserve the friendly no-id error. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Address review: 404-vs-409 on stock race, type-mismatch handler, drop dead test - ProductService.adjustStock: on a 0-row update, re-check existsById so a product deleted concurrently between findById and the UPDATE returns 404, not a misleading 409. - GlobalExceptionHandler: handle MethodArgumentTypeMismatchException so a non-numeric path variable (/api/products/abc) returns the uniform {status,error,message} 400 shape instead of Spring's default ProblemDetail. - Remove the placeholder @SpringBootTest (it needs a live DB, so CI skipped it and it was dead code) and the now-unused spring-boot-starter-test dependency. The Keploy recorded suite is this sample's test coverage. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Re-record suite: cover /summary + /{id}/stock, deterministic ordering, name length=120 Regenerates the Keploy test set against the updated app so all committed changes that touch recorded state land together and replay stays green: - Coverage: the suite now exercises all seven endpoints — adds GET /api/products/summary and PATCH /api/products/{id}/stock (restock, ship, 409 over-decrement, 404), closing the previously documented gap. 57 -> 63 test cases, 190 -> 214 Postgres mocks. - Determinism: list/filter/summary queries now use an explicit ORDER BY id (findByCategoryIgnoreCaseOrderByIdAsc / findAll(Sort.by("id"))), so re-records reproduce a stable row order instead of Postgres heap order. - Schema: name column is length = 120, matching the @SiZe(max = 120) validation constraint; the regenerated CREATE-table mock reflects it, so the DB-absent replay still starts clean. - README updated for the new counts and full-coverage note. Verified green both ways: 'keploy test' (real DB) and the DB-absent docker-compose.keploy.yml replay both report Total: 63 Passed: 63 Failed: 0. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Housekeeping: ignoreOrdering=false (ordering now deterministic), refresh seed.sh comment - keploy.yml: set ignoreOrdering to false. List/filter/summary responses are now sorted by id (and the summary's category rollup + low-stock list are built in that stable order), so there is no non-deterministic ordering left for Keploy to tolerate. Verified the DB-absent replay still passes 63/63 under strict ordering. - seed.sh: update the stale header comment (~50 -> ~60+ cases) and mention the summary, stock, and 409 paths it now drives. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Keep mocks.yaml committed: drop auto-added /*/mocks.yaml from keploy/.gitignore The keploy tooling re-adds this ignore on each run, but this sample commits mocks.yaml on purpose (it's what makes the DB-absent replay self-contained), so the ignore is removed to keep the config coherent. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Handle HttpMessageNotReadableException for the uniform 400 error shape A malformed/unparseable JSON body (e.g. {"name": }, truncated payload) threw HttpMessageNotReadableException, which bypassed the advice and fell through to Spring's default error shape. Add a handler so it returns the same {status, error, message} 400 as every other error path. Not exercised by the recorded suite (seed.sh always sends well-formed JSON), so it doesn't affect the committed replay. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Complete the uniform error contract + fix README record metadata Finding 1 (415): add HttpMediaTypeNotSupportedException handler so a missing/wrong Content-Type returns the uniform {status,error,message} 415 instead of Spring's default. Also close the rest of the same gap pattern in one pass so every error path is uniform: - HttpRequestMethodNotSupportedException -> 405 (e.g. POST /api/products/{id}) - NoResourceFoundException -> 404 (unknown path) - a catch-all Exception handler -> 500 (logged, generic message) as the backstop, so no exception can ever fall through to Spring's default error shape again. None of these paths are in the recorded suite (specific 404/409/400 handlers still win for the recorded traffic), so the committed replay is unaffected. Finding 2: README keploy record --metadata description now matches the committed config.yaml (adds summary + stock + validation). Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Handle HttpMediaTypeNotAcceptableException as body-less 406 The catch-all Exception handler from e9550a0 was swallowing HttpMediaTypeNotAcceptableException (thrown when Spring can't render an error-response body in the client's requested Accept format, e.g. an error hit with Accept: application/xml), turning a correct 406 into a 500. Add an explicit handler that returns a body-less 406 — the body is exactly what can't be negotiated, so it must not carry one, otherwise it recurses back into the catch-all. Recorded suite unaffected (all 63 cases send Accept: */*, which accepts JSON, so replay never reaches this path). Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Give custom exceptions a @ResponseStatus fallback so unwritable-body errors resolve correctly When a client sends an un-negotiable Accept (e.g. application/xml) on an error path, the @ExceptionHandler runs and builds its JSON body, but the write fails with HttpMediaTypeNotAcceptableException. Spring does not re-enter @ExceptionHandler resolution for an exception raised inside exception handling, so the original ResourceNotFoundException / InsufficientStockException propagated to the container as a 500. Spring's built-in exceptions implement ErrorResponse and self-resolve body-lessly in this case; our custom RuntimeExceptions had no fallback. Annotating them with @ResponseStatus lets ResponseStatusExceptionResolver answer with the right status (404 / 409) body-lessly. The @ExceptionHandler still wins for normal requests, so the uniform {status,error,message} body — and the recorded 63-case suite (all Accept: */*) — is unchanged. Also drop handleNotAcceptable: it never fired (the write failure isn't re-entrant), and success-path 406 is already produced by Spring's built-in negotiation. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> --------- Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
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.
Pull Request Template
Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Additional Context (Please include any Screenshots/gifs if relevant)
...
Checklist: