Repository: nanotaboada/java.samples.spring.boot
Pull request #183: refactor: migrate validations from services to controllers
Author: login: nanotaboada
Created: 2025-04-19T18:35:19Z | Merged: 2025-04-19T19:17:29Z



<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/nanotaboada/java.samples.spring.boot/183)
<!-- Reviewable:end -->


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Refactor**
  - Simplified and unified response handling in the books API, with improved validation and clearer HTTP status codes.
  - Updated and streamlined service logic for creating, updating, and deleting books by removing redundant validation steps.
  - Improved naming consistency and clarity in test utility classes and test methods.
- **Bug Fixes**
  - Corrected API documentation and summaries to accurately reference ISBN where appropriate.
- **Tests**
  - Updated tests to align with revised service logic, removed obsolete validation checks, and improved test method naming.
- **Style**
  - Improved naming conventions for classes and methods in test utilities for better readability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6d11e49..c7306a1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,7 +5,8 @@
   "editor.detectIndentation": false,
   "editor.formatOnSave": true,
   "[java]": {
-    "editor.defaultFormatter": "redhat.java"
+    "editor.defaultFormatter": "redhat.java",
+    "editor.inlayHints.enabled": "off"
   },
   "java.configuration.updateBuildConfiguration": "automatic",
   "java.compile.nullAnalysis.mode": "automatic",
diff --git a/assets/images/structure.svg b/assets/images/structure.svg
index d605c67..ea4f57f 100644
--- a/assets/images/structure.svg
+++ b/assets/images/structure.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:lucid="lucid" width="962" height="722"><g transform="translate(1681 480.9999999999999)" lucid:page-tab-id="0_0"><path d="M-1600-474a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#a" transform="matrix(1,0,0,1,-1588,-467.9999999999999) translate(49.32378472222223 32.29166666666667)"/><path d="M-1600-314a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#b" transform="matrix(1,0,0,1,-1588,-307.9999999999999) translate(65.32638888888889 32.29166666666667)"/><path d="M-1600-154a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#c" transform="matrix(1,0,0,1,-1588,-147.9999999999999) translate(43.98958333333334 32.29166666666667)"/><path d="M-1280-154a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#d" transform="matrix(1,0,0,1,-1268,-147.99999999999983) translate(49.32378472222223 32.29166666666667)"/><path d="M-960-154a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fcce14" stroke-width="2" fill="#fcfcca"/><use xlink:href="#e" transform="matrix(1,0,0,1,-948,-147.99999999999972) translate(49.323784722222214 32.29166666666667)"/><use xlink:href="#f" transform="matrix(1,0,0,1,-948,-147.99999999999972) translate(124.00260416666667 32.29166666666667)"/><path d="M-1480-162v-58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-161h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1480-235.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1480-322v-58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-321h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1480-395.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-960-314a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#54c45e" stroke-width="2" fill="#c3f7c8"/><use xlink:href="#g" transform="matrix(1,0,0,1,-948,-307.9999999999998) translate(86.66319444444444 32.60416666666667)"/><path d="M-1358-120h58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1357.97-119.03h-1.03v-1.94h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1284.24-120l-14.26 4.64v-9.28z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-962-120h-58.5" stroke="#fcce14" stroke-width="2" fill="none"/><path d="M-961-119.03h-1.03v-1.94h1.03z" stroke="#fcce14" stroke-width=".05" fill="#fcce14"/><path d="M-1035.76-120l14.26-4.64v9.28z" stroke="#fcce14" stroke-width="2" fill="#fcce14"/><path d="M-1600 6a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#h" transform="matrix(1,0,0,1,-1588,12) translate(75.99479166666666 32.29166666666667)"/><path d="M-1480-2v-58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-1h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1480-75.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1280 6a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#i" transform="matrix(1,0,0,1,-1268,11.999999999999943) translate(6.650173611111114 32.60416666666667)"/><use xlink:href="#j" transform="matrix(1,0,0,1,-1268,11.999999999999943) translate(91.99739583333334 32.60416666666667)"/><path d="M-1280-314a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#i" transform="matrix(1,0,0,1,-1268,-307.9999999999998) translate(11.984375 32.60416666666667)"/><use xlink:href="#k" transform="matrix(1,0,0,1,-1268,-307.9999999999998) translate(97.33159722222223 32.60416666666667)"/><path d="M-1282-280h-58.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1281-279.02h-1.03v-1.96h1.03z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1355.76-280l14.26-4.63v9.26z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-1600 166a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#l" transform="matrix(1,0,0,1,-1588,171.9999999999999) translate(33.32118055555554 32.60416666666667)"/><use xlink:href="#m" transform="matrix(1,0,0,1,-1588,171.9999999999999) translate(118.66840277777777 32.60416666666667)"/><path d="M-1480 158V99.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1479.03 159h-1.94v-1.03h1.94z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1480 84.24l4.63 14.26h-9.27z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-1280-474a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fcce14" stroke-width="2" fill="#fcfcca"/><use xlink:href="#n" transform="matrix(1,0,0,1,-1268,-467.9999999999999) translate(59.9921875 32.29166666666667)"/><path d="M-1282-440h-58.5" stroke="#fcce14" stroke-width="2" fill="none"/><path d="M-1281-439.02h-1.03v-1.96h1.03z" stroke="#fcce14" stroke-width=".05" fill="#fcce14"/><path d="M-1355.76-440l14.26-4.63v9.26z" stroke="#fcce14" stroke-width="2" fill="#fcce14"/><path d="M-840-162v-9.75m0-9.75V-201m0-9.75v-9.75" stroke="#fcce14" stroke-width="2" fill="none"/><path d="M-839.02-161H-841v-1.03h1.96z" stroke="#fcce14" stroke-width=".05" fill="#fcce14"/><path d="M-840-235.76l4.63 14.26h-9.26z" stroke="#fcce14" stroke-width="2" fill="#fcce14"/><path d="M-1602 40h-72a6 6 0 0 1-6-6v-148a6 6 0 0 1 6-6h54.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1601 40.98h-1.03v-1.95h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1604.24-120l-14.26 4.64v-9.28z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1602 40h-72a6 6 0 0 1-6-6v-308a6 6 0 0 1 6-6h54.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1601 40.98h-1.03v-1.95h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1604.24-280l-14.26 4.63v-9.26z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1602 40h-72a6 6 0 0 1-6-6v-468a6 6 0 0 1 6-6h54.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1601 40.98h-1.03v-1.95h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1604.24-440l-14.26 4.63v-9.26z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1282 40h-58.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1281 40.98h-1.03v-1.95h1.03z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1355.76 40l14.26-4.63v9.27z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-1480-2v-32a6 6 0 0 1 6-6h308a6 6 0 0 0 6-6v-14.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-1h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1160-75.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><defs><path d="M631 20c-350 0-501-215-501-562 0-355 162-560 502-560 250 0 399 118 446 323l-192 14c-23-124-109-196-262-196-242 0-305 171-305 415 1 245 61 427 304 427 151 0 248-77 267-215l190 12C1039-107 883 20 631 20" id="o"/><path d="M615-1102c343 0 484 203 482 560-1 347-147 562-488 562-336 0-475-219-479-562-4-349 156-560 485-560zm-8 989c240 0 301-180 301-429 0-245-55-427-290-427-236 0-299 181-299 427 0 243 61 429 288 429" id="p"/><path d="M706-1102c241 0 344 136 343 381V0H868v-695c1-168-57-273-220-268-190 6-283 138-283 336V0H185c-3-360 6-732-6-1082h170c4 54 7 126 8 185h3c63-121 164-204 346-205" id="q"/><path d="M682 16c-209 0-323-80-324-285v-671H190v-142h170l58-282h120v282h432v142H538v652c2 114 60 155 182 155 106 0 209-16 297-34v137C921-4 806 16 682 16" id="r"/><path d="M839-1102c70 0 148 7 206 17v167c-112-18-268-36-363 15-129 69-208 203-208 395V0H294c-10-367 32-789-52-1082h171c21 75 41 161 48 250h5c67-152 152-270 373-270" id="s"/><path d="M736-142h380V0H134v-142h422v-1200H267v-142h469v1342" id="t"/><path d="M617-1102c355 0 481 238 477 599H322c5 222 84 388 301 388 144 0 244-59 284-166l158 45C1002-72 854 20 623 20c-342 0-490-220-490-568 0-346 151-554 484-554zm291 461c-18-192-90-328-289-328-194 0-287 128-295 328h584" id="u"/><path d="M873-819c-18-114-119-146-250-146-163 0-245 50-245 151 0 151 170 148 294 185 182 54 388 94 388 320 0 240-189 325-439 329-245 4-410-69-454-268l159-31c24 133 136 168 295 165 144-2 270-31 270-171 0-164-195-160-331-202-167-52-350-87-350-299 0-218 173-315 413-313 220 2 373 77 412 260" id="v"/><g id="a"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#o"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#v"/></g><path d="M715 0H502L69-1082h202c94 253 197 500 285 758 19 57 36 126 52 183 96-335 234-626 350-941h201" id="w"/><path d="M745-142h380V0H143v-142h422v-798H246v-142h499v940zM545-1292v-192h200v192H545" id="x"/><g id="b"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#w"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#o"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#v"/></g><path d="M698-1104c312 3 392 244 392 558 0 315-82 566-392 566-169 0-277-65-331-184h-5c8 188 2 394 4 589H185V-858c0-76-1-156-6-224h175c6 52 9 120 10 178h4c58-122 150-202 330-200zm-49 991c225 0 255-203 255-433 0-225-32-419-253-419-236 0-285 192-285 441 0 237 53 411 283 411" id="y"/><g id="c"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,117.35243055555554,0)" xlink:href="#v"/></g><path d="M1034 0L896-382H333L196 0H0l510-1349h217L1228 0h-194zM847-531c-77-225-157-447-231-674-69 231-154 451-232 674h463" id="z"/><path d="M1000-272c3 95 12 159 101 161 21 0 41-3 59-7V-6c-44 10-86 16-139 16-141 2-191-84-197-217h-6C748-76 648 20 446 20c-207 0-318-120-318-322 0-266 194-348 454-354l236-4c12-191-40-305-222-305-140 0-220 47-232 172l-188-17c33-204 181-292 423-292 255 0 401 118 401 364v466zm-683-27c0 109 63 184 175 182 166-3 259-96 306-217 24-65 20-120 20-200-232 7-501-28-501 235" id="A"/><g id="d"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#o"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#A"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#q"/></g><path d="M614-1226c-167 1-283 53-283 213 0 183 186 193 334 234 230 63 463 120 463 409 0 286-219 387-518 390C309 23 131-98 79-338l185-37c34 165 149 248 351 246 184-2 324-58 324-238 0-203-207-221-372-266-210-57-422-111-422-377 0-267 201-356 470-360 279-5 430 101 480 324l-188 33c-28-141-121-215-293-213" id="B"/><path d="M1048-32c-2 300-135 456-433 456-222-1-358-88-400-267l184-25c22 99 100 157 222 156 184-2 248-125 248-315 0-64 3-133-2-194C807-100 706-13 524-12c-306 0-381-228-381-537 0-318 85-550 400-550 164 0 271 83 325 202h3c1-60 3-134 12-185h171c-13 339-4 702-6 1050zM585-145c210-8 284-178 284-406 0-192-52-331-177-392-33-16-69-22-104-22-223 2-259 184-259 414 0 229 31 415 256 406" id="C"/><g id="e"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#C"/></g><path d="M802-711c201 25 350 118 350 331C1152-95 921 0 634 0H162v-1349c401 9 908-74 908 327 0 184-111 275-268 311zm-224-69c174-2 300-51 300-218 0-163-124-198-302-198H353v416h225zM353-153c281-2 612 44 606-244-5-271-329-233-606-234v478" id="D"/><g id="f"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#D"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#r"/></g><g id="g"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#r"/></g><path d="M904-1102c199 0 220 177 220 381V0H956v-686c-3-114 0-215-60-264-70-33-125-4-158 71-26 56-39 140-39 252V0H531v-686c-3-114-1-215-61-264-78-41-136 24-157 84-24 69-39 159-39 259V0H105c-3-360 6-732-6-1082h149c6 50 3 123 8 175 36-100 83-195 216-195 135 0 166 79 196 196 42-105 93-196 236-196" id="E"/><path d="M865-914c-3-187-2-380-2-570h180v1261c0 76 1 155 6 223H877c-8-49-9-116-10-174h-5C801-44 708 26 530 26c-135 0-234-46-297-139s-95-232-95-419c0-377 131-566 392-566 176 0 271 63 335 184zm-286-51c-222 0-255 197-255 427 0 229 31 425 253 425 237 0 286-195 286-441 0-238-52-411-284-411" id="F"/><g id="h"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#E"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#F"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#v"/></g><path d="M587-135c154 0 209-115 209-281v-777H485v-156h501v929C982-148 857 20 586 20c-254 0-368-136-410-370l187-31c24 134 77 246 224 246" id="G"/><path d="M914 0L548-499l-132 98V0H236v-1484h180v927l475-525h211L663-617 1125 0H914" id="H"/><g id="i"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#G"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#A"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#H"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#A"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#A"/></g><path d="M622-1349c296 4 497 117 497 404 0 282-193 424-485 431H353V0H162v-1349h460zm-15 684c195-3 320-88 320-277 0-184-129-255-328-254H353v531h254" id="I"/><g id="j"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#I"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#o"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#u"/></g><path d="M713 0H515L10-1349h201C345-955 498-579 615-168c116-411 269-787 402-1181h201" id="J"/><g id="k"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#J"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#A"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#F"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#A"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#q"/></g><path d="M390 276c167 0 266-82 266-251v-965H249v-142h587V28c-4 270-165 397-431 397-107 0-208-16-288-43V242c80 18 173 34 273 34zm246-1568v-192h200v192H636" id="K"/><g id="l"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#I"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#K"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#o"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#r"/></g><path d="M237 0v-1349h191v1193h672V0H237" id="L"/><path d="M365-904c58-129 161-200 334-200 130 0 228 46 293 138s98 233 98 420c0 189-34 331-102 425S824 20 698 20c-170 0-275-64-336-184 0 55-3 116-9 164H179c5-68 6-147 6-223v-1261h180c-2 193 4 394-4 580h4zm283 791c221 0 256-197 256-427 0-229-34-425-254-425-236 0-285 195-285 441 0 237 53 411 283 411" id="M"/><g id="m"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#L"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#E"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#M"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#H"/></g><path d="M473-1349c438-1 655 221 652 661C1122-268 945-8 532 0H162v-1349h311zm42 1193c308-4 416-205 418-532 2-330-131-509-459-505H353v1037h162" id="N"/><g id="n"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#C"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#N"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#o"/></g></defs></g></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:lucid="lucid" width="962" height="882"><g transform="translate(1681 641)" lucid:page-tab-id="0_0"><path d="M-1600-474a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#a" transform="matrix(1,0,0,1,-1588,-467.9999999999999) translate(49.32378472222223 32.29166666666667)"/><path d="M-1600-314a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#b" transform="matrix(1,0,0,1,-1588,-307.9999999999999) translate(65.32638888888889 32.29166666666667)"/><path d="M-1600-154a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#c" transform="matrix(1,0,0,1,-1588,-147.9999999999999) translate(43.98958333333334 32.29166666666667)"/><path d="M-1280-154a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#d" transform="matrix(1,0,0,1,-1268,-147.99999999999983) translate(49.32378472222223 32.29166666666667)"/><path d="M-960-154a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fcce14" stroke-width="2" fill="#fcfcca"/><use xlink:href="#e" transform="matrix(1,0,0,1,-948,-147.99999999999972) translate(49.323784722222214 32.29166666666667)"/><use xlink:href="#f" transform="matrix(1,0,0,1,-948,-147.99999999999972) translate(124.00260416666667 32.29166666666667)"/><path d="M-1480-162v-58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-161h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1480-235.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1480-322v-58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-321h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1480-395.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-960-314a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#54c45e" stroke-width="2" fill="#c3f7c8"/><use xlink:href="#g" transform="matrix(1,0,0,1,-948,-307.9999999999998) translate(86.66319444444444 32.60416666666667)"/><path d="M-1358-120h58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1357.97-119.03h-1.03v-1.94h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1284.24-120l-14.26 4.64v-9.28z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-962-120h-58.5" stroke="#fcce14" stroke-width="2" fill="none"/><path d="M-961-119.03h-1.03v-1.94h1.03z" stroke="#fcce14" stroke-width=".05" fill="#fcce14"/><path d="M-1035.76-120l14.26-4.64v9.28z" stroke="#fcce14" stroke-width="2" fill="#fcce14"/><path d="M-1600 6a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#6db1ff" stroke-width="2" fill="#cfe4ff"/><use xlink:href="#h" transform="matrix(1,0,0,1,-1588,12) translate(75.99479166666666 32.29166666666667)"/><path d="M-1480-2v-58.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-1h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1480-75.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1280 6a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#i" transform="matrix(1,0,0,1,-1268,11.999999999999943) translate(6.650173611111114 32.60416666666667)"/><use xlink:href="#j" transform="matrix(1,0,0,1,-1268,11.999999999999943) translate(91.99739583333334 32.60416666666667)"/><path d="M-1280-474a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#i" transform="matrix(1,0,0,1,-1268,-467.9999999999999) translate(11.984375 32.60416666666667)"/><use xlink:href="#k" transform="matrix(1,0,0,1,-1268,-467.9999999999999) translate(97.33159722222223 32.60416666666667)"/><path d="M-1282-440h-58.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1281-439.02h-1.03v-1.96h1.03z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1355.76-440l14.26-4.63v9.26z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-1600 166a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#l" transform="matrix(1,0,0,1,-1588,171.9999999999999) translate(33.32118055555554 32.60416666666667)"/><use xlink:href="#m" transform="matrix(1,0,0,1,-1588,171.9999999999999) translate(118.66840277777777 32.60416666666667)"/><path d="M-1480 158V99.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1479.03 159h-1.94v-1.03h1.94z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1480 84.24l4.63 14.26h-9.27z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-1600-634a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#n" transform="matrix(1,0,0,1,-1588,-628) translate(59.9921875 32.29166666666667)"/><path d="M-1482-558v58.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1481.03-557.97h-1.94V-559h1.94z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1482-484.24l-4.63-14.26h9.27z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-840-162v-9.75m0-9.75V-201m0-9.75v-9.75" stroke="#fcce14" stroke-width="2" fill="none"/><path d="M-839.02-161H-841v-1.03h1.96z" stroke="#fcce14" stroke-width=".05" fill="#fcce14"/><path d="M-840-235.76l4.63 14.26h-9.26z" stroke="#fcce14" stroke-width="2" fill="#fcce14"/><path d="M-1602 40h-72a6 6 0 0 1-6-6v-148a6 6 0 0 1 6-6h54.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1601 40.98h-1.03v-1.95h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1604.24-120l-14.26 4.64v-9.28z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1602 40h-72a6 6 0 0 1-6-6v-308a6 6 0 0 1 6-6h54.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1601 40.98h-1.03v-1.95h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1604.24-280l-14.26 4.63v-9.26z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1602 40h-72a6 6 0 0 1-6-6v-468a6 6 0 0 1 6-6h54.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1601 40.98h-1.03v-1.95h1.03z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1604.24-440l-14.26 4.63v-9.26z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1282 40h-58.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1281 40.98h-1.03v-1.95h1.03z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1355.76 40l14.26-4.63v9.27z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><path d="M-1480-2v-32a6 6 0 0 1 6-6h308a6 6 0 0 0 6-6v-14.5" stroke="#6db1ff" stroke-width="2" fill="none"/><path d="M-1479.03-1h-1.94v-1.03h1.94z" stroke="#6db1ff" stroke-width=".05" fill="#6db1ff"/><path d="M-1160-75.76l4.63 14.26h-9.27z" stroke="#6db1ff" stroke-width="2" fill="#6db1ff"/><path d="M-1280-314a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6h-228a6 6 0 0 1-6-6z" stroke="#fe7070" stroke-width="2" fill="#ffd9d9"/><use xlink:href="#o" transform="matrix(1,0,0,1,-1268,-307.99999999999966) translate(49.32378472222223 32.60416666666667)"/><path d="M-1282-280h-58.5" stroke="#fe7070" stroke-width="2" fill="none"/><path d="M-1281-279.02h-1.03v-1.96h1.03z" stroke="#fe7070" stroke-width=".05" fill="#fe7070"/><path d="M-1355.76-280l14.26-4.63v9.26z" stroke="#fe7070" stroke-width="2" fill="#fe7070"/><defs><path d="M631 20c-350 0-501-215-501-562 0-355 162-560 502-560 250 0 399 118 446 323l-192 14c-23-124-109-196-262-196-242 0-305 171-305 415 1 245 61 427 304 427 151 0 248-77 267-215l190 12C1039-107 883 20 631 20" id="p"/><path d="M615-1102c343 0 484 203 482 560-1 347-147 562-488 562-336 0-475-219-479-562-4-349 156-560 485-560zm-8 989c240 0 301-180 301-429 0-245-55-427-290-427-236 0-299 181-299 427 0 243 61 429 288 429" id="q"/><path d="M706-1102c241 0 344 136 343 381V0H868v-695c1-168-57-273-220-268-190 6-283 138-283 336V0H185c-3-360 6-732-6-1082h170c4 54 7 126 8 185h3c63-121 164-204 346-205" id="r"/><path d="M682 16c-209 0-323-80-324-285v-671H190v-142h170l58-282h120v282h432v142H538v652c2 114 60 155 182 155 106 0 209-16 297-34v137C921-4 806 16 682 16" id="s"/><path d="M839-1102c70 0 148 7 206 17v167c-112-18-268-36-363 15-129 69-208 203-208 395V0H294c-10-367 32-789-52-1082h171c21 75 41 161 48 250h5c67-152 152-270 373-270" id="t"/><path d="M736-142h380V0H134v-142h422v-1200H267v-142h469v1342" id="u"/><path d="M617-1102c355 0 481 238 477 599H322c5 222 84 388 301 388 144 0 244-59 284-166l158 45C1002-72 854 20 623 20c-342 0-490-220-490-568 0-346 151-554 484-554zm291 461c-18-192-90-328-289-328-194 0-287 128-295 328h584" id="v"/><path d="M873-819c-18-114-119-146-250-146-163 0-245 50-245 151 0 151 170 148 294 185 182 54 388 94 388 320 0 240-189 325-439 329-245 4-410-69-454-268l159-31c24 133 136 168 295 165 144-2 270-31 270-171 0-164-195-160-331-202-167-52-350-87-350-299 0-218 173-315 413-313 220 2 373 77 412 260" id="w"/><g id="a"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#w"/></g><path d="M715 0H502L69-1082h202c94 253 197 500 285 758 19 57 36 126 52 183 96-335 234-626 350-941h201" id="x"/><path d="M745-142h380V0H143v-142h422v-798H246v-142h499v940zM545-1292v-192h200v192H545" id="y"/><g id="b"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#w"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#x"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#w"/></g><path d="M698-1104c312 3 392 244 392 558 0 315-82 566-392 566-169 0-277-65-331-184h-5c8 188 2 394 4 589H185V-858c0-76-1-156-6-224h175c6 52 9 120 10 178h4c58-122 150-202 330-200zm-49 991c225 0 255-203 255-433 0-225-32-419-253-419-236 0-285 192-285 441 0 237 53 411 283 411" id="z"/><g id="c"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#w"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,117.35243055555554,0)" xlink:href="#w"/></g><path d="M1034 0L896-382H333L196 0H0l510-1349h217L1228 0h-194zM847-531c-77-225-157-447-231-674-69 231-154 451-232 674h463" id="A"/><path d="M1000-272c3 95 12 159 101 161 21 0 41-3 59-7V-6c-44 10-86 16-139 16-141 2-191-84-197-217h-6C748-76 648 20 446 20c-207 0-318-120-318-322 0-266 194-348 454-354l236-4c12-191-40-305-222-305-140 0-220 47-232 172l-188-17c33-204 181-292 423-292 255 0 401 118 401 364v466zm-683-27c0 109 63 184 175 182 166-3 259-96 306-217 24-65 20-120 20-200-232 7-501-28-501 235" id="B"/><g id="d"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#A"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#r"/></g><path d="M614-1226c-167 1-283 53-283 213 0 183 186 193 334 234 230 63 463 120 463 409 0 286-219 387-518 390C309 23 131-98 79-338l185-37c34 165 149 248 351 246 184-2 324-58 324-238 0-203-207-221-372-266-210-57-422-111-422-377 0-267 201-356 470-360 279-5 430 101 480 324l-188 33c-28-141-121-215-293-213" id="C"/><path d="M1048-32c-2 300-135 456-433 456-222-1-358-88-400-267l184-25c22 99 100 157 222 156 184-2 248-125 248-315 0-64 3-133-2-194C807-100 706-13 524-12c-306 0-381-228-381-537 0-318 85-550 400-550 164 0 271 83 325 202h3c1-60 3-134 12-185h171c-13 339-4 702-6 1050zM585-145c210-8 284-178 284-406 0-192-52-331-177-392-33-16-69-22-104-22-223 2-259 184-259 414 0 229 31 415 256 406" id="D"/><g id="e"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#C"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#D"/></g><path d="M802-711c201 25 350 118 350 331C1152-95 921 0 634 0H162v-1349c401 9 908-74 908 327 0 184-111 275-268 311zm-224-69c174-2 300-51 300-218 0-163-124-198-302-198H353v416h225zM353-153c281-2 612 44 606-244-5-271-329-233-606-234v478" id="E"/><g id="f"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#E"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#s"/></g><g id="g"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#w"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#s"/></g><path d="M904-1102c199 0 220 177 220 381V0H956v-686c-3-114 0-215-60-264-70-33-125-4-158 71-26 56-39 140-39 252V0H531v-686c-3-114-1-215-61-264-78-41-136 24-157 84-24 69-39 159-39 259V0H105c-3-360 6-732-6-1082h149c6 50 3 123 8 175 36-100 83-195 216-195 135 0 166 79 196 196 42-105 93-196 236-196" id="F"/><path d="M865-914c-3-187-2-380-2-570h180v1261c0 76 1 155 6 223H877c-8-49-9-116-10-174h-5C801-44 708 26 530 26c-135 0-234-46-297-139s-95-232-95-419c0-377 131-566 392-566 176 0 271 63 335 184zm-286-51c-222 0-255 197-255 427 0 229 31 425 253 425 237 0 286-195 286-441 0-238-52-411-284-411" id="G"/><g id="h"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#F"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#G"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#w"/></g><path d="M587-135c154 0 209-115 209-281v-777H485v-156h501v929C982-148 857 20 586 20c-254 0-368-136-410-370l187-31c24 134 77 246 224 246" id="H"/><path d="M914 0L548-499l-132 98V0H236v-1484h180v927l475-525h211L663-617 1125 0H914" id="I"/><g id="i"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#H"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#I"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#B"/></g><path d="M622-1349c296 4 497 117 497 404 0 282-193 424-485 431H353V0H162v-1349h460zm-15 684c195-3 320-88 320-277 0-184-129-255-328-254H353v531h254" id="J"/><g id="j"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#J"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#w"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#w"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#v"/></g><path d="M713 0H515L10-1349h201C345-955 498-579 615-168c116-411 269-787 402-1181h201" id="K"/><g id="k"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#K"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#G"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#s"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#r"/></g><path d="M390 276c167 0 266-82 266-251v-965H249v-142h587V28c-4 270-165 397-431 397-107 0-208-16-288-43V242c80 18 173 34 273 34zm246-1568v-192h200v192H636" id="L"/><g id="l"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#J"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#L"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#p"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#s"/></g><path d="M237 0v-1349h191v1193h672V0H237" id="M"/><path d="M365-904c58-129 161-200 334-200 130 0 228 46 293 138s98 233 98 420c0 189-34 331-102 425S824 20 698 20c-170 0-275-64-336-184 0 55-3 116-9 164H179c5-68 6-147 6-223v-1261h180c-2 193 4 394-4 580h4zm283 791c221 0 256-197 256-427 0-229-34-425-254-425-236 0-285 195-285 441 0 237 53 411 283 411" id="N"/><g id="m"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#M"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#F"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#N"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#I"/></g><path d="M473-1349c438-1 655 221 652 661C1122-268 945-8 532 0H162v-1349h311zm42 1193c308-4 416-205 418-532 2-330-131-509-459-505H353v1037h162" id="O"/><g id="n"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#C"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#t"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#y"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#r"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#D"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#O"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#p"/></g><path d="M285-1169c8 382 2 780 4 1169H129v-1349h237c86 239 188 461 253 720 69-258 169-481 255-720h225V0H937c2-390-5-788 6-1169-75 255-170 488-259 729H547c-90-240-185-475-262-729" id="P"/><g id="o"><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,0,0)" xlink:href="#P"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,10.668402777777779,0)" xlink:href="#q"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,21.336805555555557,0)" xlink:href="#G"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,32.005208333333336,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,42.673611111111114,0)" xlink:href="#u"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,53.34201388888889,0)" xlink:href="#P"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,64.01041666666667,0)" xlink:href="#B"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,74.67881944444446,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,85.34722222222223,0)" xlink:href="#z"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,96.015625,0)" xlink:href="#v"/><use transform="matrix(0.008680555555555556,0,0,0.008680555555555556,106.68402777777777,0)" xlink:href="#t"/></g></defs></g></svg>
\ No newline at end of file
diff --git a/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java b/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java
index b7bcc59..e7a9da5 100644
--- a/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java
+++ b/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java
@@ -1,9 +1,13 @@
 package ar.com.nanotaboada.java.samples.spring.boot.controllers;
 
+import static org.springframework.http.HttpHeaders.LOCATION;
+
 import java.net.URI;
 import java.util.List;
 
-import org.springframework.http.HttpHeaders;
+import jakarta.validation.Valid;
+
+import org.hibernate.validator.constraints.ISBN;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -25,6 +29,7 @@
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.tags.Tag;
+
 import ar.com.nanotaboada.java.samples.spring.boot.models.BookDTO;
 import ar.com.nanotaboada.java.samples.spring.boot.services.BooksService;
 
@@ -52,22 +57,18 @@ public BooksController(BooksService booksService) {
             @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content),
             @ApiResponse(responseCode = "409", description = "Conflict", content = @Content)
     })
-    public ResponseEntity<String> post(@RequestBody BookDTO bookDTO) {
-        if (booksService.retrieveByIsbn(bookDTO.getIsbn()) != null) {
-            return new ResponseEntity<>(HttpStatus.CONFLICT);
-        } else {
-            if (booksService.create(bookDTO)) {
-                URI location = MvcUriComponentsBuilder
-                        .fromMethodName(BooksController.class, "getByIsbn", bookDTO.getIsbn())
-                        .build()
-                        .toUri();
-                HttpHeaders httpHeaders = new HttpHeaders();
-                httpHeaders.setLocation(location);
-                return new ResponseEntity<>(httpHeaders, HttpStatus.CREATED);
-            } else {
-                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
-            }
+    public ResponseEntity<Void> post(@RequestBody @Valid BookDTO bookDTO) {
+        boolean created = booksService.create(bookDTO);
+        if (!created) {
+            return ResponseEntity.status(HttpStatus.CONFLICT).build();
         }
+        URI location = MvcUriComponentsBuilder
+                .fromMethodCall(MvcUriComponentsBuilder.on(BooksController.class).getByIsbn(bookDTO.getIsbn()))
+                .build()
+                .toUri();
+        return ResponseEntity.status(HttpStatus.CREATED)
+                .header(LOCATION, location.toString())
+                .build();
     }
 
     /*
@@ -77,18 +78,16 @@ public ResponseEntity<String> post(@RequestBody BookDTO bookDTO) {
      */
 
     @GetMapping("/books/{isbn}")
-    @Operation(summary = "Retrieves a book by its ID")
+    @Operation(summary = "Retrieves a book by its ISBN")
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = BookDTO.class))),
             @ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
     })
     public ResponseEntity<BookDTO> getByIsbn(@PathVariable String isbn) {
         BookDTO bookDTO = booksService.retrieveByIsbn(isbn);
-        if (bookDTO != null) {
-            return new ResponseEntity<>(bookDTO, HttpStatus.OK);
-        } else {
-            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
-        }
+        return (bookDTO != null)
+                ? ResponseEntity.status(HttpStatus.OK).body(bookDTO)
+                : ResponseEntity.status(HttpStatus.NOT_FOUND).build();
     }
 
     @GetMapping("/books")
@@ -98,7 +97,7 @@ public ResponseEntity<BookDTO> getByIsbn(@PathVariable String isbn) {
     })
     public ResponseEntity<List<BookDTO>> getAll() {
         List<BookDTO> books = booksService.retrieveAll();
-        return new ResponseEntity<>(books, HttpStatus.OK);
+        return ResponseEntity.status(HttpStatus.OK).body(books);
     }
 
     /*
@@ -108,22 +107,17 @@ public ResponseEntity<List<BookDTO>> getAll() {
      */
 
     @PutMapping("/books")
-    @Operation(summary = "Updates (entirely) a book by its ID")
+    @Operation(summary = "Updates (entirely) a book by its ISBN")
     @ApiResponses(value = {
             @ApiResponse(responseCode = "204", description = "No Content", content = @Content),
             @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content),
             @ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
     })
-    public ResponseEntity<String> put(@RequestBody BookDTO bookDTO) {
-        if (booksService.retrieveByIsbn(bookDTO.getIsbn()) != null) {
-            if (booksService.update(bookDTO)) {
-                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
-            } else {
-                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
-            }
-        } else {
-            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
-        }
+    public ResponseEntity<Void> put(@RequestBody @Valid BookDTO bookDTO) {
+        boolean updated = booksService.update(bookDTO);
+        return (updated)
+                ? ResponseEntity.status(HttpStatus.NO_CONTENT).build()
+                : ResponseEntity.status(HttpStatus.NOT_FOUND).build();
     }
 
     /*
@@ -133,21 +127,16 @@ public ResponseEntity<String> put(@RequestBody BookDTO bookDTO) {
      */
 
     @DeleteMapping("/books/{isbn}")
-    @Operation(summary = "Deletes a book by its ID")
+    @Operation(summary = "Deletes a book by its ISBN")
     @ApiResponses(value = {
             @ApiResponse(responseCode = "204", description = "No Content", content = @Content),
             @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content),
             @ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
     })
-    public ResponseEntity<String> delete(@PathVariable String isbn) {
-        if (booksService.retrieveByIsbn(isbn) != null) {
-            if (booksService.delete(isbn)) {
-                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
-            } else {
-                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
-            }
-        } else {
-            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
-        }
+    public ResponseEntity<Void> delete(@PathVariable @ISBN String isbn) {
+        boolean deleted = booksService.delete(isbn);
+        return (deleted)
+                ? ResponseEntity.status(HttpStatus.NO_CONTENT).build()
+                : ResponseEntity.status(HttpStatus.NOT_FOUND).build();
     }
 }
diff --git a/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java b/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java
index 7855537..d286ea6 100644
--- a/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java
+++ b/src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java
@@ -1,11 +1,10 @@
 package ar.com.nanotaboada.java.samples.spring.boot.services;
 
-import jakarta.validation.Validator;
-import lombok.RequiredArgsConstructor;
-
 import java.util.List;
 import java.util.stream.StreamSupport;
 
+import lombok.RequiredArgsConstructor;
+
 import org.modelmapper.ModelMapper;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
@@ -21,7 +20,6 @@
 public class BooksService {
 
     private final BooksRepository booksRepository;
-    private final Validator validator;
     private final ModelMapper modelMapper;
 
     /*
@@ -32,14 +30,14 @@ public class BooksService {
 
     @CachePut(value = "books", key = "#bookDTO.isbn")
     public boolean create(BookDTO bookDTO) {
-        boolean notExists = !booksRepository.existsById(bookDTO.getIsbn());
-        boolean valid = validator.validate(bookDTO).isEmpty();
-        if (notExists && valid) {
+        if (booksRepository.existsById(bookDTO.getIsbn())) {
+            return false;
+        } else {
             Book book = mapFrom(bookDTO);
             booksRepository.save(book);
             return true;
         }
-        return false;
+
     }
 
     /*
@@ -70,14 +68,13 @@ public List<BookDTO> retrieveAll() {
 
     @CachePut(value = "books", key = "#bookDTO.isbn")
     public boolean update(BookDTO bookDTO) {
-        boolean exists = booksRepository.existsById(bookDTO.getIsbn());
-        boolean valid = validator.validate(bookDTO).isEmpty();
-        if (exists && valid) {
+        if (booksRepository.existsById(bookDTO.getIsbn())) {
             Book book = mapFrom(bookDTO);
             booksRepository.save(book);
             return true;
+        } else {
+            return false;
         }
-        return false;
     }
 
     /*
@@ -91,8 +88,9 @@ public boolean delete(String isbn) {
         if (booksRepository.existsById(isbn)) {
             booksRepository.deleteById(isbn);
             return true;
+        } else {
+            return false;
         }
-        return false;
     }
 
     private BookDTO mapFrom(Book book) {
diff --git a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOsBuilder.java b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java
similarity index 96%
rename from src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOsBuilder.java
rename to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java
index 32b728f..d0d661a 100644
--- a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOsBuilder.java
+++ b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java
@@ -6,9 +6,13 @@
 
 import ar.com.nanotaboada.java.samples.spring.boot.models.BookDTO;
 
-public class BookDTOsBuilder {
+public final class BookDTOFakes {
 
-    public static BookDTO buildOneValid() {
+    private BookDTOFakes() {
+        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
+    }
+
+    public static BookDTO createOneValid() {
         BookDTO bookDTO = new BookDTO();
         bookDTO.setIsbn("978-1484200773");
         bookDTO.setTitle("Pro Git");
@@ -28,7 +32,7 @@ Pro Git (Second Edition) is your fully-updated guide to Git and its \
         return bookDTO;
     }
 
-    public static BookDTO buildOneInvalid() {
+    public static BookDTO createOneInvalid() {
         BookDTO bookDTO = new BookDTO();
         bookDTO.setIsbn("978-1234567890"); // Invalid (invalid ISBN)
         bookDTO.setTitle("Title");
@@ -42,7 +46,7 @@ public static BookDTO buildOneInvalid() {
         return bookDTO;
     }
 
-    public static List<BookDTO> buildManyValid() {
+    public static List<BookDTO> createManyValid() {
         ArrayList<BookDTO> bookDTOs = new ArrayList<>();
         BookDTO bookDTO9781838986698 = new BookDTO();
         bookDTO9781838986698.setIsbn("9781838986698");
diff --git a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BooksBuilder.java b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java
similarity index 97%
rename from src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BooksBuilder.java
rename to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java
index 0cfb0f9..a71cfda 100644
--- a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BooksBuilder.java
+++ b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java
@@ -6,9 +6,13 @@
 
 import ar.com.nanotaboada.java.samples.spring.boot.models.Book;
 
-public class BooksBuilder {
+public final class BookFakes {
 
-    public static Book buildOneValid() {
+    private BookFakes() {
+        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
+    }
+
+    public static Book createOneValid() {
         Book book = new Book();
         book.setIsbn("9781484200773");
         book.setTitle("Pro Git");
@@ -28,7 +32,7 @@ Pro Git (Second Edition) is your fully-updated guide to Git and its \
         return book;
     }
 
-    public static Book buildOneInvalid() {
+    public static Book createOneInvalid() {
         Book book = new Book();
         book.setIsbn("9781234567890"); // Invalid (invalid ISBN)
         book.setTitle("Title");
@@ -42,7 +46,7 @@ public static Book buildOneInvalid() {
         return book;
     }
 
-    public static List<Book> buildManyValid() {
+    public static List<Book> createManyValid() {
         ArrayList<Book> books = new ArrayList<>();
         Book book9781838986698 = new Book();
         book9781838986698.setIsbn("9781838986698");
diff --git a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java
index 966fca7..c4e524a 100644
--- a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java
+++ b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java
@@ -3,6 +3,7 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -29,7 +30,7 @@
 import ar.com.nanotaboada.java.samples.spring.boot.models.BookDTO;
 import ar.com.nanotaboada.java.samples.spring.boot.repositories.BooksRepository;
 import ar.com.nanotaboada.java.samples.spring.boot.services.BooksService;
-import ar.com.nanotaboada.java.samples.spring.boot.test.BookDTOsBuilder;
+import ar.com.nanotaboada.java.samples.spring.boot.test.BookDTOFakes;
 
 @DisplayName("HTTP Methods on Controller")
 @WebMvcTest(BooksController.class)
@@ -53,17 +54,17 @@ class BooksControllerTests {
      */
 
     @Test
-    void givenPost_whenRequestBodyContainsExistingValidBook_thenShouldReturnStatusConflict()
+    void givenPost_whenRequestBodyIsValidButExistingBook_thenResponseStatusIsConflict()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        String content = new ObjectMapper().writeValueAsString(bookDTO);
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String body = new ObjectMapper().writeValueAsString(bookDTO);
         Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+                .when(booksServiceMock.create(any(BookDTO.class)))
+                .thenReturn(false); // Existing
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .post(PATH)
-                .content(content)
+                .content(body)
                 .contentType(MediaType.APPLICATION_JSON);
         // Act
         MockHttpServletResponse response = application
@@ -71,25 +72,26 @@ void givenPost_whenRequestBodyContainsExistingValidBook_thenShouldReturnStatusCo
                 .andReturn()
                 .getResponse();
         // Assert
+        verify(booksServiceMock, times(1)).create(any(BookDTO.class));
         assertThat(response.getStatus()).isEqualTo(HttpStatus.CONFLICT.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
+
     }
 
     @Test
-    void givenPost_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusCreatedAndLocationHeader()
+    void givenPost_whenRequestBodyIsValidAndNonExistentBook_thenResponseStatusIsCreated()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        String content = new ObjectMapper().writeValueAsString(bookDTO);
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String body = new ObjectMapper().writeValueAsString(bookDTO);
         Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(null); // New
+                .when(booksServiceMock.create(any(BookDTO.class)))
+                .thenReturn(true); // Non-existent
         Mockito
                 .when(booksServiceMock.create(any(BookDTO.class)))
                 .thenReturn(true);
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .post(PATH)
-                .content(content)
+                .content(body)
                 .contentType(MediaType.APPLICATION_JSON);
         // Act
         MockHttpServletResponse response = application
@@ -98,28 +100,22 @@ void givenPost_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusCreated
                 .getResponse();
         response.setContentType("application/json;charset=UTF-8");
         // Assert
+        verify(booksServiceMock, times(1)).create(any(BookDTO.class));
         assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());
         assertThat(response.getHeader(HttpHeaders.LOCATION)).isNotNull();
         assertThat(response.getHeader(HttpHeaders.LOCATION)).contains(PATH + "/" + bookDTO.getIsbn());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
-        verify(booksServiceMock, times(1)).create(any(BookDTO.class));
+
     }
 
     @Test
-    void givenPost_whenRequestBodyContainsInvalidBook_thenShouldReturnStatusBadRequest()
+    void givenPost_whenRequestBodyIsInvalidBook_thenResponseStatusIsBadRequest()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        String content = new ObjectMapper().writeValueAsString(bookDTO);
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(null); // New
-        Mockito
-                .when(booksServiceMock.create(any(BookDTO.class)))
-                .thenReturn(false);
+        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
+        String body = new ObjectMapper().writeValueAsString(bookDTO);
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .post(PATH)
-                .content(content)
+                .content(body)
                 .contentType(MediaType.APPLICATION_JSON);
         // Act
         MockHttpServletResponse response = application
@@ -127,9 +123,8 @@ void givenPost_whenRequestBodyContainsInvalidBook_thenShouldReturnStatusBadReque
                 .andReturn()
                 .getResponse();
         // Assert
+        verify(booksServiceMock, never()).create(any(BookDTO.class));
         assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
-        verify(booksServiceMock, times(1)).create(any(BookDTO.class));
     }
 
     /*
@@ -139,15 +134,16 @@ void givenPost_whenRequestBodyContainsInvalidBook_thenShouldReturnStatusBadReque
      */
 
     @Test
-    void givenGetByIsbn_whenRequestParameterIdentifiesExistingBook_thenShouldReturnStatusOkAndTheBook()
+    void givenGetByIsbn_whenRequestPathVariableIsValidAndExistingISBN_thenResponseStatusIsOKAndResultIsBook()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String isbn = bookDTO.getIsbn();
         Mockito
                 .when(booksServiceMock.retrieveByIsbn(anyString()))
                 .thenReturn(bookDTO); // Existing
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .get(PATH + "/{isbn}", bookDTO.getIsbn());
+                .get(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
@@ -155,21 +151,21 @@ void givenGetByIsbn_whenRequestParameterIdentifiesExistingBook_thenShouldReturnS
                 .getResponse();
         response.setContentType("application/json;charset=UTF-8");
         String content = response.getContentAsString();
-        BookDTO actual = new ObjectMapper().readValue(content, BookDTO.class);
+        BookDTO result = new ObjectMapper().readValue(content, BookDTO.class);
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
-        assertThat(actual).usingRecursiveComparison().isEqualTo(bookDTO);
         verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
+        assertThat(result).usingRecursiveComparison().isEqualTo(bookDTO);
     }
 
     @Test
-    void givenGetByIsbn_whenRequestParameterDoesNotIdentifyAnExistingBook_thenShouldReturnStatusNotFound()
+    void givenGetByIsbn_whenRequestPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound()
             throws Exception {
         // Arrange
         String isbn = "9781484242216";
         Mockito
                 .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(null); // New
+                .thenReturn(null); // Non-existent
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .get(PATH + "/{isbn}", isbn);
         // Act
@@ -178,18 +174,18 @@ void givenGetByIsbn_whenRequestParameterDoesNotIdentifyAnExistingBook_thenShould
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
         verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
     }
 
     @Test
-    void givenGetAll_whenRequestPathIsBooks_thenShouldReturnStatusOkAndCollectionOfBooks()
+    void givenGetAll_whenRequestPathIsBooks_thenResponseIsOkAndResultIsBooks()
             throws Exception {
         // Arrange
-        List<BookDTO> expected = BookDTOsBuilder.buildManyValid();
+        List<BookDTO> bookDTOs = BookDTOFakes.createManyValid();
         Mockito
                 .when(booksServiceMock.retrieveAll())
-                .thenReturn(expected);
+                .thenReturn(bookDTOs);
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .get(PATH);
         // Act
@@ -199,12 +195,12 @@ void givenGetAll_whenRequestPathIsBooks_thenShouldReturnStatusOkAndCollectionOfB
                 .getResponse();
         response.setContentType("application/json;charset=UTF-8");
         String content = response.getContentAsString();
-        List<BookDTO> actual = new ObjectMapper().readValue(content, new TypeReference<List<BookDTO>>() {
+        List<BookDTO> result = new ObjectMapper().readValue(content, new TypeReference<List<BookDTO>>() {
         });
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
-        assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
         verify(booksServiceMock, times(1)).retrieveAll();
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
+        assertThat(result).usingRecursiveComparison().isEqualTo(bookDTOs);
     }
 
     /*
@@ -214,20 +210,17 @@ void givenGetAll_whenRequestPathIsBooks_thenShouldReturnStatusOkAndCollectionOfB
      */
 
     @Test
-    void givenPut_whenRequestBodyContainsExistingValidBook_thenShouldReturnStatusNoContent()
+    void givenPut_whenRequestBodyIsValidAndExistingBook_thenResponseStatusIsNoContent()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        String content = new ObjectMapper().writeValueAsString(bookDTO);
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String body = new ObjectMapper().writeValueAsString(bookDTO);
         Mockito
                 .when(booksServiceMock.update(any(BookDTO.class)))
-                .thenReturn(true);
+                .thenReturn(true); // Existing
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .put(PATH)
-                .content(content)
+                .content(body)
                 .contentType(MediaType.APPLICATION_JSON);
         // Act
         MockHttpServletResponse response = application
@@ -235,26 +228,22 @@ void givenPut_whenRequestBodyContainsExistingValidBook_thenShouldReturnStatusNoC
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
         verify(booksServiceMock, times(1)).update(any(BookDTO.class));
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
     }
 
     @Test
-    void givenPut_whenRequestBodyContainsExistingInvalidBook_thenShouldReturnStatusBadRequest()
+    void givenPut_whenRequestBodyIsValidButNonExistentBook_thenResponseStatusIsNotFound()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        String content = new ObjectMapper().writeValueAsString(bookDTO);
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String body = new ObjectMapper().writeValueAsString(bookDTO);
         Mockito
                 .when(booksServiceMock.update(any(BookDTO.class)))
-                .thenReturn(false);
+                .thenReturn(false); // Non-existent
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .put(PATH)
-                .content(content)
+                .content(body)
                 .contentType(MediaType.APPLICATION_JSON);
         // Act
         MockHttpServletResponse response = application
@@ -262,23 +251,19 @@ void givenPut_whenRequestBodyContainsExistingInvalidBook_thenShouldReturnStatusB
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
         verify(booksServiceMock, times(1)).update(any(BookDTO.class));
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
     }
 
     @Test
-    void givenPut_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusNotFound()
+    void givenPut_whenRequestBodyIsInvalidBook_thenResponseStatusIsBadRequest()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        String content = new ObjectMapper().writeValueAsString(bookDTO);
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(null); // New
+        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
+        String body = new ObjectMapper().writeValueAsString(bookDTO);
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                 .put(PATH)
-                .content(content)
+                .content(body)
                 .contentType(MediaType.APPLICATION_JSON);
         // Act
         MockHttpServletResponse response = application
@@ -286,8 +271,8 @@ void givenPut_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusNotFound
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
+        verify(booksServiceMock, never()).update(any(BookDTO.class));
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
     }
 
     /*
@@ -297,70 +282,62 @@ void givenPut_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusNotFound
      */
 
     @Test
-    void givenDelete_whenRequestBodyContainsExistingValidBook_thenShouldReturnStatusNoContent()
+    void givenDelete_whenPathVariableIsValidAndExistingISBN_thenResponseStatusIsNoContent()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String isbn = bookDTO.getIsbn();
         Mockito
                 .when(booksServiceMock.delete(anyString()))
-                .thenReturn(true);
+                .thenReturn(true); // Existing
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .delete(PATH + "/{isbn}", bookDTO.getIsbn());
+                .delete(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
         verify(booksServiceMock, times(1)).delete(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
     }
 
     @Test
-    void givenDelete_whenRequestBodyContainsExistingInvalidBook_thenShouldReturnStatusBadRequest()
+    void givenDelete_whenPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String isbn = bookDTO.getIsbn();
         Mockito
                 .when(booksServiceMock.delete(anyString()))
-                .thenReturn(false);
+                .thenReturn(false); // Non-existent
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .delete(PATH + "/{isbn}", bookDTO.getIsbn());
+                .delete(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
         verify(booksServiceMock, times(1)).delete(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
     }
 
     @Test
-    void givenDelete_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusNotFound()
+    void givenDelete_whenPathVariableIsInvalidISBN_thenResponseStatusIsBadRequest()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(null); // New
+        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
+        String isbn = bookDTO.getIsbn();
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .delete(PATH + "/{isbn}", bookDTO.getIsbn());
+                .delete(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
+        verify(booksServiceMock, never()).delete(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
     }
 }
diff --git a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java
index 7b312d5..47c5ac7 100644
--- a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java
+++ b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java
@@ -12,7 +12,7 @@
 
 import ar.com.nanotaboada.java.samples.spring.boot.models.Book;
 import ar.com.nanotaboada.java.samples.spring.boot.repositories.BooksRepository;
-import ar.com.nanotaboada.java.samples.spring.boot.test.BooksBuilder;
+import ar.com.nanotaboada.java.samples.spring.boot.test.BookFakes;
 
 @DisplayName("Derived Query Methods on Repository")
 @DataJpaTest
@@ -22,9 +22,9 @@ class BooksRepositoryTests {
     private BooksRepository repository;
 
     @Test
-    void givenFindByIsbn_whenIsbnAlreadyExists_thenShouldReturnExistingBook() {
+    void givenFindByIsbn_whenISBNAlreadyExists_thenShouldReturnExistingBook() {
         // Arrange
-        Book existing = BooksBuilder.buildOneValid();
+        Book existing = BookFakes.createOneValid();
         repository.save(existing); // Exists
         // Act
         Optional<Book> actual = repository.findByIsbn(existing.getIsbn());
@@ -34,9 +34,9 @@ void givenFindByIsbn_whenIsbnAlreadyExists_thenShouldReturnExistingBook() {
     }
 
     @Test
-    void givenFindByIsbn_whenIsbnDoesNotExist_thenShouldReturnEmptyOptional() {
+    void givenFindByIsbn_whenISBNDoesNotExist_thenShouldReturnEmptyOptional() {
         // Arrange
-        Book expected = BooksBuilder.buildOneValid();
+        Book expected = BookFakes.createOneValid();
         // Act
         Optional<Book> actual = repository.findByIsbn(expected.getIsbn());
         // Assert
diff --git a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java
index 70985d4..3e62ba7 100644
--- a/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java
+++ b/src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java
@@ -3,18 +3,12 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
-
-import jakarta.validation.ConstraintViolation;
-import jakarta.validation.Validator;
 
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
@@ -29,8 +23,8 @@
 import ar.com.nanotaboada.java.samples.spring.boot.models.BookDTO;
 import ar.com.nanotaboada.java.samples.spring.boot.repositories.BooksRepository;
 import ar.com.nanotaboada.java.samples.spring.boot.services.BooksService;
-import ar.com.nanotaboada.java.samples.spring.boot.test.BookDTOsBuilder;
-import ar.com.nanotaboada.java.samples.spring.boot.test.BooksBuilder;
+import ar.com.nanotaboada.java.samples.spring.boot.test.BookDTOFakes;
+import ar.com.nanotaboada.java.samples.spring.boot.test.BookFakes;
 
 @DisplayName("CRUD Operations on Service")
 @ExtendWith(MockitoExtension.class)
@@ -39,9 +33,6 @@ class BooksServiceTests {
     @Mock
     private BooksRepository booksRepositoryMock;
 
-    @Mock
-    private Validator validatorMock;
-
     @Mock
     private ModelMapper modelMapperMock;
 
@@ -55,86 +46,39 @@ class BooksServiceTests {
      */
 
     @Test
-    void givenCreate_whenBookExistsInRepositoryAndIsValid_thenShouldNeverSaveBookIntoRepositoryAndReturnFalse() {
+    void givenCreate_whenRepositoryExistsByIdReturnsTrue_thenRepositoryNeverSaveBookAndResultIsFalse() {
         // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
+        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
         Mockito
                 .when(booksRepositoryMock.existsById(anyString()))
                 .thenReturn(true);
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(new HashSet<ConstraintViolation<BookDTO>>());
         // Act
-        result = booksService.create(bookDTO);
+        boolean result = booksService.create(bookDTO);
         // Assert
         verify(booksRepositoryMock, never()).save(any(Book.class));
+        verify(modelMapperMock, never()).map(bookDTO, Book.class);
         assertThat(result).isFalse();
     }
 
     @Test
-    void givenCreate_whenBookExistsInRepositoryAndIsNotValid_thenShouldNeverSaveBookIntoRepositoryAndReturnFalse() {
+    void givenCreate_whenRepositoryExistsByIdReturnsFalse_thenRepositorySaveBookAndResultIsTrue() {
         // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        Mockito
-                .when(booksRepositoryMock.existsById(anyString()))
-                .thenReturn(true);
-        @SuppressWarnings("unchecked")
-        Set<ConstraintViolation<BookDTO>> constraintViolations = Set.of(mock(ConstraintViolation.class));
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(constraintViolations);
-        // Act
-        result = booksService.create(bookDTO);
-        // Assert
-        verify(booksRepositoryMock, never()).save(any(Book.class));
-        assertThat(result).isFalse();
-    }
-
-    @Test
-    void givenCreate_whenBookdDoesNotExistsInRepositoryAndIsValid_thenShouldSaveBookIntoRepositoryAndReturnTrue() {
-        // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Book book = BooksBuilder.buildOneValid();
+        Book book = BookFakes.createOneValid();
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
         Mockito
                 .when(booksRepositoryMock.existsById(anyString()))
                 .thenReturn(false);
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(new HashSet<ConstraintViolation<BookDTO>>());
         Mockito
                 .when(modelMapperMock.map(bookDTO, Book.class))
                 .thenReturn(book);
         // Act
-        result = booksService.create(bookDTO);
+        boolean result = booksService.create(bookDTO);
         // Assert
-        verify(modelMapperMock, times(1)).map(bookDTO, Book.class);
         verify(booksRepositoryMock, times(1)).save(any(Book.class));
+        verify(modelMapperMock, times(1)).map(bookDTO, Book.class);
         assertThat(result).isTrue();
     }
 
-    @Test
-    void givenCreate_whenBookDoesNotExistsInRepositoryAndIsNotValid_thenShouldNeverSaveBookIntoRepositoryAndReturnFalse() {
-        // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        Mockito
-                .when(booksRepositoryMock.existsById(anyString()))
-                .thenReturn(false);
-        @SuppressWarnings("unchecked")
-        Set<ConstraintViolation<BookDTO>> constraintViolations = Set.of(mock(ConstraintViolation.class));
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(constraintViolations);
-        // Act
-        result = booksService.create(bookDTO);
-        // Assert
-        verify(booksRepositoryMock, never()).save(any(Book.class));
-        assertThat(result).isFalse();
-    }
-
     /*
      * -------------------------------------------------------------------------
      * Retrieve
@@ -142,10 +86,10 @@ void givenCreate_whenBookDoesNotExistsInRepositoryAndIsNotValid_thenShouldNeverS
      */
 
     @Test
-    void givenRetrieveByIsbn_whenIsbnIsFoundInRepository_thenShouldReturnBook() {
+    void givenRetrieveByIsbn_whenRepositoryFindByIdReturnsBook_thenResultIsEqualToBook() {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Book book = BooksBuilder.buildOneValid();
+        Book book = BookFakes.createOneValid();
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
         Mockito
                 .when(booksRepositoryMock.findByIsbn(anyString()))
                 .thenReturn(Optional.of(book));
@@ -155,13 +99,13 @@ void givenRetrieveByIsbn_whenIsbnIsFoundInRepository_thenShouldReturnBook() {
         // Act
         BookDTO result = booksService.retrieveByIsbn(bookDTO.getIsbn());
         // Assert
+        verify(booksRepositoryMock, times(1)).findByIsbn(anyString());
         verify(modelMapperMock, times(1)).map(book, BookDTO.class);
         assertThat(result).usingRecursiveComparison().isEqualTo(bookDTO);
-        verify(booksRepositoryMock, times(1)).findByIsbn(anyString());
     }
 
     @Test
-    void givenRetrieveByIsbn_whenIsbnIsNotFoundInRepository_thenShouldReturnNull() {
+    void givenRetrieveByIsbn_whenRepositoryFindByIdReturnsEmpty_thenResultIsNull() {
         // Arrange
         String isbn = "9781484242216";
         Mockito
@@ -170,32 +114,32 @@ void givenRetrieveByIsbn_whenIsbnIsNotFoundInRepository_thenShouldReturnNull() {
         // Act
         BookDTO result = booksService.retrieveByIsbn(isbn);
         // Assert
-        assertThat(result).isNull();
-        verify(modelMapperMock, never()).map(any(Book.class), any(BookDTO.class));
         verify(booksRepositoryMock, times(1)).findByIsbn(anyString());
+        verify(modelMapperMock, never()).map(any(Book.class), any(BookDTO.class));
+        assertThat(result).isNull();
     }
 
     @Test
-    void givenRetrieveAll_whenRepositoryHasCollectionOfBooks_thenShouldReturnAllBooks() {
+    void givenRetrieveAll_whenRepositoryFindAllReturnsBooks_thenResultIsEqualToBooks() {
         // Arrange
-        List<BookDTO> expected = BookDTOsBuilder.buildManyValid();
-        List<Book> existing = BooksBuilder.buildManyValid();
+        List<Book> books = BookFakes.createManyValid();
+        List<BookDTO> bookDTOs = BookDTOFakes.createManyValid();
         Mockito
                 .when(booksRepositoryMock.findAll())
-                .thenReturn(existing);
-        for (int index = 0; index < existing.size(); index++) {
+                .thenReturn(books);
+        for (int index = 0; index < books.size(); index++) {
             Mockito
-                    .when(modelMapperMock.map(existing.get(index), BookDTO.class))
-                    .thenReturn(expected.get(index));
+                    .when(modelMapperMock.map(books.get(index), BookDTO.class))
+                    .thenReturn(bookDTOs.get(index));
         }
         // Act
-        List<BookDTO> actual = booksService.retrieveAll();
+        List<BookDTO> result = booksService.retrieveAll();
         // Assert
-        assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
-        for (Book book : existing) {
+        verify(booksRepositoryMock, times(1)).findAll();
+        for (Book book : books) {
             verify(modelMapperMock, times(1)).map(book, BookDTO.class);
         }
-        verify(booksRepositoryMock, times(1)).findAll();
+        assertThat(result).usingRecursiveComparison().isEqualTo(bookDTOs);
     }
 
     /*
@@ -205,83 +149,37 @@ void givenRetrieveAll_whenRepositoryHasCollectionOfBooks_thenShouldReturnAllBook
      */
 
     @Test
-    void givenUpdate_whenBookExistsInRepositoryAndIsValid_thenShouldSaveBookIntoRepositoryAndReturnTrue() {
+    void givenUpdate_whenRepositoryExistsByIdReturnsTrue_thenRepositorySaveBookAndResultIsTrue() {
         // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Book book = BooksBuilder.buildOneValid();
+        Book book = BookFakes.createOneValid();
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+
         Mockito
                 .when(booksRepositoryMock.existsById(anyString()))
                 .thenReturn(true);
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(new HashSet<ConstraintViolation<BookDTO>>());
         Mockito
                 .when(modelMapperMock.map(bookDTO, Book.class))
                 .thenReturn(book);
         // Act
-        result = booksService.update(bookDTO);
+        boolean result = booksService.update(bookDTO);
         // Assert
-        verify(modelMapperMock, times(1)).map(bookDTO, Book.class);
         verify(booksRepositoryMock, times(1)).save(any(Book.class));
+        verify(modelMapperMock, times(1)).map(bookDTO, Book.class);
         assertThat(result).isTrue();
     }
 
     @Test
-    void givenUodate_whenBookExistsInRepositoryAndIsNotValid_thenShouldNeverSaveBookIntoRepositoryAndReturnFalse() {
-        // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        Mockito
-                .when(booksRepositoryMock.existsById(anyString()))
-                .thenReturn(true);
-        @SuppressWarnings("unchecked")
-        Set<ConstraintViolation<BookDTO>> constraintViolations = Set.of(mock(ConstraintViolation.class));
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(constraintViolations);
-        // Act
-        result = booksService.update(bookDTO);
-        // Assert
-        verify(booksRepositoryMock, never()).save(any(Book.class));
-        assertThat(result).isFalse();
-    }
-
-    @Test
-    void givenUpdate_whenBookDoesNotExistsInRepositoryAndIsValid_thenShouldNeverSaveBookIntoRepositoryAndReturnFalse() {
-        // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Mockito
-                .when(booksRepositoryMock.existsById(anyString()))
-                .thenReturn(false);
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(new HashSet<ConstraintViolation<BookDTO>>());
-        // Act
-        result = booksService.update(bookDTO);
-        // Assert
-        verify(booksRepositoryMock, never()).save(any(Book.class));
-        assertThat(result).isFalse();
-    }
-
-    @Test
-    void givenUodate_whenBookDoesNotExistsInRepositoryAndIsNotValid_thenShouldNeverSaveBookIntoRepositoryAndReturnFalse() {
+    void givenUpdate_whenRepositoryExistsByIdReturnsFalse_thenRepositoryNeverSaveBookAndResultIsFalse() {
         // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
         Mockito
                 .when(booksRepositoryMock.existsById(anyString()))
                 .thenReturn(false);
-        @SuppressWarnings("unchecked")
-        Set<ConstraintViolation<BookDTO>> constraintViolations = Set.of(mock(ConstraintViolation.class));
-        Mockito
-                .when(validatorMock.validate(any(BookDTO.class)))
-                .thenReturn(constraintViolations);
         // Act
-        result = booksService.update(bookDTO);
+        boolean result = booksService.update(bookDTO);
         // Assert
         verify(booksRepositoryMock, never()).save(any(Book.class));
+        verify(modelMapperMock, never()).map(bookDTO, Book.class);
         assertThat(result).isFalse();
     }
 
@@ -292,45 +190,32 @@ void givenUodate_whenBookDoesNotExistsInRepositoryAndIsNotValid_thenShouldNeverS
      */
 
     @Test
-    void givenDelete_whenIsbnIsBlank_thenShouldNeverDeleteBookFromRepositoryAndReturnFalse() {
-        // Arrange
-        boolean result = false;
-        Book book = BooksBuilder.buildOneValid();
-        book.setIsbn("");
-        // Act
-        result = booksService.delete(book.getIsbn());
-        // Assert
-        verify(booksRepositoryMock, never()).deleteById(anyString());
-        assertThat(result).isFalse();
-    }
-
-    @Test
-    void givenDelete_whenIsbnIsNotBlankButDoesNotExistInRepository_thenShouldNeverDeleteBookFromRepositoryAndReturnFalse() {
+    void givenDelete_whenRepositoryExistsByIdReturnsFalse_thenRepositoryNeverDeleteBookAndResultIsFalse() {
         // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
+        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
         Mockito
                 .when(booksRepositoryMock.existsById(anyString()))
                 .thenReturn(false);
         // Act
-        result = booksService.delete(bookDTO.getIsbn());
+        boolean result = booksService.delete(bookDTO.getIsbn());
         // Assert
         verify(booksRepositoryMock, never()).deleteById(anyString());
+        verify(modelMapperMock, never()).map(bookDTO, Book.class);
         assertThat(result).isFalse();
     }
 
     @Test
-    void givenDelete_whenIsbnIsNotBlankAndExistInRepository_thenShouldDeleteBookFromRepositoryAndReturnTrue() {
+    void givenDelete_whenRepositoryExistsByIdReturnsTrue_thenRepositoryDeleteBookAndResultIsTrue() {
         // Arrange
-        boolean result = false;
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
         Mockito
                 .when(booksRepositoryMock.existsById(anyString()))
                 .thenReturn(true);
         // Act
-        result = booksService.delete(bookDTO.getIsbn());
+        boolean result = booksService.delete(bookDTO.getIsbn());
         // Assert
         verify(booksRepositoryMock, times(1)).deleteById(anyString());
+        verify(modelMapperMock, never()).map(bookDTO, Book.class);
         assertThat(result).isTrue();
     }
 }


Reviews:
user: login: qlty-cloud-legacy[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T18:35:40Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 193d218f757fbbb35ab58c7776f1902b11546a18 and 9202e5f52fe02c8107940110acfe33af93944c13.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `assets/images/structure.svg` is excluded by `!**/*.svg`

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `.vscode/settings.json` (1 hunks)
* `src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java` (7 hunks)
* `src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java` (4 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java` (3 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java` (3 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java` (9 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java` (3 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java` (6 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (3)</summary>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java (1)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java (1)</summary>

* `BookFakes` (9-152)

</details>

</blockquote></details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java (1)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java (1)</summary>

* `BookDTOFakes` (9-153)

</details>

</blockquote></details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java (2)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java (1)</summary>

* `BookDTOFakes` (9-153)

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java (1)</summary>

* `BookFakes` (9-152)

</details>

</blockquote></details>

</details><details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java</summary>

[failure] 25-34: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNAlreadyExists_thenShouldReturnExistingBook' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 37-44: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNDoesNotExist_thenShouldReturnEmptyOptional' doesn't match '[a-z][a-zA-Z0-9]*'

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java</summary>

[failure] 213-233: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenRequestBodyIsValidAndExistingBook_thenResponseStatusIsNoContent' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 236-256: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenRequestBodyIsValidButNonExistentBook_thenResponseStatusIsNotFound' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 244-256: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:221

---

[warning] 221-233: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:244

---

[failure] 285-303: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidAndExistingISBN_thenResponseStatusIsNoContent' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 306-324: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 314-324: CodeDuplication
This block of 10 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:293

---

[warning] 293-303: CodeDuplication
This block of 10 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:314

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java</summary>

[warning] 75-83: CodeDuplication
This block of 8 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java:164

---

[warning] 56-68: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java:179

---

[failure] 49-61: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsTrue_thenRepositoryNeverSaveBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 64-80: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsFalse_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 164-172: CodeDuplication
This block of 8 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java:75

---

[failure] 152-169: MethodNamingConventions
The JUnit 5 test method name 'givenUpdate_whenRepositoryExistsByIdReturnsTrue_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Codacy Static Code Analysis
* GitHub Check: Analyze (java-kotlin)

</details>

<details>
<summary>🔇 Additional comments (46)</summary><blockquote>

<details>
<summary>.vscode/settings.json (1)</summary>

`8-9`: **Disable Java Inlay Hints for a Cleaner Workspace**

Adding `"editor.inlayHints.enabled": "off"` under the `[java]` scope silences parameter and type hints in Java files, complementing the existing default formatter (`"redhat.java"`) and improving readability during the ongoing refactor. Since this is a workspace‑level setting, please confirm that it’s intended to be committed for the entire team rather than as a personal preference.

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java (2)</summary>

`9-9`: **Class name change improves clarity.**

The renaming from `BookDTOsBuilder` to `BookDTOFakes` better represents the purpose of this class - to create fake DTO objects for testing, rather than using a builder pattern.

---

`11-11`: **Method naming update establishes consistency.**

Changing method names from `buildOneValid()`, `buildOneInvalid()`, and `buildManyValid()` to use the `create` prefix better reflects what these methods do and aligns with other test utility classes like `BookFakes`. This also makes the naming consistent across your test utilities.



Also applies to: 31-31, 45-45

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java (2)</summary>

`9-9`: **Class name change clarifies intent.**

Renaming from `BooksBuilder` to `BookFakes` effectively communicates the purpose of this class - providing fake Book objects for testing rather than using a builder pattern.

---

`11-11`: **Method naming update establishes consistency.**

Changing method names from `buildOneValid()`, `buildOneInvalid()`, and `buildManyValid()` to use the `create` prefix better reflects the actual functionality and maintains consistency with other test utilities like `BookDTOFakes`.



Also applies to: 31-31, 45-45

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java (3)</summary>

`15-15`: **Import update properly reflects class renaming.**

Updated import statement correctly references the renamed `BookFakes` class, maintaining code functionality after the refactoring.

---

`25-25`: **Method naming improvement for acronym capitalization.**

The test method names now properly capitalize "ISBN" as an acronym, improving readability and consistency with naming conventions.



Also applies to: 37-37

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 25-34: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNAlreadyExists_thenShouldReturnExistingBook' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`27-27`: **Usage of renamed factory methods.**

Updated to use `BookFakes.createOneValid()` instead of the previous builder method, maintaining functionality while adapting to the refactored helper class.



Also applies to: 39-39

</details>
<details>
<summary>src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java (4)</summary>

`6-7`: **Added Lombok annotation simplifies constructor requirements.**

Adding `@RequiredArgsConstructor` eliminates boilerplate constructor code while ensuring that all final fields (repositories and mappers) are properly initialized.

---

`33-35`: **Simplified create method logic.**

Validation logic has been removed from the create method, focusing it on core business logic - checking existence and saving the entity. This supports the refactoring goal of moving validations to controllers.

---

`71-77`: **Simplified update method with cleaner control flow.**

The update method now uses a more straightforward if-else structure without intermediate variables, making the code more readable while maintaining the same functionality.

---

`91-92`: **Simplified delete method with cleaner control flow.**

The delete method has been restructured with a clearer if-else pattern that directly returns the result, making the code more concise and readable.

</details>
<details>
<summary>src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java (11)</summary>

`3-4`: **Added static import for LOCATION header constant.**

This improves code readability by directly using the well-defined constant name rather than referencing through the HttpHeaders class.

---

`8-11`: **Add validation annotations support.**

Adding imports for validation annotations (`@Valid` and `@ISBN`) aligns with the PR objective of moving validations from services to controllers.

---

`60-64`: **Improved request validation and response handling.**

The method now:
1. Uses `@Valid` annotation for automatic bean validation
2. Returns a boolean result from the service layer
3. Handles conflict case clearly with HTTP 409

This approach properly moves validation to the controller layer and simplifies the error handling flow.

---

`65-71`: **Improved location header construction.**

Using `MvcUriComponentsBuilder.fromMethodCall()` with the controller method reference is more type-safe than using string-based method names. The response construction is also more readable using the builder pattern.

---

`81-81`: **Updated API documentation for accuracy.**

The operation summary now correctly references ISBN instead of ID, improving API documentation clarity.

---

`88-90`: **Simplified response handling with ternary operator.**

The ternary operator provides a more concise way to handle the conditional response while maintaining readability.

---

`100-100`: **Standardized response creation pattern.**

Using `ResponseEntity.status().body()` pattern improves consistency with other controller methods.

---

`110-110`: **Updated API documentation for accuracy.**

The operation summary now correctly specifies updating by ISBN rather than a generic reference.

---

`116-120`: **Streamlined update logic with validation.**

The method now:
1. Uses `@Valid` annotation for automatic bean validation
2. Calls the service method directly, eliminating redundant checks
3. Uses a ternary operator for cleaner response handling
4. Returns the appropriate status code based on the result

This approach properly moves validation to the controller layer and simplifies the flow.

---

`130-130`: **Updated API documentation for accuracy.**

The operation summary now correctly references ISBN instead of ID, improving API documentation clarity.

---

`136-140`: **Added validation and simplified delete logic.**

The method now:
1. Uses `@ISBN` annotation to validate the ISBN path variable
2. Directly calls the service method with simplified flow
3. Uses a ternary operator for cleaner response handling
4. Returns the appropriate status code based on the result

This approach properly moves validation to the controller layer and simplifies the flow.

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java (10)</summary>

`26-27`: **Test utility classes renamed for better clarity.**

Changing from `BookDTOsBuilder` and `BooksBuilder` to `BookDTOFakes` and `BookFakes` better reflects the purpose of these classes - providing test fake objects rather than just building them.

---

`49-61`: **Updated test to reflect validation removal from service layer.**

The test now focuses on repository interactions rather than validation, specifically:
1. The method name clearly describes the test scenario
2. The test verifies that when a book already exists, the repository never saves it
3. Added verification that model mapper is never called in this scenario

This aligns with the PR objective of moving validations from services to controllers.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 49-61: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsTrue_thenRepositoryNeverSaveBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`64-79`: **Test updated to use the new test fake factories.**

The test now uses `BookDTOFakes` and `BookFakes` instead of builder classes while maintaining the same verification logic, ensuring that when a book doesn't exist, it gets properly mapped and saved to the repository.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 64-80: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsFalse_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`89-105`: **Test updated to use the new test fake factories.**

The test now uses `BookFakes` and `BookDTOFakes` to create test objects with the same verification logic for retrieving an existing book.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 89-105: MethodNamingConventions
The JUnit 5 test method name 'givenRetrieveByIsbn_whenRepositoryFindByIdReturnsBook_thenResultIsEqualToBook' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`108-119`: **Enhanced verification in retrieveByIsbn test.**

The test now explicitly verifies that the model mapper is never called when the repository returns empty, which is an important assertion that was missing before.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 108-120: MethodNamingConventions
The JUnit 5 test method name 'givenRetrieveByIsbn_whenRepositoryFindByIdReturnsEmpty_thenResultIsNull' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`123-142`: **Improved test for retrieveAll method.**

The test now:
1. Uses the renamed fake object factories
2. Has a more explicit verification of the model mapper being called for each book
3. Maintains the same overall verification logic

This refactoring maintains test coverage while using the updated test utilities.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 123-143: MethodNamingConventions
The JUnit 5 test method name 'givenRetrieveAll_whenRepositoryFindAllReturnsBooks_thenResultIsEqualToBooks' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`152-169`: **Updated update test to focus on repository interaction.**

The test now focuses on repository interactions rather than validation, verifying that when a book exists, it gets properly mapped and saved to the repository. This aligns with moving validations to the controller layer.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 152-169: MethodNamingConventions
The JUnit 5 test method name 'givenUpdate_whenRepositoryExistsByIdReturnsTrue_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`172-183`: **Enhanced verification in update test.**

The test now explicitly verifies that the model mapper is never called when the book doesn't exist, which is an important assertion that focuses on the core service behavior without validation concerns.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 172-184: MethodNamingConventions
The JUnit 5 test method name 'givenUpdate_whenRepositoryExistsByIdReturnsFalse_thenRepositoryNeverSaveBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`193-204`: **Test refactored to focus on repository interaction.**

The test now verifies that when a book doesn't exist, the delete operation is never called and the model mapper is never involved, focusing purely on the repository interaction pattern.

---

`208-218`: **Test refactored for successful deletion scenario.**

The test now correctly verifies that the repository's deleteById method is called when a book exists, while ensuring the model mapper is never called during a delete operation.

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java (13)</summary>

`6-6`: **Added verification for never interactions.**

Adding the static import for Mockito.never allows verifying that certain methods are never called, which is important for validating that the controller respects validation annotations before calling service methods.

---

`33-33`: **Updated import for test data generation.**

The import now uses `BookDTOFakes` instead of `BookDTOsBuilder`, aligning with the renamed test utility class for creating fake test data.

---

`57-77`: **Updated POST test for conflict scenario.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Mocks the `create` method to return false (existing book)
3. Verifies that the service method is called once
4. Checks the response status is 409 Conflict

These changes align with the updated controller implementation.

---

`81-107`: **Updated POST test for successful creation.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Mocks the `create` method to return true (successful creation)
3. Verifies that the service method is called once
4. Checks the response status is 201 Created and location header is set

These changes align with the updated controller implementation.

---

`111-127`: **Updated POST test for validation failure.**

The test now:
1. Uses `BookDTOFakes` for creating an invalid test object
2. Verifies that the service method is never called (validation fails before)
3. Checks the response status is 400 Bad Request

These changes confirm that controller-level validation works properly.

---

`137-158`: **Updated GET test for successful retrieval.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Properly verifies both the response status and the returned object
3. Maintains the same core verification logic

This refactoring adapts the test to use the updated test utilities.

---

`162-178`: **Updated GET test for non-existent resource.**

The test now explicitly mocks the service to return null for a non-existent book and verifies the 404 Not Found response.

---

`182-203`: **Updated GetAll test to use new test utilities.**

The test now uses `BookDTOFakes` to create test data while maintaining the same verification logic.

---

`213-232`: **Updated PUT test for successful update.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Mocks the `update` method to return true (successful update)
3. Verifies that the service method is called once
4. Checks the response status is 204 No Content

These changes align with the updated controller implementation.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 213-233: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenRequestBodyIsValidAndExistingBook_thenResponseStatusIsNoContent' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`236-255`: **Updated PUT test for non-existent resource.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Mocks the `update` method to return false (book not found)
3. Verifies that the service method is called once
4. Checks the response status is 404 Not Found

These changes align with the updated controller implementation.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 236-256: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenRequestBodyIsValidButNonExistentBook_thenResponseStatusIsNotFound' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`259-275`: **Updated PUT test for validation failure.**

The test now:
1. Uses `BookDTOFakes` for creating an invalid test object
2. Verifies that the service method is never called (validation fails before)
3. Checks the response status is 400 Bad Request

These changes confirm that controller-level validation works properly.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 259-276: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenRequestBodyIsInvalidBook_thenResponseStatusIsBadRequest' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`285-302`: **Updated DELETE test for successful deletion.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Mocks the `delete` method to return true (successful deletion)
3. Verifies that the service method is called once
4. Checks the response status is 204 No Content

These changes align with the updated controller implementation.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 285-303: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidAndExistingISBN_thenResponseStatusIsNoContent' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`306-323`: **Updated DELETE test for non-existent resource.**

The test now:
1. Uses `BookDTOFakes` for creating test data
2. Mocks the `delete` method to return false (book not found)
3. Verifies that the service method is called once
4. Checks the response status is 404 Not Found

These changes align with the updated controller implementation.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 306-324: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T18:38:56Z

Review comments:
diff_hunk: @@ -1,14 +1,14 @@
~package ar.com.nanotaboada.java.samples.spring.boot.test;
~
~import java.time.LocalDate;
~import java.util.ArrayList;
~import java.util.List;
path: src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java
user: login: qlty-cloud-legacy[bot]
body: Add a private constructor to hide the implicit public one.
created_at: 2025-04-19T18:35:40Z
author_association: NONE
diff_hunk: @@ -1,14 +1,14 @@
~package ar.com.nanotaboada.java.samples.spring.boot.test;
~
~import java.time.LocalDate;
~import java.util.ArrayList;
~import java.util.List;
path: src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java
user: login: qlty-cloud-legacy[bot]
body: Add a private constructor to hide the implicit public one.
created_at: 2025-04-19T18:35:40Z
author_association: NONE
diff_hunk: @@ -297,70 +282,62 @@
      */
 
     @Test
-    void givenDelete_whenRequestBodyContainsExistingValidBook_thenShouldReturnStatusNoContent()
+    void givenDelete_whenPathVariableIsValidAndExistingISBN_thenResponseStatusIsNoContent()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String isbn = bookDTO.getIsbn();
         Mockito
                 .when(booksServiceMock.delete(anyString()))
-                .thenReturn(true);
+                .thenReturn(true); // Existing
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .delete(PATH + "/{isbn}", bookDTO.getIsbn());
+                .delete(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
         verify(booksServiceMock, times(1)).delete(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());
     }
 
     @Test
-    void givenDelete_whenRequestBodyContainsExistingInvalidBook_thenShouldReturnStatusBadRequest()
+    void givenDelete_whenPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneInvalid();
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(bookDTO); // Existing
+        BookDTO bookDTO = BookDTOFakes.createOneValid();
+        String isbn = bookDTO.getIsbn();
         Mockito
                 .when(booksServiceMock.delete(anyString()))
-                .thenReturn(false);
+                .thenReturn(false); // Non-existent
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .delete(PATH + "/{isbn}", bookDTO.getIsbn());
+                .delete(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
         verify(booksServiceMock, times(1)).delete(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
     }
 
     @Test
-    void givenDelete_whenRequestBodyContainsNewValidBook_thenShouldReturnStatusNotFound()
+    void givenPut_whenPathVariableIsInvalidISBN_thenResponseStatusIsBadRequest()
             throws Exception {
         // Arrange
-        BookDTO bookDTO = BookDTOsBuilder.buildOneValid();
-        Mockito
-                .when(booksServiceMock.retrieveByIsbn(anyString()))
-                .thenReturn(null); // New
+        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
+        String isbn = bookDTO.getIsbn();
         MockHttpServletRequestBuilder request = MockMvcRequestBuilders
-                .delete(PATH + "/{isbn}", bookDTO.getIsbn());
+                .delete(PATH + "/{isbn}", isbn);
         // Act
         MockHttpServletResponse response = application
                 .perform(request)
                 .andReturn()
                 .getResponse();
         // Assert
-        assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
-        verify(booksServiceMock, times(1)).retrieveByIsbn(anyString());
+        verify(booksServiceMock, never()).delete(anyString());
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
path: src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Incorrect test method name.**

This test is validating a DELETE operation but is named "givenPut_when..." which is misleading.


```diff
-@Test
-void givenPut_whenPathVariableIsInvalidISBN_thenResponseStatusIsBadRequest()
+@Test
+void givenDelete_whenPathVariableIsInvalidISBN_thenResponseStatusIsBadRequest()
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    @Test
    void givenDelete_whenPathVariableIsInvalidISBN_thenResponseStatusIsBadRequest()
            throws Exception {
        // Arrange
        BookDTO bookDTO = BookDTOFakes.createOneInvalid();
        String isbn = bookDTO.getIsbn();
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                .delete(PATH + "/{isbn}", isbn);
        // Act
        MockHttpServletResponse response = application
                .perform(request)
                .andReturn()
                .getResponse();
        // Assert
        verify(booksServiceMock, never()).delete(anyString());
        assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
    }
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 327-342: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenPathVariableIsInvalidISBN_thenResponseStatusIsBadRequest' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 2b44db7
created_at: 2025-04-19T18:38:54Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-19T18:35:26Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes refactor the book management API and its supporting layers to streamline validation, response handling, and testing. Controller methods now rely on validation annotations and simplified service calls, returning more appropriate HTTP statuses and headers. The service layer removes manual validation logic, focusing on repository existence checks and CRUD operations. Supporting test utilities are renamed for clarity, and all tests are updated to use the new method and class names. Test logic is revised to align with the updated service and controller flows, removing validation mocks and focusing on repository interactions and response codes. Editor settings are also updated to disable Java inlay hints.

## Changes

| File(s)                                                                                       | Change Summary                                                                                                                                                                                                                                                  |
|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| .vscode/settings.json                                                                         | Added a setting to disable Java inlay hints in the editor configuration.                                                                                                                                                                                        |
| src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java    | Refactored controller methods to use validation annotations, simplified service calls, updated response handling, and improved API documentation summaries. Changed response types to `ResponseEntity<Void>` where appropriate.                                 |
| src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java          | Removed `Validator` dependency and all validation logic. Simplified control flow in CRUD methods to focus on repository existence checks and direct returns.                                                                                                    |
| src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java              | Renamed class from `BookDTOsBuilder` to `BookDTOFakes` and updated static method names to use `create` instead of `build`.                                                                                                                                    |
| src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java                 | Renamed class from `BooksBuilder` to `BookFakes` and updated static method names to use `create` instead of `build`.                                                                                                                                          |
| src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java | Updated tests to use new test data builders, revised service method mocking and verification to match refactored service/controller logic, and updated test method names for clarity. Removed validation-related test logic.                                     |
| src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java | Updated to use `BookFakes` for test data and standardized method names for ISBN capitalization.                                                                                                                         |
| src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java | Removed validation mocks and logic, updated to use new test data builders, revised test method names, and focused tests on repository interactions and mapping. Removed or merged tests that previously checked validation logic.                                |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant BooksController
    participant BooksService
    participant BooksRepository

    Client->>BooksController: POST /books (BookDTO)
    BooksController->>BooksService: create(BookDTO)
    BooksService->>BooksRepository: existsById(isbn)
    alt Book exists
        BooksService-->>BooksController: false
        BooksController-->>Client: 409 Conflict
    else Book does not exist
        BooksService->>BooksRepository: save(Book)
        BooksService-->>BooksController: true
        BooksController-->>Client: 201 Created (Location header)
    end

    Client->>BooksController: PUT /books (BookDTO)
    BooksController->>BooksService: update(BookDTO)
    BooksService->>BooksRepository: existsById(isbn)
    alt Book exists
        BooksService->>BooksRepository: save(Book)
        BooksService-->>BooksController: true
        BooksController-->>Client: 204 No Content
    else Book does not exist
        BooksService-->>BooksController: false
        BooksController-->>Client: 404 Not Found
    end

    Client->>BooksController: DELETE /books/{isbn}
    BooksController->>BooksService: delete(isbn)
    BooksService->>BooksRepository: existsById(isbn)
    alt Book exists
        BooksService->>BooksRepository: deleteById(isbn)
        BooksService-->>BooksController: true
        BooksController-->>Client: 204 No Content
    else Book does not exist
        BooksService-->>BooksController: false
        BooksController-->>Client: 404 Not Found
    end

    Client->>BooksController: GET /books/{isbn}
    BooksController->>BooksService: retrieveByIsbn(isbn)
    BooksService->>BooksRepository: findById(isbn)
    alt Book found
        BooksService-->>BooksController: BookDTO
        BooksController-->>Client: 200 OK (BookDTO)
    else Book not found
        BooksService-->>BooksController: null
        BooksController-->>Client: 404 Not Found
    end

    Client->>BooksController: GET /books
    BooksController->>BooksService: retrieveAll()
    BooksService->>BooksRepository: findAll()
    BooksService-->>BooksController: List<BookDTO>
    BooksController-->>Client: 200 OK (List<BookDTO>)
```

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9202e5f52fe02c8107940110acfe33af93944c13 and 2b44db706a83009b8d69dbe804b31b3ac813121c.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `assets/images/structure.svg` is excluded by `!**/*.svg`

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `.vscode/settings.json` (1 hunks)
* `src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java` (7 hunks)
* `src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java` (4 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java` (3 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java` (3 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java` (9 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java` (3 hunks)
* `src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java` (6 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* .vscode/settings.json

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (4)</summary>

* src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java
* src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java
* src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java
* src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/BooksController.java

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (3)</summary>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java (2)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java (1)</summary>

* `BookDTOFakes` (9-157)

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java (1)</summary>

* `BookFakes` (9-156)

</details>

</blockquote></details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java (1)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java (1)</summary>

* `BookDTOFakes` (9-157)

</details>

</blockquote></details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java (1)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java (1)</summary>

* `BookFakes` (9-156)

</details>

</blockquote></details>

</details><details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java</summary>

[failure] 49-61: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsTrue_thenRepositoryNeverSaveBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 64-80: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsFalse_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 75-83: CodeDuplication
This block of 8 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java:164

---

[warning] 56-68: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java:179

---

[warning] 164-172: CodeDuplication
This block of 8 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java:75

---

[failure] 152-169: MethodNamingConventions
The JUnit 5 test method name 'givenUpdate_whenRepositoryExistsByIdReturnsTrue_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 193-205: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenRepositoryExistsByIdReturnsFalse_thenRepositoryNeverDeleteBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java</summary>

[warning] 244-256: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:221

---

[warning] 221-233: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:244

---

[failure] 285-303: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidAndExistingISBN_thenResponseStatusIsNoContent' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 306-324: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 314-324: CodeDuplication
This block of 10 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:293

---

[warning] 293-303: CodeDuplication
This block of 10 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:314

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java</summary>

[failure] 25-34: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNAlreadyExists_thenShouldReturnExistingBook' doesn't match '[a-z][a-zA-Z0-9]*'

---

[failure] 37-44: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNDoesNotExist_thenShouldReturnEmptyOptional' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Codacy Static Code Analysis

</details>

<details>
<summary>🔇 Additional comments (26)</summary><blockquote>

<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/repositories/BooksRepositoryTests.java (3)</summary>

`15-15`: **Appropriately updated test dependency import.**

The import has been updated from `BooksBuilder` to `BookFakes`, aligning with the project-wide refactoring of test utility classes to follow a more descriptive naming convention.

---

`25-34`: **Method name capitalization improvement.**

The test method name has been updated to consistently capitalize "ISBN" throughout the codebase, improving naming consistency. The test data creation has also been updated to use the new `BookFakes.createOneValid()` method instead of the former builder pattern.


Note that the static analysis tool flags the method naming convention as not matching `[a-z][a-zA-Z0-9]*`, but this is a false positive since the project is clearly using a given-when-then BDD-style naming convention, which is a common practice in test code.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 25-34: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNAlreadyExists_thenShouldReturnExistingBook' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`37-44`: **Consistent naming pattern applied.**

Similar to the previous test method, this one has been updated to consistently capitalize "ISBN" in the method name, improving readability and consistency across the test suite.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 37-44: MethodNamingConventions
The JUnit 5 test method name 'givenFindByIsbn_whenISBNDoesNotExist_thenShouldReturnEmptyOptional' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/services/BooksServiceTests.java (10)</summary>

`26-27`: **Updated utility class imports.**

The imports have been properly updated to reference the renamed utility classes `BookDTOFakes` and `BookFakes`, which are part of the broader refactoring to improve naming consistency across test classes.

---

`49-61`: **Refactored test to focus on repository existence check instead of validation.**

This test has been correctly refactored to focus solely on repository existence checks rather than validation logic, aligning with the migration of validation responsibility from services to controllers. The test method name and assertions have been updated to reflect this change in responsibility.


The method naming convention warning from the static analysis tool is a false positive as the project is using BDD-style naming for tests.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 49-61: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsTrue_thenRepositoryNeverSaveBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`64-80`: **Simplified test structure with appropriate verifications.**

The test has been appropriately simplified to focus on repository interactions rather than validation. The verification now checks that the model mapper is called exactly once when the repository returns false for `existsById()`, ensuring that the service correctly maps the DTO to a domain object before saving.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 64-80: MethodNamingConventions
The JUnit 5 test method name 'givenCreate_whenRepositoryExistsByIdReturnsFalse_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`89-105`: **Reorganized test assertions with improved verification order.**

The test now properly verifies the repository and model mapper interactions in a logical order, with the repository check preceding the mapper verification. This reflects the execution flow of the service method being tested.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 89-105: MethodNamingConventions
The JUnit 5 test method name 'givenRetrieveByIsbn_whenRepositoryFindByIdReturnsBook_thenResultIsEqualToBook' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`108-120`: **Added explicit model mapper verification.**

The test now includes an explicit verification that the model mapper is never called when the repository returns an empty optional, ensuring that no unnecessary mapping operations are performed when no entity is found.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 108-120: MethodNamingConventions
The JUnit 5 test method name 'givenRetrieveByIsbn_whenRepositoryFindByIdReturnsEmpty_thenResultIsNull' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`123-143`: **Updated test to use new fake data generators.**

The test has been updated to use the new `BookFakes` and `BookDTOFakes` utility classes, making the test more readable and consistent with the rest of the codebase. The updated verification loop properly checks that each book is mapped exactly once.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 123-143: MethodNamingConventions
The JUnit 5 test method name 'givenRetrieveAll_whenRepositoryFindAllReturnsBooks_thenResultIsEqualToBooks' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`152-169`: **Refactored update test with explicit model mapper verification.**

The test now explicitly verifies that the model mapper is called exactly once when the book exists in the repository, ensuring that the DTO is correctly mapped to a domain object before saving.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 152-169: MethodNamingConventions
The JUnit 5 test method name 'givenUpdate_whenRepositoryExistsByIdReturnsTrue_thenRepositorySaveBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`172-184`: **Added explicit model mapper verification in negative update test.**

The test now explicitly verifies that the model mapper is never called when the book doesn't exist in the repository, ensuring that no unnecessary mapping operations are performed when the update operation cannot be completed.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 172-184: MethodNamingConventions
The JUnit 5 test method name 'givenUpdate_whenRepositoryExistsByIdReturnsFalse_thenRepositoryNeverSaveBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`193-205`: **Added explicit model mapper verification in negative delete test.**

Similar to the update test, this test now explicitly verifies that the model mapper is never called when the book doesn't exist in the repository, ensuring that no unnecessary operations are performed when the delete operation cannot be completed.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 193-205: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenRepositoryExistsByIdReturnsFalse_thenRepositoryNeverDeleteBookAndResultIsFalse' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`208-220`: **Updated delete test with proper model mapper verification.**

The test now correctly verifies that the model mapper is never called during a delete operation, regardless of whether the book exists in the repository or not, reflecting the fact that mapping is not needed for deletion.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 208-220: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenRepositoryExistsByIdReturnsTrue_thenRepositoryDeleteBookAndResultIsTrue' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

</details>
<details>
<summary>src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java (13)</summary>

`6-6`: **Added important Mockito verification import.**

The import for `Mockito.never()` has been added, enabling verification that certain methods are not called under specific conditions. This is essential for testing that invalid data never reaches the service layer when validation is performed at the controller level.

---

`33-33`: **Updated test utility class import.**

The import has been correctly updated to reference the renamed utility class `BookDTOFakes`, which is part of the broader refactoring to improve naming consistency across test classes.

---

`57-78`: **Improved POST test for existing book scenario.**

The test has been updated to use the new `BookDTOFakes` utility and renamed variables from `content` to `body` for clarity. The test now properly verifies that the service's `create` method is called exactly once, even when the book already exists, reflecting that validation happens before the service call.

---

`81-108`: **Enhanced test for successful book creation.**

The test has been improved to properly verify the successful creation flow, including checking that the service's `create` method is called exactly once and verifying that the response contains the expected location header with the book's ISBN.

---

`111-128`: **Added service method verification for invalid book POST.**

The test now explicitly verifies that the service's `create` method is never called when the request body contains an invalid book, confirming that validation happens at the controller level before reaching the service.

---

`137-159`: **Reordered assertions in GET by ISBN test.**

The assertions have been reordered to first verify the response status and then the response body, providing a more logical flow for the test assertions. The test data creation has also been updated to use the new `BookDTOFakes` utility.

---

`162-179`: **Improved GET test for non-existent book scenario.**

The test has been updated to return `null` instead of a `null` book when mocking the service's `retrieveByIsbn` method for a non-existent book, better reflecting the actual service implementation.

---

`182-204`: **Enhanced GET all books test with proper service method verification.**

The test now properly verifies that the service's `retrieveAll` method is called exactly once and that the response status and body match the expected values.

---

`213-233`: **Updated PUT test for existing book scenario.**

The test has been updated to use the new `BookDTOFakes` utility and to properly verify that the service's `update` method is called exactly once when updating an existing book.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[warning] 221-233: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:244

</details>

</details>

---

`236-256`: **Improved PUT test for non-existent book scenario.**

The test has been updated to use the new `BookDTOFakes` utility and to properly verify that the service's `update` method is called exactly once when attempting to update a non-existent book, confirming that validation happens before checking for existence.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[warning] 244-256: CodeDuplication
This block of 12 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:221

</details>

</details>

---

`259-276`: **Added service method verification for invalid book PUT.**

The test now explicitly verifies that the service's `update` method is never called when the request body contains an invalid book, confirming that validation happens at the controller level before reaching the service.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 259-276: MethodNamingConventions
The JUnit 5 test method name 'givenPut_whenRequestBodyIsInvalidBook_thenResponseStatusIsBadRequest' doesn't match '[a-z][a-zA-Z0-9]*'

</details>

</details>

---

`285-303`: **Enhanced DELETE test for existing book scenario.**

The test has been updated to use the new `BookDTOFakes` utility and to properly verify that the service's `delete` method is called exactly once when deleting an existing book.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 285-303: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidAndExistingISBN_thenResponseStatusIsNoContent' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 293-303: CodeDuplication
This block of 10 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:314

</details>

</details>

---

`306-324`: **Updated DELETE test for non-existent book scenario.**

The test has been updated to use the new `BookDTOFakes` utility and to properly verify that the service's `delete` method is called exactly once when attempting to delete a non-existent book, confirming that validation happens before checking for existence.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Codeac Code Quality</summary>

[failure] 306-324: MethodNamingConventions
The JUnit 5 test method name 'givenDelete_whenPathVariableIsValidButNonExistentISBN_thenResponseStatusIsNotFound' doesn't match '[a-z][a-zA-Z0-9]*'

---

[warning] 314-324: CodeDuplication
This block of 10 lines is too similar to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/controllers/BooksControllerTests.java:293

</details>

</details>

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=nanotaboada/java.samples.spring.boot&utm_content=183):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: codacy-production[bot]
created_at: 2025-04-19T18:36:06Z
body: ## Coverage summary from Codacy
### [See diff coverage on Codacy](https://app.codacy.com/gh/nanotaboada/java.samples.spring.boot/pull-requests/183/coverage?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummaryPullRequest)

 | Coverage variation | Diff coverage | 
 | ------------- | ------------- |
 | :white_check_mark: **+0.00%** (target: -1.00%) | :white_check_mark: **100.00%** |

<details>
<summary>Coverage variation details</summary>

| | Coverable lines | Covered lines | Coverage |
| ------------- | ------------- | ------------- | ------------- |
| Common ancestor commit (193d218f757fbbb35ab58c7776f1902b11546a18) | 56 | 56 | 100.00% | |
| Head commit (2b44db706a83009b8d69dbe804b31b3ac813121c) | 49 (-7) | 49 (-7) | 100.00% (**+0.00%**) |

**Coverage variation** is the difference between the coverage for the head and common ancestor commits of the pull request branch: `<coverage of head commit> - <coverage of common ancestor commit>`
</details>

<details>
<summary>Diff coverage details</summary>

| | Coverable lines | Covered lines | Diff coverage |
| ------------- | ------------- | ------------- | ------------- |
| Pull request (#183) | 27 | 27 | **100.00%** |

**Diff coverage** is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: `<covered lines added or modified>/<coverable lines added or modified> * 100%`
</details>

#### [See your quality gate settings](https://app.codacy.com/gh/nanotaboada/java.samples.spring.boot/settings/quality?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummaryGates)&nbsp;&nbsp;&nbsp;&nbsp;[Change summary preferences](https://app.codacy.com/gh/nanotaboada/java.samples.spring.boot/settings/integrations?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummarySettings)

<!-- 108f945d-e553-4918-8ccf-027c6d4acc2c -->
author_association: NONE
user: login: codecov[bot]
created_at: 2025-04-19T18:36:14Z
body: ## [Codecov](https://app.codecov.io/gh/nanotaboada/java.samples.spring.boot/pull/183?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`193d218`)](https://app.codecov.io/gh/nanotaboada/java.samples.spring.boot/commit/193d218f757fbbb35ab58c7776f1902b11546a18?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada) to head [(`2b44db7`)](https://app.codecov.io/gh/nanotaboada/java.samples.spring.boot/commit/2b44db706a83009b8d69dbe804b31b3ac813121c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada).
> Report is 2 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##              master      #183   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
+ Complexity        26        20    -6     
===========================================
  Files              2         2           
  Lines             56        49    -7     
  Branches           6         4    -2     
===========================================
- Hits              56        49    -7     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/nanotaboada/java.samples.spring.boot/pull/183?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nano+Taboada).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: qlty-cloud-legacy[bot]
created_at: 2025-04-19T19:04:58Z
body: Code Climate has analyzed commit 2b44db70 and detected **2 issues** on this pull request.

Here's the issue category breakdown:

| Category | Count |
|:---:|:---:|
| Clarity | 2 |


Note: there are **2 critical issues.**

View more on [Code Climate](https://codeclimate.com/github/nanotaboada/java.samples.spring.boot/pull/183).

author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-19T19:05:03Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=nanotaboada_java.samples.spring.boot&pullRequest=183) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&pullRequest=183&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&pullRequest=183&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=nanotaboada_java.samples.spring.boot&pullRequest=183&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=nanotaboada_java.samples.spring.boot&pullRequest=183&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=nanotaboada_java.samples.spring.boot&pullRequest=183&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=nanotaboada_java.samples.spring.boot&pullRequest=183)


author_association: NONE