Zimin A.N.
|
75967edc9a
|
feat(webhook): Phase 5 — dispatcher filter + delivery lifecycle tests
WebhookDispatcherMatchTest (11 tests):
- empty filters match anything
- scope filter inclusion/exclusion
- dictionary filter inclusion/exclusion + null dictionary handling
- event_type filter inclusion/exclusion
- AND-semantics между всеми тремя фильтрами
- empty list treated as null (match all)
WebhookDeliveryLifecycleTest (7 tests):
- initial state (pending, attempt 0)
- markDelivered → delivered status + statusCode
- markFailed → retrying + attempt++ + scheduled next
- exponential backoff растёт между attempts
- 1000 attempts → DLQ
- resetDlq → retrying (admin recovery path)
- delivered после failures clears lastError
Total ordinis-outbox tests: 6 → 47.
Project total: 132 → 149 unit tests.
Phase 5 e2e tests с embedded HTTP receiver — deferred (нужен JDK
HttpServer or wiremock setup, нетривиально). Текущее покрытие
закрывает critical filter logic + state machine + HMAC + SSRF.
Реальный e2e на staging будет через manual run + UI verification.
|
2026-05-06 15:26:51 +03:00 |
|
Zimin A.N.
|
e9a7278709
|
feat(webhook): Phase 3 — REST API CRUD + HMAC/SSRF tests
REST API endpoints под /api/v1/admin/webhooks/:
- GET /subscriptions — list (INTERNAL+ scope)
- POST /subscriptions — create (RESTRICTED scope)
- GET /subscriptions/{id} — get one (INTERNAL+)
- PUT /subscriptions/{id} — update (RESTRICTED)
- DELETE /subscriptions/{id} — delete (RESTRICTED)
- POST /subscriptions/{id}/rotate-secret — regenerate HMAC (RESTRICTED)
- GET /subscriptions/{id}/deliveries — delivery history per sub
- GET /deliveries/dlq — DLQ listing
RBAC через ScopeContext: RESTRICTED для mutating, INTERNAL+ для read.
PUBLIC scope получает 403.
DTOs:
- CreateWebhookSubscriptionRequest (Bean Validation: URL regex, length)
- WebhookSubscriptionResponse (с factory `from()` маскированный secret
vs `fromWithSecret()` plaintext только для create response)
- WebhookDeliveryResponse
Service:
- HMAC secret 32 random bytes → 64 hex chars через SecureRandom
- Plaintext secret виден только в create/rotate response (single-time)
- list/get показывают `sk_****<last4>` masked
Tests (15 SsrfGuard + 8 HmacSigner = 23 new):
- HmacSigner: known vector verification, prefix, hex length, distinct
inputs produce distinct sigs, empty secret rejected
- SsrfGuard: rejects 127/8, 169.254/16, 10/8, 192.168/16, 0.0.0.0,
non-http schemes, missing host, unresolvable DNS. Allowlist bypass.
isPrivate() unit tests для loopback/link-local/site-local/public IP.
Total: 109 → 132 unit tests.
Phase 4 далее: admin-ui /webhooks page (list + create + delivery history).
|
2026-05-06 15:12:28 +03:00 |
|
Zimin A.N.
|
e182b30c58
|
feat(outbox): attempt cap → DLQ + admin endpoint + 6 unit-тестов
После N (default 1000) неудачных попыток публикации событие маркируется
dlq_at и исключается из polling. Без cap poller бесконечно ретраил мёртвые
сообщения (orphan topic, ACL deny), забивая логи и lag-метрику.
- 0011-outbox-dlq.xml: dlq_at column + перестроенный idx_outbox_unpublished
(исключает DLQ) + idx_outbox_dlq для admin-листинга.
- OutboxPoller: ordinis.outbox.attempt-max (default 1000), markDlq() при
достижении cap, новый counter ordinis_outbox_dlq_total.
- OutboxLagGauge: ordinis_outbox_dlq_size gauge — алерт на > 0.
- OutboxEventRepository: countPending() (без DLQ), findByDlqAtIsNotNull(Pageable).
- OutboxAdminController: GET /admin/outbox/{stats,dlq} для UI.
- 6 unit-тестов через mock-maker-subclass (JDK 25 ломает Mockito inline).
|
2026-05-04 15:43:56 +03:00 |
|