SOAP, gRPC і асинхронні API
Зміст
REST поверх JSON — це мейнстрім, але не весь світ. У банку чи страховій ти зустрінеш SOAP, якому понад двадцять років і який нікуди не подінеться. У мікросервісній платформі два сервіси всередині часто говорять не REST, а gRPC — бінарним протоколом, який у DevTools JSON-ом не подивишся. А половина «магії» сучасних продуктів — це взагалі не запит-відповідь: платіжка сама стукає до тебе вебхуком, замовлення пливе через Kafka до складського сервісу, лист відправляється через чергу. Це асинхронний світ, де немає синхронної відповіді, яку звично заасертити, — і саме тут ламаються тести, написані за звичкою «відправив запит — перевірив тіло».
Це глава-поглиблення: при першому проході її можна пропустити. Але коли ти потрапляєш на проєкт із SOAP-інтеграцією, gRPC-сервісами чи чергами — тобі потрібна модель, а не паніка. Мета глави — дати чесний каркас: як влаштований кожен із цих протоколів, що саме перевіряє QA і де межа між «я це знаю» і «цим я не керував руками». Оглядову карту типів API дає перша глава розділу; тут — деталі й стратегія.
SOAP і WSDL: чому досі живе
SOAP — це протокол обміну структурованими повідомленнями, де кожне повідомлення загорнуте в XML- (envelope) із фіксованою структурою: Envelope → опційний Header → обов'язковий Body. Зазвичай він їздить поверх HTTP методом POST, але, на відміну від REST, HTTP тут — лише транспорт: уся семантика операції живе всередині XML, а не в URL і методі.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<getBalance>
<accountId>ACC-42</accountId>
</getBalance>
</soap:Body>
</soap:Envelope>
Контракт SOAP-сервісу описує WSDL (Web Services Description Language) — машиночитний XML-документ, який перелічує операції, їхні вхідні й вихідні повідомлення та типи (через XML Schema, XSD). Для SOAP WSDL — це те саме, чим для REST є OpenAPI: єдине джерело істини про форму запитів і відповідей (пор. OpenAPI/Swagger). З WSDL інструмент може згенерувати клієнт і заготовки запитів автоматично.
Чому це досі живе, хоча «всі перейшли на REST»? Бо навколо SOAP існує зрілий стек стандартів WS-* — зокрема WS-Security (підпис і шифрування на рівні повідомлення, а не лише транспорту), формальні контракти й розширення. Банки, страхові, державні реєстри, телеком і старі enterprise-інтеграції побудовані на цьому й переписувати їх ніхто не буде. Тому SOAP — це не «legacy, яке помирає», а «legacy, яке платить зарплати».
Канонічний інструмент ручного й автоматизованого тестування SOAP — SoapUI (та його комерційна версія ReadyAPI): він читає WSDL, будує дерево операцій, дає слати запити й відповіді за XPath. Postman теж уміє слати SOAP (це просто POST з XML-тілом і правильним Content-Type), але зручність SoapUI саме в тому, що він розуміє WSDL як контракт. Одна деталь, на якій легко спіткнутися: заголовок SOAPAction — це версія 1.1. У SOAP 1.2 такого заголовка немає: дія передається параметром медіа-типу — необовʼязковим параметром action у application/soap+xml, який використовують для оптимізації диспетчеризації чи маршрутизації.
Головна пастка для QA: SOAP-помилка — це не HTTP-помилка. Коли операція завершується невдало, сервіс повертає SOAP Fault — спеціальний елемент у Body (у SOAP 1.1 з полями faultcode/faultstring, у SOAP 1.2 перейменованими на Code/Reason). За специфікацією Fault їде зі статусом 500 (у SOAP 1.2 помилка на боці відправника — 400), але на практиці чимало реалізацій кладуть Fault у тіло навіть під 200 OK. Тобто тест, який дивиться лише на HTTP-код, легко пропустить фактичну помилку в конверті. Це той самий клас пасток, що й «200 з помилкою в тілі» у REST і масив errors у GraphQL: перевіряй тіло, а не тільки статус.
gRPC: контракт, типи викликів, інструменти
gRPC — це фреймворк віддалених викликів процедур (Remote Procedure Call, RPC) від Google, який за замовчуванням використовує Protocol Buffers (protobuf) — компактний бінарний формат — як мову опису інтерфейсу й для серіалізації повідомлень; за потреби можна взяти й інші альтернативи. Замість «ресурсів і методів HTTP» тут ідея інша: клієнт викликає метод сервісу так, ніби це локальна функція, а фреймворк ховає мережу під капотом.
Контракт описується у .proto-файлі: сервіс, його методи (rpc) і структура повідомлень зі строго типізованими полями. Це схема, генератор коду і документація в одному:
syntax = "proto3";
service AccountService {
rpc GetBalance (BalanceRequest) returns (BalanceReply);
}
message BalanceRequest {
string account_id = 1;
}
message BalanceReply {
int64 cents = 1;
string currency = 2;
}
Числа 1, 2 біля полів — це не значення, а номери полів (field numbers). Головне правило про них таке: номер не можна перепризначати чи перевикористовувати, а видаляючи поле, його номер треба зарезервувати, щоб ніхто випадково не зайняв його знову. Сумісність protobuf дає в обидва боки — і зворотну, і пряму, — але не автоматично, а «якщо дотримуватися кількох простих практик» під час оновлення .proto: тоді старий код читає нові повідомлення, просто ігноруючи нові поля. Ця дисципліна номерів — основа сумісності версій (детально — у главі контрактне тестування).
Навіщо це, якщо є простий JSON? Три причини: швидкість бінарного формату, строга типізація прямо з контракту й стрімінг. gRPC має чотири типи викликів:
| Тип виклику | Клієнт шле | Сервер відповідає | Приклад |
|---|---|---|---|
| Unary | одне повідомлення | одне повідомлення | звичайний запит балансу |
| Server streaming | одне | потік повідомлень | підписка на оновлення ціни |
| Client streaming | потік | одне | завантаження великого файлу частинами |
| Bidirectional streaming | потік | потік | чат, двобічна телеметрія |
Ключова відмінність для AQA: gRPC-виклик не подивишся в DevTools як JSON — це бінарний формат, тож звична вкладка Network майже безпорадна (детальніше про це — у главі REST API та формати даних розділу про веб). Так само gRPC має власні , а не HTTP: OK (0), NOT_FOUND (5), INVALID_ARGUMENT (3), UNAUTHENTICATED (16), DEADLINE_EXCEEDED (4) і так далі. Плутати gRPC-статус із HTTP-статусом — типова помилка новачка в gRPC.
Канонічний CLI-інструмент — grpcurl: це «curl для gRPC». Щоб він знав форму викликів, йому потрібен або .proto-файл, або ввімкнена на сервері server reflection (сервіс сам віддає свою схему). Без схеми grpcurl не знає, як серіалізувати запит:
grpcurl -plaintext -d '{"account_id": "ACC-42"}' localhost:50051 AccountService/GetBalance
grpcurl приймає JSON на вхід і показує JSON на виході, а бінарне кодування бере на себе — тому руками ти працюєш зі зрозумілим текстом. Postman теж підтримує gRPC-запити з імпортом .proto; є й окремі GUI-клієнти. Стратегія тестування далі та сама, що й для REST: перевірки статусу (тут — gRPC-статусу), полів відповіді, меж і негативних сценаріїв, тільки контракт береться з .proto, а не з OpenAPI.
Вебхуки: вихідні виклики і стратегія перевірки
Досі протокол був синхронним: ти шлеш запит — одразу отримуєш відповідь. Вебхук (webhook) перевертає напрямок: це коли зовнішня система сама викликає тебе — робить HTTP-POST на твій URL, щойно стається подія. «Не дзвони нам — ми подзвонимо тобі». Платіжний провайдер стукає, коли оплата пройшла; GitHub — коли є push; сервіс розсилки — коли лист відкрито.
Це головна складність тестування: у звичайному API-тесті ти ініціюєш запит і одразу маєш відповідь для асерту. З вебхуком ти — : подію тригерить хтось інший, прилетить вона асинхронно й невідомо коли, а провайдер ще й може прислати її повторно. Класична стратегія перевірки (це канонічний виклад — інші глави посилаються сюди):
- Підняти приймач. Локальний HTTP-сервер, що ловить
POSTі зберігає тіло; для реального провайдера localhost треба виставити назовні (ngrok і аналоги). Механіка приймача й тунелів — у главі мокання залежностей. - Стригерити подію — через API продукту або провайдера (наприклад, тестова оплата).
- Дочекатися доставки , а не sleep-ом. Вебхук асинхронний: фіксований
sleep(3000)то марно чекає, то не дочікується. Правильно — полінг із (механіка очікувань — глава стабільність API-тестів). - Перевірити те, що прилетіло: схему тіла (як і будь-яку відповідь); підпис; і — головне — побічний ефект у твоїй системі (вебхук прийшов → стан справді змінився, замовлення стало
paid).
Окремо про підпис. Вебхук приходить на публічний URL, тож будь-хто може підробити POST і сказати «оплата пройшла». Щоб цьому запобігти, провайдер підписує тіло — зазвичай HMAC (hash-based message authentication code) із спільним секретом — і кладе підпис у заголовок. Приймач має перерахувати HMAC від сирого тіла тим самим секретом і звірити; не збіглося — запит відкинути. Перевірка, що продукт валідує підпис і відкидає підроблений/зіпсований, — обов'язковий негативний тест, а не опція.
І пам'ятай про два боки медалі. Ти можеш тестувати продукт як чужих вебхуків (правильно приймаємо й обробляємо) — і як продюсера власних: чи генерується подія взагалі, чи є при недоступному отримувачі, чи правильний підпис і формат. Ретраї підводять нас до ключової теми асинхронності — дублікатів.
Черги повідомлень: Kafka і RabbitMQ
Ще далі від запиту-відповіді — черги повідомлень (message queues). Продюсер кладе повідомлення в брокер і йде далі, не чекаючи; консюмер колись його забере й обробить. Це розв'язує зв'язність сервісів (вони не знають одне про одного), згладжує піки навантаження й дає стійкість: якщо консюмер лежить, повідомлення чекають у брокері, а не губляться.
Два імена, які треба знати. RabbitMQ — класичний брокер повідомлень: продюсер шле в exchange, той маршрутизує в черги, консюмери розбирають; після підтвердження (ack) повідомлення зникає. Kafka — це радше розподілений лог: повідомлення пишуться в topic, поділений на , і лишаються там на час retention; консюмери читають, рухаючи свій offset, і те саме повідомлення можуть прочитати різні групи. Для QA практична різниця в тому, що в Kafka повідомлення можна перечитати й після обробки, а в RabbitMQ після ack його вже нема.
Що тут перевіряє тестувальник:
- Формат повідомлення. Повідомлення має свій контракт (у Kafka часто через schema registry). Це той самий контроль схеми, що й для API-відповіді: типи, обов'язкові поля, сумісність версій.
- Продюсер справді публікує. Дія в продукті (створили замовлення) → у топіку/черзі з'явилося очікуване повідомлення. Тут головна пастка: продюсер не отримує «відповіді», тож заасертити нема чого за звичкою — треба або читати чергу в тесті, або перевіряти наслідок.
- Консюмер правильно обробляє. Поклали повідомлення (або стригерили подію) → перевірили побічний ефект (downstream): запис у БД, наступне повідомлення, надісланий лист.
- Гарантії доставки. at-most-once (можлива втрата), at-least-once (можливі дублікати — задокументований дефолт Kafka), exactly-once (складна й обмежена гарантія, часто це насправді «at-least-once + ідемпотентний консюмер», а не магія брокера). На співбесіді чесніше сказати саме так, ніж обіцяти «Kafka гарантує exactly-once» без нюансів.
- Повторна обробка й недоставлені. Що стається, коли консюмер падає на повідомленні: брокер віддасть його знову.
poison message(повідомлення, що завжди падає) не має крутитися вічно — його відправляють у (dead letter queue, DLQ). Але «вичерпав N спроб» — лише один із чотирьох шляхів туди, і в RabbitMQ він привʼязаний саме до quorum-черг (перевищеноdelivery-limit). Решта три: консюмер зробив negative acknowledgement ізrequeue=false; сплив власний TTL повідомлення; чергу переповнено й повідомлення відкинуто за лімітом довжини. І окрема пастка, яка підриває інваріант «у DLQ впаде все безнадійне»: якщо спливає вся черга, її повідомлення в dead letter не потрапляють — тож порожня DLQ ще не доводить, що втрат не було. Кожен із цих шляхів — окремий сценарій. - Порядок. У Kafka порядок гарантований лише в межах однієї партиції, не між ними; у розподілених системах загалом розраховувати на глобальний порядок не варто.
Технічно локальний брокер для тестів піднімають (testcontainers і подібне — деталі поза цією главою), а тест або читає топік/чергу напряму, або асертить наслідок обробки.
Ідемпотентність консюмера і дедуплікація
Це серце всієї асинхронності, тож винесемо окремо. Домінантна гарантія в чергах — at-least-once: система радше доставить повідомлення двічі, ніж втратить його. Це не загальне враження, а задокументований дефолт: Kafka прямо пише, що гарантує at-least-once за замовчуванням, а at-most-once користувач отримує, лише вимкнувши ретраї продюсера й закомітивши offset до обробки; RabbitMQ у режимі ручного підтвердження автоматично повертає непідтверджену доставку в чергу й вимагає писати консюмер із розрахунку на . На вебхуки та сама модель переноситься за аналогією — стандарту, який зобовʼязував би провайдерів до at-least-once, немає, тож тут це робоча настанова, а не документована гарантія. А отже, дублікати — не аварія, а нормальний режим роботи: ретрай вебхука після таймауту, повторна доставка після падіння консюмера, повтор продюсера при мережевому збої.
Наслідок жорсткий: консюмер зобов'язаний бути ідемпотентним. Обробка того самого повідомлення двічі має давати той самий результат, що й один раз. Інакше — подвійне списання коштів, два однакові листи, дубль замовлення. Ідемпотентність тут — точно та сама ідея, що й Idempotency-Key для вхідних POST-запитів (див. главу тест-дизайн для API), тільки з боку отримувача.
Механізми дедуплікації, які варто впізнавати:
- Кожне повідомлення/вебхук несе унікальний
id; консюмер тримає таблицю оброблених id і мовчки ігнорує вже бачені. - Унікальне обмеження (unique constraint) у БД, яке фізично не дасть вставити дубль.
- (вставити-або-оновити) замість чистого insert — повтор просто перезапише тим самим значенням.
І звідси випливає головний тест асинхронної системи: достав те саме повідомлення (чи той самий вебхук) двічі — і переконайся, що побічний ефект стався рівно один раз. Один платіж, один лист, одне замовлення. Якщо після двох однакових доставок з'явилося два платежі — це не «баг черги», це неідемпотентний консюмер, і саме твій тест мусить його спіймати.
Чесна межа знань на співбесіді
Ця тема — саме та, де сеньйор виграє чесністю, а не бравадою. Практична експертиза в Kafka чи SOAP-стеку — це окрема глибока спеціалізація; від кандидата на QA/AQA переважно чекають правильну модель, а не досвід адміністрування брокера. Сильна відповідь звучить так: «gRPC — бінарний RPC, за замовчуванням із protobuf, контракт у .proto, чотири типи викликів, тестую через grpcurl від контракту; але продакшн-стрімінг під навантаженням руками не гоняв». Це набагато краще за впевнене плавання в деталях, яких не знаєш.
Погана відповідь — вдавати глибину: сказати «Kafka гарантує exactly-once» без застережень, переплутати вебхук зі стрімінгом, стверджувати, що дублікати доставки — це баг брокера. Інтерв'юер якраз і мацає, чи розумієш ти межу власного знання: чи відрізниш «я це проєктував» від «я знаю, як це влаштовано». Назвати межу вголос — ознака сеньйорності, а не слабкості.
Типові помилки
- Виглядає як успіх, а насправді SOAP Fault. HTTP-код
200, тест зелений — але вBodyлежитьfaultstringз помилкою. Перевіряй наявність Fault у конверті, а не лише статус транспорту. - Виглядає як помилка gRPC-сервера, а насправді не подав схему. grpcurl без
.protoчи server reflection не знає, як серіалізувати виклик, і падає — це не баг сервісу, а забутий контракт. - Виглядає як HTTP-код, а насправді gRPC-статус.
NOT_FOUNDу gRPC — це код 5 у власній системі статусів, а не HTTP 404; асерти на HTTP-коди для gRPC безглузді. - Виглядає як тест вебхука, а насправді гонка.
sleep(3000)замість полінгу: подія асинхронна, тож тест то марно тупцює, то не дочікується доставки — класичний . - Виглядає як «відповіді нема — значить, ок». Продюсер поклав повідомлення в чергу й не отримав відповіді — і тест нічого не заасертив. Перевіряй, що повідомлення справді лягло або що стався наслідок.
- Виглядає як баг: вебхук прийшов двічі. Дубль доставки при at-least-once — норма. Баг — це коли неідемпотентний консюмер зробив на ньому два платежі.
- Виглядає як , а насправді лише . Одна успішна доставка перевірена, а повтор, підроблений підпис і DLQ — ні. Для асинхронних систем саме ці сценарії найцінніші.
Підсумок
- HTTP тут — часто лише транспорт: у SOAP семантика в XML-конверті, у gRPC — у бінарному protobuf; помилку шукай у тілі/статусі протоколу, а не тільки в HTTP-коді.
- gRPC = бінарний RPC, за замовчуванням із protobuf, контракт у
.proto, чотири типи викликів (unary й три стрімінгові), власні статус-коди; ручний інструмент — grpcurl, і йому потрібна схема. - Вебхук — це вхідний виклик до тебе: підняти приймач, стригерити подію, дочекатися полінгом, перевірити підпис і побічний ефект; тестувати і як споживача, і як продюсера.
- Асинхронна доставка типово at-least-once (у Kafka це задокументований дефолт), тож дублікати — норма, а не аварія; консюмер мусить бути ідемпотентним, а дедуплікація — через унікальний id, constraint або upsert.
- Головний тест асинхронної системи — доставити те саме повідомлення двічі й довести, що ефект стався рівно один раз.
Можливі питання
- «Чим gRPC відрізняється від REST і коли його брати?» Чекають: бінарний protobuf за замовчуванням, строгий контракт у
.proto, стрімінг, швидкість — тому популярний між мікросервісами; мінус — не подивишся як JSON у DevTools. Дивляться на розуміння компромісу, а не на завчений список. - «Як протестувати вебхук?» Сильна відповідь — це стратегія: приймач + тунель, тригер події, очікування полінгом, перевірка схеми, підпису й побічного ефекту. Провал — «поставлю sleep і гляну». Бонус — згадати ретраї й дублікати.
- «Прилетів той самий вебхук двічі. Баг?» Правильно: at-least-once робить дублікати очікуваними; баг — лише якщо консюмер неідемпотентний і зробив ефект двічі. Тут перевіряють, чи не плутаєш симптом із причиною.
- «Як тестувати систему на Kafka/RabbitMQ, якщо продюсер не дає відповіді?» Очікують: асерт наслідку далі по ланцюжку або читання топіка/черги, перевірка формату повідомлення, ідемпотентності консюмера, поведінки при повторі й DLQ.
- «Що знаєш про SOAP?» Для не-legacy проєкту достатньо моделі: XML-конверт, контракт WSDL, SoapUI, SOAP Fault замість HTTP-помилки. Інтерв'юер дивиться, чи впізнаєш стек, а не чи писав ти WS-Security руками — тут доречно чесно окреслити .
Джерела
- W3C — SOAP Version 1.2 Part 1: Messaging Framework — конверт
Envelopeз необовʼязковимHeaderі обовʼязковимBody; помилка живе всередині повідомлення, а HTTP — лише один із біндингів. - SoapUI — SOAP and WSDL: Getting Started (SmartBear) — інструмент читає WSDL і генерує запити за замовчуванням, дає асерти на кроці SOAP Request, покриття за WSDL і прямо з контракту.
gRPC: контракт, типи викликів, інструменти
- gRPC — Core concepts, architecture and lifecycle — чотири типи викликів і їхні гарантії порядку; відправна точка — опис у
.proto, а protocol buffers є дефолтом, а не властивістю протоколу. - Protocol Buffers — Overview — сумісність тримається на номерах полів, а не іменах: номер не можна перевикористати, а видаливши поле, його номер СЛІД зарезервувати.
- gRPC — Status codes and their use in gRPC — власна система статусів:
OK= 0,INVALID_ARGUMENT= 3,NOT_FOUND= 5,UNAUTHENTICATED= 16 — і вона не збігається з HTTP. - gRPC over HTTP2 — протокольна специфікація — на дроті кожен виклик —
POSTповерх HTTP/2, і успіх, і помилка їдуть із:status 200, а вердикт лежить у трейлеріgrpc-status.
Вебхуки: вихідні виклики і стратегія перевірки
- Martin Fowler — Eradicating Non-Determinism in Tests — асинхронну доставку чекають умовою, а не паузою: голі
sleepджерело забороняє прямо. - Playwright — Test assertions (expect.poll, toPass) —
expect.pollдає готовий механізм очікування з таймаутом та інтервалами.
Черги повідомлень: Kafka і RabbitMQ
- Apache Kafka — Introduction (документація 4.0) — події зберігаються в партиційованих топіках і не видаляються після споживання; порядок гарантований лише в межах топік-партиції.
- RabbitMQ — Queues (офіційна документація) — черга — FIFO зі споживанням із голови; порядок ламають кілька каналів, пріоритети й повторна доставка, а сам повтор позначено прапорцем
redelivered. - Apache Kafka — Design (документація 4.3) — три семантики доставки дослівно, поділ на довговічність публікації й гарантії споживання, і дрібний шрифт під обіцянкою exactly-once.
- RabbitMQ — Consumer Acknowledgements and Publisher Confirms — автоматичне підтвердження («fire-and-forget») втрачає повідомлення при закритті каналу; при ручному — непідтверджена доставка повертається в чергу.
- RabbitMQ — Dead Letter Exchanges — чотири події, за яких повідомлення перепубліковується в dead letter, і виняток: якщо спливає вся черга, її повідомлення туди не потрапляють.
Ідемпотентність консюмера і дедуплікація
- Apache Kafka — Design (документація 4.3) — дефолт — at-least-once; at-most-once користувач отримує сам, вимкнувши ретраї продюсера й закомітивши офсети до обробки.
- RabbitMQ — Consumer Acknowledgements and Publisher Confirms — дока формулює обовʼязок прямо: консюмер має бути готовим до повторних доставок і написаним з ідемпотентністю на думці.
- IETF Internet-Draft — The Idempotency-Key HTTP Header Field (-06) — той самий принцип із боку вхідних запитів: унікальний ключ дає ресурсу впізнати повтор і не виконати операцію вдруге.
Чесна межа знань на співбесіді
- Apache Kafka — Design (документація 4.3) — чому «Kafka гарантує exactly-once» без застережень хибно: обіцянка часто не поширюється на падіння консюмера чи продюсера й на кілька процесів-консюмерів.
- W3C — SOAP Version 1.2 Part 1: Messaging Framework — SOAP fault переносить інформацію про помилку всередині повідомлення — статус транспорту про неї не говорить.
- gRPC over HTTP2 — протокольна специфікація — асерт «HTTP 200 = успіх» на gRPC не працює: провалений виклик теж приходить зі
:status 200. - gRPC — Status codes and their use in gRPC —
NOT_FOUNDу gRPC — код 5 власної системи статусів, а не HTTP404. - Protocol Buffers — Overview — «поле зникло» у protobuf не проявляється як помилка — для старого коду воно тихо стає значенням за замовчуванням.
- Apache Kafka — Design (документація 4.3) — дубль доставки при at-least-once — задокументований дефолт, а не аварія брокера.
Що таке SOAP і чим він принципово відрізняється від REST?
SOAP — це протокол обміну структурованими повідомленнями, де кожне повідомлення загорнуте в XML- (envelope) із фіксованою структурою Envelope → Header (опційний) → Body. Їздить він зазвичай поверх HTTP методом POST, але ключова відмінність від REST у тому, що HTTP тут — лише транспорт: уся семантика операції живе всередині XML, а не в URL і HTTP-методі. У REST ти дивишся на GET /accounts/42 і метод GET уже каже «читаю ресурс»; у SOAP і URL, і метод часто однакові для всіх операцій, а що саме викликається (getBalance, transfer) — написано в тілі конверта. Практичний наслідок для QA: контракт SOAP не читається з маршруту, його треба брати з WSDL, а помилку шукати в тілі, а не в HTTP-коді.
Що таке WSDL і навіщо він потрібен QA?
WSDL (Web Services Description Language) — це машиночитний XML-документ, який описує контракт SOAP-сервісу: перелік операцій, їхні вхідні й вихідні повідомлення та типи (через XML Schema, XSD). Для SOAP WSDL — це те саме, чим для REST є OpenAPI: єдине джерело істини про форму запитів і відповідей. З WSDL інструмент може автоматично згенерувати клієнт і заготовки запитів, тож тестувальнику не треба вручну вгадувати структуру XML. Саме тому канонічний інструмент SoapUI спершу читає WSDL і будує з нього дерево операцій. Для QA це означає: не впевнений, які поля обов'язкові й що повертає операція — відкривай WSDL, а не пробуй наосліп.
Чому SOAP досі живий, хоча всі нібито перейшли на REST?
Бо навколо SOAP існує зрілий стек стандартів WS-*, якого в REST просто немає з коробки: зокрема WS-Security (підпис і шифрування на рівні самого повідомлення, а не лише транспорту), формальні контракти й розширення. Банки, страхові, державні реєстри, телеком і старі enterprise-інтеграції побудовані на цьому багато років, і переписувати їх ніхто не буде — і вартість надто великі. Тому SOAP коректніше сприймати не як «legacy, яке помирає», а як «legacy, яке платить зарплати». Для QA це значить, що на проєкті в певних доменах ти цілком реально зустрінеш SOAP, і треба мати модель, а не панікувати.
Що таке SOAP Fault і чому це головна пастка для тестувальника?
SOAP Fault — це спеціальний елемент усередині Body, який сервіс повертає, коли операція завершилася невдало (у SOAP 1.1 з полями faultcode/faultstring, у SOAP 1.2 перейменованими на Code/Reason). Пастка в тому, що SOAP-помилка — це не обов'язково HTTP-помилка: за специфікацією Fault їде зі статусом 500 (у SOAP 1.2 помилка на боці відправника — 400), але на практиці чимало реалізацій кладуть Fault у тіло навіть під 200 OK. Тобто тест, який дивиться лише на HTTP-код і бачить зелений 200, легко пропустить фактичну помилку в конверті. Це той самий клас пасток, що й «200 з помилкою в тілі» у REST або масив errors у GraphQL. Правильна перевірка — відсутність елемента Fault у Body, а не тільки статус транспорту.
Що таке gRPC і чим він за замовчуванням серіалізує дані?
gRPC — це фреймворк віддалених викликів процедур (Remote Procedure Call, RPC) від Google, який за замовчуванням бере Protocol Buffers (protobuf) — компактний бінарний формат — і як мову опису інтерфейсу, і для серіалізації повідомлень; за потреби можна використати інші альтернативи. Модель тут не «ресурси й HTTP-методи», а виклик функції: клієнт викликає метод сервісу так, ніби це локальна функція, а фреймворк ховає мережу під капотом. Дані передаються не текстовим JSON, а бінарем, тому їх не прочитаєш очима без інструмента. Беруть gRPC переважно для зв'язку між мікросервісами, де важливі швидкість, строгий контракт і стрімінг. Для QA головне усвідомити: це інша парадигма, і звичні REST-звички (подивитись JSON у Network, асертити HTTP-код) тут не працюють напряму.
Що таке .proto-файл і чому числа біля полів — це не значення?
.proto — це файл, який описує контракт gRPC-сервісу: сам сервіс, його методи (rpc) і структуру повідомлень зі строго типізованими полями. Це одночасно схема, генератор коду й документація. Числа 1, 2 біля полів — це не значення й не дефолти, а номери полів (field numbers): саме за номером, а не за назвою, поле впізнається в байтах. Головне правило: номер не можна перепризначати чи перевикористовувати, а видаляючи поле, його номер треба зарезервувати, щоб ніхто випадково не зайняв його знову. Сумісність protobuf працює в обидва боки — і зворотна, і пряма, — але не автоматично: старий код читає нові повідомлення, ігноруючи нові поля, лише якщо дотримуватися кількох простих практик оновлення .proto. Змінити номер наявного поля — ламальна зміна контракту, і саме таке має ловити контрактний тест.
Які є типи викликів у gRPC і чим вони відрізняються?
Їх чотири. Unary — клієнт шле одне повідомлення й отримує одне у відповідь (звичайний запит балансу). Server streaming — клієнт шле одне, а сервер відповідає потоком повідомлень (підписка на оновлення ціни). Client streaming — клієнт шле потік, сервер відповідає одним (завантаження великого файлу частинами). Bidirectional streaming — обидва шлють потоки одночасно (чат, двобічна телеметрія). Ключове для AQA: три з чотирьох типів — стрімінгові, а це вже не звичне «запит-відповідь», яке легко заасертити одним рядком, тому стратегія перевірки стріму складніша. На співбесіді достатньо назвати всі чотири й навести приклад до кожного.
Чому gRPC-виклик не подивишся в DevTools і які в нього статус-коди?
Бо gRPC передає бінарні дані, а не текстовий JSON, тож звична вкладка Network майже безпорадна: замість читабельного тіла ти побачиш бінарні фрейми. Крім того, gRPC має власні , а не HTTP: OK (0), INVALID_ARGUMENT (3), DEADLINE_EXCEEDED (4), NOT_FOUND (5), UNAUTHENTICATED (16) і так далі. Плутати gRPC-статус із HTTP-статусом — типова помилка новачка: NOT_FOUND у gRPC — це код 5 у власній системі, а не HTTP 404, тож асерт на HTTP-код для gRPC безглуздий. Тому для gRPC перевіряєш саме gRPC-статус і поля відповіді, а не HTTP-код транспорту.
Що таке grpcurl і чому йому потрібна схема?
grpcurl — це «curl для gRPC», канонічний CLI-інструмент, який дозволяє слати gRPC-виклики руками. Він приймає JSON на вхід і показує JSON на виході, а бінарне кодування бере на себе, тож ти працюєш зі зрозумілим текстом. Але щоб знати форму викликів і як серіалізувати запит, йому потрібен або .proto-файл, або ввімкнена на сервері server reflection (сервіс сам віддає свою схему). Без схеми grpcurl просто не знає структуру повідомлення й падає — і це не баг сервісу, а забутий контракт. Тому типова пастка звучить так: «виглядає як помилка gRPC-сервера, а насправді ти не подав схему».
Що таке вебхук і чому його складно тестувати?
Вебхук (webhook) перевертає звичний напрямок виклику: замість того щоб ти слав запит і отримував відповідь, зовнішня система сама викликає тебе — робить HTTP-POST на твій URL, щойно стається подія. «Не дзвони нам — ми подзвонимо тобі»: платіжка стукає, коли оплата пройшла, GitHub — коли є push. Складність тестування в тому, що ти тут , а не ініціатор: подію тригерить хтось інший, прилетить вона асинхронно й невідомо коли, а провайдер ще й може прислати її повторно. Немає синхронної відповіді, яку звично заасертити одразу після запиту. Саме тому вебхуки ламають тести, написані за звичкою «відправив запит — перевірив тіло».
Як правильно протестувати вебхук?
Це питання про стратегію, і сильна відповідь — це послідовність кроків, а не «поставлю sleep і гляну». Перше: підняти приймач — локальний HTTP-сервер, що ловить POST і зберігає тіло; для реального провайдера localhost треба виставити назовні (ngrok і аналоги). Друге: стригерити подію — через API продукту або провайдера, наприклад тестову оплату. Третє: дочекатися доставки із , а не фіксованим sleep, бо подія асинхронна й sleep то марно чекає, то не дочікується. Четверте: перевірити те, що прилетіло — схему тіла, підпис і, головне, побічний ефект у твоїй системі (замовлення справді стало paid). Бонус на співбесіді — згадати й дублікати.
Навіщо вебхуку підпис і як його перевіряти?
Вебхук приходить на публічний URL, тож будь-хто може підробити POST і сказати «оплата пройшла» — без захисту система прийме подію на віру. Щоб цьому запобігти, провайдер підписує тіло, зазвичай через HMAC (hash-based message authentication code) зі спільним секретом, і кладе підпис у заголовок. Приймач має перерахувати HMAC від сирого тіла тим самим секретом і звірити з тим, що в заголовку; не збіглося — запит відкинути. Для QA це означає обов'язковий негативний тест: перевірити, що продукт валідує підпис і відкидає підроблений чи зіпсований запит, а не тільки що проходить. Перевірка підпису — не опція, а частина безпеки інтеграції.
Що таке черга повідомлень і яку проблему вона розв'язує?
Черга повідомлень (message queue) — це спосіб спілкування сервісів через -брокер замість прямого виклику: продюсер кладе повідомлення в брокер і йде далі, не чекаючи, а консюмер колись його забере й обробить. Це розв'язує зв'язність сервісів — вони не знають одне про одного напряму, — згладжує піки навантаження й дає стійкість: якщо консюмер лежить, повідомлення чекають у брокері, а не губляться. Головна відмінність від запиту-відповіді в тому, що продюсер не отримує синхронної відповіді, тож заасертити нема чого за звичкою. Для QA це той самий асинхронний виклик, що й вебхуки: перевіряти треба наслідок або читати чергу напряму.
Чим Kafka відрізняється від RabbitMQ для тестувальника?
RabbitMQ — це класичний брокер повідомлень: продюсер шле в exchange, той маршрутизує в черги, консюмери розбирають, а після підтвердження (ack) повідомлення зникає. Kafka — це радше розподілений лог: повідомлення пишуться в topic, поділений на , і лишаються там на час retention; консюмери читають, рухаючи свій offset, і те саме повідомлення можуть прочитати різні групи. Практична різниця для QA в тому, що в Kafka повідомлення можна перечитати й після обробки (offset відмотується назад), а в RabbitMQ після ack його вже нема. Ще нюанс порядку: у Kafka порядок гарантований лише в межах однієї партиції, не між ними, тож на глобальний порядок у розподіленій системі розраховувати не варто.
Які бувають гарантії доставки і що не так з обіцянкою exactly-once?
Гарантій три. At-most-once — можлива втрата: доставимо не більше разу, але можемо й загубити. At-least-once — можливі дублікати: доставимо щонайменше раз, але можемо й повторити; у Kafka це задокументований дефолт, а в RabbitMQ той самий режим дає ручне підтвердження з поверненням непідтвердженої доставки в чергу. Exactly-once — рівно один раз, але це складна й обмежена гарантія: часто те, що продають як exactly-once, насправді є «at-least-once + ідемпотентний консюмер», а не магія самого брокера. На співбесіді чесніше сказати саме так, ніж впевнено заявити «Kafka гарантує exactly-once» без застережень — інтерв'юер якраз і мацає, чи розумієш ти нюанс. Практичний висновок: раз домінує at-least-once, дублікати — це нормальний режим роботи, а не аварія.
Що таке dead letter queue і навіщо вона?
Dead letter queue (DLQ), , — це місце, куди брокер перепубліковує повідомлення, яке не поїхало далі штатним шляхом. Найвідоміший сценарій — poison message, повідомлення, що завжди падає при обробці: якщо брокер віддаватиме його знову й знову, консюмер зациклиться. Але вичерпані спроби — лише один із чотирьох шляхів у dead letter, і в RabbitMQ він привʼязаний до quorum-черг (перевищено delivery-limit). Решта три: negative acknowledgement із requeue=false; спливання власного TTL повідомлення; відкидання за лімітом довжини черги. Окремо варто знати пастку: якщо спливає вся черга, її повідомлення в dead letter не потрапляють, тож порожня DLQ ще не доводить, що втрат не було. Для QA кожен із цих шляхів — окремий сценарій.
Що таке ідемпотентність консюмера і чому вона обов'язкова?
означає, що обробка того самого повідомлення двічі дає той самий результат, що й один раз. Вона обов'язкова тому, що домінантна гарантія доставки — at-least-once, тож дублікати неминучі: ретрай вебхука після таймауту, повторна доставка після падіння консюмера, повтор продюсера при мережевому збої. Якщо консюмер не ідемпотентний, наслідок жорсткий — подвійне списання коштів, два однакові листи, дубль замовлення. Це та сама ідея, що й Idempotency-Key для вхідних POST-запитів, тільки з боку отримувача. Механізми дедуплікації, які варто впізнавати: унікальний id повідомлення плюс таблиця оброблених id, унікальне обмеження (unique constraint) у БД, або замість чистого insert.
Прилетів той самий вебхук двічі. Це баг?
Не обов'язково. При гарантії at-least-once дубль доставки очікуваний: система радше доставить повідомлення двічі, ніж втратить його, тож повтор після таймауту чи падіння — норма, а не аварія. Баг тут інший: він з'являється, лише якщо консюмер неідемпотентний і на другій доставці зробив ефект удруге — два платежі, два листи, дубль замовлення. Тобто плутати симптом (дубль прилетів) із причиною (дубль спричинив другий ефект) — помилка. Правильна реакція QA: не заводити баг на сам факт повтору, а перевірити, чи побічний ефект стався рівно один раз. Якщо після двох однакових доставок з'явилося два платежі — це неідемпотентний консюмер, і саме твій тест мусить його спіймати.
Який головний тест асинхронної системи?
Достав те саме повідомлення (чи той самий вебхук) двічі — і переконайся, що побічний ефект стався рівно один раз: один платіж, один лист, одне замовлення. Це прямо випливає з того, що at-least-once робить дублікати нормою, а отже, єдиний надійний захист — ідемпотентність консюмера, і саме її треба перевіряти. Слабке виглядає так: перевірили одну успішну доставку (happy-path) — і все; а повтор, підроблений підпис і DLQ лишилися непокритими, хоча для асинхронних систем саме ці сценарії найцінніші. Тому тест на подвійну доставку — не екзотика, а обов'язковий елемент. Якщо він червоний, ти знайшов не «баг черги», а неідемпотентного консюмера.
Як звучить чесна відповідь про SOAP чи Kafka на співбесіді, якщо руками ти з ними не працював?
Ця тема — саме та, де сеньйор виграє чесністю, а не бравадою: практична експертиза в Kafka чи SOAP-стеку — окрема глибока спеціалізація, і від QA/AQA переважно чекають правильну модель, а не досвід адміністрування брокера. Сильна відповідь окреслює межу вголос: «gRPC — бінарний RPC, за замовчуванням із protobuf, контракт у .proto, чотири типи викликів, тестую через grpcurl від контракту; але продакшн-стрімінг під навантаженням руками не гоняв». Це набагато краще за впевнене плавання в деталях, яких не знаєш. Погана відповідь — вдавати глибину: заявити «Kafka гарантує exactly-once» без застережень, переплутати вебхук зі стрімінгом, назвати дублікати доставки багом брокера. Інтерв'юер якраз і перевіряє, чи відрізняєш ти «я це проєктував» від «я знаю, як це влаштовано» — і назвати межу є ознакою сеньйорності, а не слабкості.
Три кейси, де ламаються тести, написані за звичкою «відправив запит — перевірив тіло»: SOAP Fault, що причаївся під зеленим 200 OK; ручний виклик gRPC через grpcurl, коли DevTools безпорадні; і головний тест асинхронної системи — подвійна доставка вебхука проти ідемпотентності консюмера. Скрізь — що дивитися і чому.
Кейс 1. SOAP Fault під 200 OK: чому зелений статус бреше
Тест шле SOAP-запит на переказ, отримує 200 OK, на статус зелений — а гроші не переказалися. Причина класична: сервіс поклав помилку не в HTTP-код, а в тіло , у вигляді SOAP Fault. Ось що реально прийшло:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<soap:Fault>
<soap:Code><soap:Value>soap:Sender</soap:Value></soap:Code>
<soap:Reason>
<soap:Text xml:lang="en">Insufficient funds on ACC-42</soap:Text>
</soap:Reason>
</soap:Fault>
</soap:Body>
</soap:Envelope>
HTTP-код — 200, а операція провалилась. Тест, який перевіряє лише статус транспорту, цього не побачить. Правильна перевірка — асертити відсутність елемента Fault у Body, а не тільки код:
import { test, expect } from '@playwright/test';
test('SOAP: успішний переказ не містить Fault у конверті', async ({ request }) => {
const res = await request.post('https://bank.example.com/soap/AccountService', {
headers: { 'Content-Type': 'application/soap+xml' },
data: transferEnvelope('ACC-42', 'ACC-7', 100),
});
// статусу замало — реалізації кладуть Fault навіть під 200
const body = await res.text();
expect(body, 'у Body лежить SOAP Fault — операція провалилась').not.toContain('<soap:Fault>');
expect(body).toContain('<transferResult>OK</transferResult>');
});
Що дивитися і чому:
200 OKу SOAP не означає «операція вдалась». За специфікацією Fault їде зі статусом500(у SOAP 1.2 помилка відправника —400), але на практиці багато реалізацій віддають Fault під200. Тому успіху — це тіло конверта, а не HTTP-код.- Fault 1.1 vs 1.2 — різні поля. У SOAP 1.1 це
faultcode/faultstring, у 1.2 —Code/Reason. Асерт «немає Fault» треба звіряти з версією конкретного сервісу, інакше перевірка шукає не той елемент. - SoapUI зручніший саме тут. Він читає WSDL і дає асертити відповідь за XPath (наприклад, «елемент
Faultвідсутній»), не парсячи XML руками. Для регулярного SOAP-тестування це надійніше, ніжtoContainпо рядку. - Це той самий клас пасток, що й у REST/GraphQL. «200 з помилкою в тілі» і масив
errorsу GraphQL — рідні брати SOAP Fault: усюди перевіряй тіло, а не лише статус.
Кейс 2. grpcurl: викликати gRPC руками, коли Network безпорадна
Тобі кажуть «перевір, що AccountService/GetBalance повертає правильний баланс», але це gRPC — у DevTools замість JSON бінарні фрейми. Ручний інструмент тут — grpcurl, «curl для gRPC». Він приймає JSON на вхід і показує JSON на виході, а бінарне кодування бере на себе:
# зі схемою з .proto-файлу
grpcurl -plaintext -import-path ./proto -proto account.proto \
-d '{"account_id": "ACC-42"}' \
localhost:50051 AccountService/GetBalance
{
"cents": "125000",
"currency": "UAH"
}
Якщо сервер увімкнув server reflection, .proto не потрібен — сервіс сам віддасть схему, і можна спершу переглянути список методів:
grpcurl -plaintext localhost:50051 list AccountService
# AccountService.GetBalance
А ось як виглядає негативний сценарій — неіснуючий акаунт повертає gRPC-статус, а не HTTP-код:
ERROR:
Code: NotFound
Message: account ACC-999 not found
Що дивитися і чому:
- Без схеми grpcurl не працює — і це не баг сервісу. Немає ні
.proto, ні server reflection — grpcurl не знає, як серіалізувати запит, і падає. Пастка «виглядає як помилка gRPC-сервера, а насправді забув подати контракт». Code: NotFound— це gRPC-статус 5, а не HTTP 404. gRPC має власну систему кодів (OK0,INVALID_ARGUMENT3,DEADLINE_EXCEEDED4,NOT_FOUND5,UNAUTHENTICATED16). Асертити треба саме на них; асерт на HTTP-код для gRPC безглуздий.- Стратегія тесту та сама, що для REST. Перевірки статусу (тут — gRPC-статусу), полів відповіді, меж і негативних сценаріїв; змінюється лише джерело контракту —
.protoзамість OpenAPI, і бінарний транспорт, який grpcurl ховає за JSON. -plaintext— це про TLS, не про формат. означає з'єднання без шифрування (типово для локального сервера); дані на вході все одно JSON. Не плутати з «показати бінарь як текст».
Кейс 3. Головний тест асинхронної системи: подвійна доставка проти ідемпотентності
Продукт приймає вебхук оплати від провайдера й ставить замовлення в paid. (один вебхук → замовлення оплачене) команда вже покрила. Але доставку вебхуків будують за тією ж моделлю at-least-once, тож провайдер цілком може прислати ту саму подію двічі. Найцінніший тут не happy-path, а перевірка ідемпотентності: достав той самий вебхук двічі й доведи, що платіж стався рівно один раз.
import { test, expect } from '@playwright/test';
import crypto from 'node:crypto';
const SECRET = process.env.WEBHOOK_SECRET!;
function sign(body: string): string {
return crypto.createHmac('sha256', SECRET).update(body).digest('hex');
}
// полінг наслідку замість sleep — подія асинхронна
async function waitForOrderPaid(request, orderId: string, timeoutMs = 10_000) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await request.get(`/api/orders/${orderId}`);
if ((await res.json()).status === 'paid') return;
await new Promise((r) => setTimeout(r, 300));
}
throw new Error(`замовлення ${orderId} не стало paid за ${timeoutMs}ms`);
}
test('дубль вебхука не створює другий платіж (ідемпотентність консюмера)', async ({ request }) => {
const orderId = `ord-${Date.now()}`;
// однакове тіло з однаковим event id — саме той дубль, який шле провайдер при ретраї
const body = JSON.stringify({ event_id: 'evt-abc-123', order_id: orderId, type: 'payment.succeeded' });
const headers = { 'X-Signature': sign(body), 'Content-Type': 'application/json' };
// перша доставка
const first = await request.post('/webhooks/payments', { headers, data: body });
expect(first.status()).toBe(200);
await waitForOrderPaid(request, orderId);
// друга доставка — той самий payload, той самий підпис
const second = await request.post('/webhooks/payments', { headers, data: body });
expect(second.status(), 'повтор має прийматись штатно, а не 500').toBe(200);
// головний асерт: ефект стався РІВНО один раз
const payments = await (await request.get(`/api/orders/${orderId}/payments`)).json();
expect(payments, 'два платежі = неідемпотентний консюмер').toHaveLength(1);
});
І обов'язковий негативний тест на підпис — підроблений POST має бути відкинутий:
test('вебхук з невалідним підписом відкидається', async ({ request }) => {
const body = JSON.stringify({ event_id: 'evt-x', order_id: 'ord-x', type: 'payment.succeeded' });
const res = await request.post('/webhooks/payments', {
headers: { 'X-Signature': 'deadbeef', 'Content-Type': 'application/json' },
data: body,
});
expect(res.status(), 'підроблений підпис має відхилятись').toBe(401);
});
Що дивитися і чому:
- Оракул — побічний ефект, а не відповідь на
POST. Вебхук приймається асинхронно, тож200на самPOSTще нічого не доводить. Перевіряти треба стан у системі:payments.length === 1після двох доставок. - , а не
sleep.waitForOrderPaidтисне доти, доки стан не зміниться, або падає по . Фіксованийsleep(3000)то марно чекав би, то не дочікувався — класичний асинхронних тестів. - Однаковий
event_id— суть перевірки. Дедуплікація тримається на унікальному id події: другийPOSTіз тим самимevent_idконсюмер має мовчки проігнорувати. Якщо змінити id, ти тестуєш дві різні події, а не . - Дубль — не баг, неідемпотентність — баг. Якщо після двох однакових доставок з'явилося два платежі, це не «баг черги/провайдера», а неідемпотентний консюмер. Саме цей тест мусить його спіймати — happy-path його не бачить.
SOAP і WSDL
- Можу пояснити, що SOAP — це XML- (
Envelope→Header→Body) поверх HTTP, де HTTP лише транспорт, а вся семантика операції в тілі, а не в URL/методі (у REST — навпаки: операція читається з маршруту й методу). - Розумію, що WSDL для SOAP — це те саме, чим OpenAPI для REST: машиночитний контракт з операціями, повідомленнями й типами (XSD).
- Знаю, що SOAP Fault — це елемент у
Body, а не обов'язково HTTP-помилка: реалізації часто кладуть Fault навіть під200 OK, тож перевіряти треба тіло, а не лише статус. - Можу пояснити, чому SOAP досі живий (стек WS-*, зокрема WS-Security на рівні повідомлення) і що канонічний інструмент — SoapUI/ReadyAPI (читає WSDL, відповідь за XPath).
gRPC і protobuf
- Можу пояснити, що gRPC — це RPC-фреймворк, який за замовчуванням бере protobuf і як мову опису інтерфейсу, і як бінарну серіалізацію (альтернативи можливі), де клієнт викликає метод сервісу як локальну функцію, а контракт живе в
.proto(сервіс,rpc-методи, типізовані повідомлення). - Розумію, що числа біля полів — це номери полів (field numbers), а не значення; номер не можна перепризначати чи перевикористовувати, а видаляючи поле, його номер треба зарезервувати. Сумісність protobuf працює в обидва боки, але лише за дотримання правил оновлення
.proto. - Знаю чотири типи викликів (unary, server streaming, client streaming, bidirectional streaming) і приклад до кожного.
- Розумію, чому gRPC-виклик не подивишся в DevTools як JSON (бінарний формат замість тексту) і що в нього власні , а не HTTP (
NOT_FOUND— це код 5, а не HTTP 404); grpcurl потребує.protoабо server reflection, інакше не серіалізує запит.
Вебхуки
- Можу пояснити, що вебхук перевертає напрямок: зовнішня система сама робить
POSTна твій URL при події («не дзвони нам — ми подзвонимо тобі»). - Знаю стратегію тесту вебхука: підняти + → стригерити подію → дочекатися (не
sleep) → перевірити схему, підпис і побічний ефект;sleep(3000)замість полінгу дає . - Можу пояснити, навіщо підпис (HMAC зі спільним секретом) і що перевірка відкидання підробленого/зіпсованого підпису — обов'язковий негативний тест.
- Розрізняю тестування вебхуків як (правильно приймаємо/обробляємо) і як продюсера (чи генерується подія, , підпис, формат).
Черги повідомлень
- Знаю різницю Kafka vs RabbitMQ: Kafka — розподілений лог (topic//offset, повідомлення лишаються на retention, можна перечитати), RabbitMQ — черга з ack (після ack повідомлення нема).
- Розумію, що продюсер не дає синхронної відповіді, тож асертити треба наслідок або читати топік/чергу напряму.
- Знаю три гарантії доставки (at-most-once, at-least-once, exactly-once) і чому exactly-once часто насправді «at-least-once + ідемпотентний консюмер».
- Можу пояснити DLQ і назвати чотири шляхи туди (negative acknowledgement із
requeue=false, власний TTL повідомлення, перевищений ліміт довжини черги, перевищенийdelivery-limitу quorum-черзі), пастку «сплила вся черга — повідомлення в DLQ не потрапляють» і межі порядку (у Kafka порядок гарантований лише в межах партиції, не між ними).
Ідемпотентність і дедуплікація
- Розумію, чому at-least-once робить дублікати нормою й що консюмер зобов'язаний бути ідемпотентним (інакше — подвійне списання, два листи, дубль замовлення); механізми дедуплікації — унікальний
id+ таблиця оброблених, unique constraint у БД, замість чистого insert. - Пам'ятаю головний тест асинхронної системи: достав те саме повідомлення двічі й доведи, що ефект стався рівно один раз; «вебхук прийшов двічі» — не баг, баг — коли неідемпотентний консюмер зробив ефект двічі (симптом ≠ причина).
Межа знань на співбесіді
- Можу назвати межу вголос: «модель знаю, продакшн-стрімінг/адміністрування брокера руками не гоняв» — і розумію, що це ознака сеньйорності, а не слабкості.
- Знаю типові провальні відповіді: «Kafka гарантує exactly-once» без застережень, плутанина вебхука зі стрімінгом, дублікати доставки як «баг брокера».
Квіз
Перед стартом
- Питань: 14
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
SOAP — що це і де живе семантика операції?