From 18483927221e1f3d9669c2b4baac7ebd6404a474 Mon Sep 17 00:00:00 2001 From: okturan Date: Sat, 18 Jul 2026 04:09:27 +0200 Subject: [PATCH 1/3] Add attributable API error proof --- .github/workflows/ci.yml | 5 ++++ README.md | 6 ++++ docs/library-api-error-proof.png | Bin 0 -> 61830 bytes ...CategoryErrorResponseIntegrationTests.java | 26 ++++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 docs/library-api-error-proof.png create mode 100644 src/test/java/com/alisimsek/LibraryManagementProject/CategoryErrorResponseIntegrationTests.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f89d566..4805f78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,11 @@ jobs: - name: Test and package run: ./mvnw --batch-mode --no-transfer-progress verify + - name: Validate API proof asset + run: | + test -s docs/library-api-error-proof.png + file docs/library-api-error-proof.png | grep -q "PNG image data, 1280 x 720" + - name: Validate Compose contract run: docker compose --env-file .env.example config --quiet diff --git a/README.md b/README.md index af59cd0..c62815d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ Kütüphaneler tarafından kullanılabilecek kayıt yönetimlerini, kitap ödün - Java 17'ye sabitlenmiş container build/runtime aşamaları ve PostgreSQL servis sağlık kontrolü. - Kaynakta parola tutmayan Compose yapılandırması ve Java 17/PostgreSQL CI doğrulaması. +## Canlı API Kanıtı + +![Swagger UI'da var olmayan kategori isteğinin global hata yönetimiyle 404 dönmesi](docs/library-api-error-proof.png) + +*Gerçek Java 17/PostgreSQL 16 container çalıştırmasından alınmıştır. Swagger UI, `DataLoader` tarafından üretilen sentetik yerel veriler üzerinde `GET /api/v1/categories/999999` isteğini çalıştırır; fork'ta eklenen global hata yönetimi de tam 404 yanıtını üretir. Bu, barındırılan bir demo değil, yerel ve tekrar üretilebilir bir çalışma kanıtıdır.* + ## Kullanılan Teknolojiler Java diff --git a/docs/library-api-error-proof.png b/docs/library-api-error-proof.png new file mode 100644 index 0000000000000000000000000000000000000000..ea1e6b1d803469a95c51e21074d82a390a23fb02 GIT binary patch literal 61830 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 qN}+BU_c1F^uZXXV~3dl literal 0 HcmV?d00001 diff --git a/src/test/java/com/alisimsek/LibraryManagementProject/CategoryErrorResponseIntegrationTests.java b/src/test/java/com/alisimsek/LibraryManagementProject/CategoryErrorResponseIntegrationTests.java new file mode 100644 index 0000000..f92fe36 --- /dev/null +++ b/src/test/java/com/alisimsek/LibraryManagementProject/CategoryErrorResponseIntegrationTests.java @@ -0,0 +1,26 @@ +package com.alisimsek.LibraryManagementProject; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class CategoryErrorResponseIntegrationTests { + + @Autowired + private MockMvc mockMvc; + + @Test + void missingCategoryUsesTheForksGlobalErrorResponse() throws Exception { + mockMvc.perform(get("/api/v1/categories/{id}", 999999L)) + .andExpect(status().isNotFound()) + .andExpect(content().string("Category with ID 999999 not found in the system.")); + } +} From 45eabd22ccca1c4005a637aba448e0939dbca22a Mon Sep 17 00:00:00 2001 From: okturan Date: Sat, 18 Jul 2026 04:11:33 +0200 Subject: [PATCH 2/3] Make proof validation portable --- .github/workflows/ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4805f78..7e1dd6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,14 @@ jobs: - name: Validate API proof asset run: | test -s docs/library-api-error-proof.png - file docs/library-api-error-proof.png | grep -q "PNG image data, 1280 x 720" + python3 - <<'PY' + import struct + from pathlib import Path + + proof = Path("docs/library-api-error-proof.png").read_bytes() + assert proof[:8] == b"\x89PNG\r\n\x1a\n", "proof asset is not a PNG" + assert struct.unpack(">II", proof[16:24]) == (1280, 720), "proof asset must be 1280x720" + PY - name: Validate Compose contract run: docker compose --env-file .env.example config --quiet From c1794f9461ff9943ac2f7ca8bd87cb2bfb200df0 Mon Sep 17 00:00:00 2001 From: okturan Date: Sat, 18 Jul 2026 04:12:30 +0200 Subject: [PATCH 3/3] Validate the proof in its real image format --- .github/workflows/ci.yml | 27 ++++++++++++++---- README.md | 2 +- ...-proof.png => library-api-error-proof.jpg} | Bin 3 files changed, 23 insertions(+), 6 deletions(-) rename docs/{library-api-error-proof.png => library-api-error-proof.jpg} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e1dd6c..543d8de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,14 +54,31 @@ jobs: - name: Validate API proof asset run: | - test -s docs/library-api-error-proof.png + test -s docs/library-api-error-proof.jpg python3 - <<'PY' - import struct from pathlib import Path - proof = Path("docs/library-api-error-proof.png").read_bytes() - assert proof[:8] == b"\x89PNG\r\n\x1a\n", "proof asset is not a PNG" - assert struct.unpack(">II", proof[16:24]) == (1280, 720), "proof asset must be 1280x720" + proof = Path("docs/library-api-error-proof.jpg").read_bytes() + assert proof[:2] == b"\xff\xd8", "proof asset is not a JPEG" + + offset = 2 + dimensions = None + start_of_frame = {0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF} + while offset < len(proof): + assert proof[offset] == 0xFF, "invalid JPEG marker" + marker = proof[offset + 1] + offset += 2 + if marker in {0xD8, 0xD9}: + continue + length = int.from_bytes(proof[offset:offset + 2], "big") + if marker in start_of_frame: + height = int.from_bytes(proof[offset + 3:offset + 5], "big") + width = int.from_bytes(proof[offset + 5:offset + 7], "big") + dimensions = (width, height) + break + offset += length + + assert dimensions == (1280, 720), "proof asset must be 1280x720" PY - name: Validate Compose contract diff --git a/README.md b/README.md index c62815d..30699a1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Kütüphaneler tarafından kullanılabilecek kayıt yönetimlerini, kitap ödün ## Canlı API Kanıtı -![Swagger UI'da var olmayan kategori isteğinin global hata yönetimiyle 404 dönmesi](docs/library-api-error-proof.png) +![Swagger UI'da var olmayan kategori isteğinin global hata yönetimiyle 404 dönmesi](docs/library-api-error-proof.jpg) *Gerçek Java 17/PostgreSQL 16 container çalıştırmasından alınmıştır. Swagger UI, `DataLoader` tarafından üretilen sentetik yerel veriler üzerinde `GET /api/v1/categories/999999` isteğini çalıştırır; fork'ta eklenen global hata yönetimi de tam 404 yanıtını üretir. Bu, barındırılan bir demo değil, yerel ve tekrar üretilebilir bir çalışma kanıtıdır.* diff --git a/docs/library-api-error-proof.png b/docs/library-api-error-proof.jpg similarity index 100% rename from docs/library-api-error-proof.png rename to docs/library-api-error-proof.jpg