Тестування WebSocket та gRPC


WebSocket

Що таке WebSocket

WebSocket — двостороннє постійне з'єднання між клієнтом і сервером. Обидві сторони можуть надсилати повідомлення в будь-який момент без нового запиту.

На відміну від HTTP (запит → відповідь → з'єднання закрито), WebSocket тримає з'єднання відкритим весь час.

Де використовується: чат, live-оновлення цін, сповіщення, онлайн-ігри, трейдингові платформи, колаборативні редактори.


WebSocket lifecycle

1. Handshake (HTTP Upgrade)

GET /ws HTTP/1.1
Host: api.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
 
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

2. З'єднання відкрите — обмін повідомленнями

// Client → Server
{"type": "subscribe", "channel": "prices", "symbol": "BTC"}
 
// Server → Client (пуш)
{"type": "update", "symbol": "BTC", "price": 67420.5}
 
// Client → Server (keepalive)
{"type": "ping"}
 
// Server → Client
{"type": "pong"}

3. Закриття

Client → Server: Close frame (code 1000 — normal closure)
Server → Client: Close frame (echo)
З'єднання закрито

WebSocket Close Codes

КодЗначення
1000Normal closure
1001Going away (сервер перезапускається)
1006Аномальне закриття (без close frame)
1009Message too big
4000–4999Кастомні коди додатку

Тест-кейси для WebSocket

З'єднання та lifecycle

#ТестОчікуваний результат
WS-01Успішне підключення101, з'єднання відкрите
WS-02Підключення без токена401, відхилено
WS-03Невалідний токен401 або close(4001)
WS-04Прострочений токен під час сесіїСервер закриває з'єднання
WS-05Reconnect після розривуПереприєднання, missed-повідомлення

Повідомлення

#ТестОчікуваний результат
WS-06Валідне JSON повідомленняСервер обробляє, відповідь/broadcast
WS-07Невалідний JSONError-повідомлення, з'єднання не закривається
WS-08Порожнє повідомленняІгнорується або error
WS-09Дуже велике повідомленняClose(1009) або error
WS-10Порядок повідомленьКлієнт отримує в правильному порядку

Security

#ТестОчікуваний результат
WS-11ws:// замість wss://Відхилено в продакшені
WS-12Origin з чужого доменуВідхилено
WS-13Підписка на чужий channelНе отримує чужі повідомлення
WS-14Flood повідомленнями (1000/сек)Rate limiting, не падає

Edge Cases

#ТестОчікуваний результат
WS-15Розрив мережіСервер детектує через heartbeat
WS-16Кілька з'єднань одного юзераНемає дублювання повідомлень
WS-17Одночасні повідомлення від N клієнтівНемає race conditions

Інструменти для WebSocket

Postman

New Request → WebSocket Request
URL: wss://api.example.com/ws?token=abc
Connect → Send Messages

wscat (CLI)

npm install -g wscat
 
# Підключитись
wscat -c wss://api.example.com/ws
 
# З авторизацією
wscat -c wss://api.example.com/ws \
  -H "Authorization: Bearer TOKEN"
 
# Надіслати повідомлення
> {"type": "subscribe", "channel": "prices"}
< {"type": "subscribed", "channel": "prices"}

Browser DevTools

Network → WS → вибрати з'єднання → Messages
Видно всі фрейми в реальному часі з timestamp

gRPC

Що таке gRPC

gRPC — RPC-фреймворк від Google поверх HTTP/2 з бінарним протоколом Protocol Buffers.

RESTgRPC
ПротоколHTTP/1.1 або 2HTTP/2
ФорматJSON (текст)Protocol Buffers (бінарний)
КонтрактOpenAPI/Swagger.proto файл
СтрімінгНі (тільки SSE)4 типи
ШвидкістьСередняДуже висока

Де використовується: мікросервісна комунікація, real-time streaming, mobile backends, гейтвеї.


.proto файл — основа gRPC

syntax = "proto3";
 
package user;
 
service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListUsersRequest) returns (stream User);
  rpc CreateUser (stream CreateUserRequest) returns (CreateSummary);
  rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
 
message GetUserRequest {
  int64 user_id = 1;
}
 
message User {
  int64  id    = 1;
  string name  = 2;
  string email = 3;
}

4 типи gRPC комунікації

1. Unary RPC — один запит → одна відповідь

Client: GetUser(id: 42)  →
                          ← Server: User{id: 42, name: "Anna"}

Тест-кейси:

  • Валідний запит → OK + об'єкт
  • Неіснуючий ID → NOT_FOUND
  • ID = 0 або від'ємний → INVALID_ARGUMENT
  • Без metadata (токен) → UNAUTHENTICATED
  • Чужий ресурс → PERMISSION_DENIED

2. Server Streaming — один запит → потік відповідей

Client: WatchPrices("BTC")  →
                             ← Server: {price: 67420}
                             ← Server: {price: 67430}
                             ← Server: {price: 67415}
                             ← Server: END_STREAM

Тест-кейси:

  • Стрім запускається → перше повідомлення прийшло
  • Формат кожного повідомлення → всі поля присутні
  • Закриття стріму → клієнт отримує END_STREAM
  • Скасування клієнтом → сервер зупиняє відправку
  • Помилка посеред стріму → клієнт отримує status error

3. Client Streaming — потік запитів → одна відповідь

Client: Metric{name: "cpu", value: 80}  →
Client: Metric{name: "mem", value: 60}  →
Client: Metric{name: "disk", value: 45} →
Client: END_STREAM                      →
                                         ← Server: Summary{recorded: 3}

Тест-кейси:

  • Відправити N повідомлень → summary.recorded = N
  • Порожній стрім → обробляється gracefully
  • Одне невалідне з N → summary.failed = 1
  • Клієнт не закрив стрім → server timeout

4. Bidirectional Streaming — потік ↔ потік

Client: ChatMessage →
                     ← Server: ChatMessage
Client: ChatMessage →
                     ← Server: ChatMessage

Тест-кейси:

  • Обидва стріми відкриті → повідомлення йдуть в обидві сторони
  • Клієнт закрив вхідний стрім → сервер доопрацьовує і закривається
  • Помилка в одному стрімі → другий також закривається

gRPC статус коди (замість HTTP)

gRPC кодАналог HTTPКоли виникає
OK200Успіх
NOT_FOUND404Ресурс не існує
INVALID_ARGUMENT400Невалідний вхід
UNAUTHENTICATED401Відсутній або невалідний токен
PERMISSION_DENIED403Немає прав
ALREADY_EXISTS409Дублікат
RESOURCE_EXHAUSTED429Rate limit
UNAVAILABLE503Сервіс недоступний
DEADLINE_EXCEEDED504Таймаут
INTERNAL500Внутрішня помилка

Інструменти для gRPC

grpcurl (основний інструмент)

# Перелік сервісів
grpcurl -plaintext localhost:50051 list
 
# Перелік методів сервісу
grpcurl -plaintext localhost:50051 list UserService
 
# Виклик методу
grpcurl -plaintext \
  -d '{"user_id": 42}' \
  localhost:50051 \
  UserService/GetUser
 
# З авторизацією
grpcurl \
  -H "authorization: Bearer TOKEN" \
  -d '{"user_id": 42}' \
  api.example.com:443 \
  UserService/GetUser
 
# Server streaming
grpcurl -plaintext \
  -d '{"symbol": "BTC"}' \
  localhost:50051 \
  PriceService/WatchPrice

Postman

New Request → gRPC
URL: grpc://localhost:50051
Import .proto файл → вибрати метод → заповнити поля

Evans (інтерактивний REPL)

evans --host localhost --port 50051 --proto user.proto
 
# В REPL:
> package user
> service UserService
> call GetUser
user_id (TYPE_INT64) => 42

WebSocket vs gRPC: ключова різниця

WebSocketgRPC
СхемаНемає (домовляються неформально)Строга (.proto файл)
ФорматБудь-який (JSON, binary)Protocol Buffers
Тип зв'язкуБраузер ↔ СерверСервіс ↔ Сервіс
ТипізаціяРучна валідаціяКомпілятор гарантує
ТестуванняLifecycle + структура вручнуБізнес-логіка + статус коди

Питання на співбесіді

«Чим WebSocket відрізняється від HTTP?»

HTTP — запит-відповідь, після відповіді з'єднання закривається. WebSocket — постійне двостороннє з'єднання, обидві сторони можуть надсилати повідомлення в будь-який момент без нового запиту.

«Яка різниця між REST і gRPC?»

REST — HTTP/1.1, JSON, один ресурс = один URL. gRPC — HTTP/2, Protocol Buffers (бінарний), контракт у .proto файлі, підтримує 4 типи стрімінгу. gRPC значно швидший для мікросервісної комунікації.

«Що таке metadata в gRPC?»

Metadata в gRPC — аналог HTTP headers. Використовується для передачі авторизаційних токенів, трейсинг ID, локалізації. Передається поза тілом повідомлення.

«Як тестувати gRPC без UI?»

За допомогою grpcurl (CLI), Evans (REPL), або Postman з підтримкою gRPC. grpcurl дозволяє викликати будь-який метод з командного рядка і передавати JSON-аргументи.