Основи автоматизованого тестування

Iryna Volnykh

+ 7 years in E-commerce
+ 6 years as a QA engineer

 

ISTQB Certified Tester

Manual/Automation QA engineer

QA lead at

Course teacher at

Автоматизація - це лише засіб тестування

Процеси, які можуть бути автоматизовані:

 

> регресійне та smoke тестування;

> тестування продуктивності;

> тестування навантаження;

> тестування методом білого ящика (за допомогою інструментів модульного тестування);

> аналіз покриття коду (наприклад використання статичних аналізаторів коду, що допомагають уникнути помилок у коді і так званого мертвого коду (такого, що не використовується));

> Тестування інтеграцій або інтеграційне тестування;

Як пишуться тести і якими мають бути тести

Складання тестів починається зі збору списку вимог до тестованого коду. Потім для кожного вимоги пишеться тестовий сценарій, який перевіряє, що воно виконується. Сценарії бувають позитивними - коли ми передаємо коректні дані і чекаємо успішний результат, і негативними - коли ми передаємо неправильні дані і чекаємо повідомлення про помилку.

На кожну вимогу ми пишемо окремий тест - це дозволить при помилці зрозуміти, що саме зламалося. Тести зазвичай пишуть в стилі Arrange, Act, Assert. Ми спочатку готуємо і налаштовуємо потрібні компоненти (Arrange), виконуємо дію (Act) і перевіряємо результат (Assert).

> маленькими - ідеальний тест укладається в 5-15 рядків і перевіряється тільки одна вимога. Він повинен легко читатися, щоб з першого погляду був очевидний сценарій, за яким відбувається тестування.

> повторюваним - тест повинен видавати однаковий результат при кожному запуску. Варто уникати використання випадкових величин, так як інакше може вийти «нестабільний» (flaky) тест, який то працює, то ні, і кожен раз треба витрачати час, і шукати причину.

Вимога:

Функція пошуку на веб-сайті повинна дозволяти користувачам знаходити та отримувати доступ до відповідної інформації чи сторінок на веб-сайті.

Test case: функція пошуку на веб-сайті

Steps (або тестовий cценарій):

1. Перейдіть до панелі пошуку веб-сайту.

2. Введіть дійсний пошуковий запит у пошуковий рядок і натисніть клавішу «Enter».

Expected result:

1. Переконайтеся, що відображені результати пошуку відповідають пошуковому запиту.

2. Переконайтеся, що результати пошуку відображаються чітко та впорядковано.

3. Переконайтеся, що результати пошуку доступні для кліків і ведуть на відповідні сторінки чи інформацію.

Selenium та його продукти: Selenium IDE, Selenium  WebDriver, Selenium Grid

Selenium — це масштабний open source проєкт, а точніше, browser automation framework, у межах якого розробляється серія програмних продуктів для автоматизованого тестування, які зазвичай використовуються для тестування вебдодатків. Ось посилання на офіційне джерело і документацію: https://www.seleniumhq.org/

Selenium WebDriver — це гнучкий інструмент для автоматизованого тестування вебпроєктів на базі набору бібліотек для різних мов програмування, як-от Java, .Net (C#), Python, Ruby, PHP, Perl, JavaScript. Цей інструмент підтримує роботу на базі Windows, macOS та Linux, а також найпоширеніші браузери Google Chrome, Firefox, Safari, Edge, Internet Explorer та навіть деякі браузери без графічного інтерфейсу. Selenium WebDriver найчастіше використовується з регресійним та функціональним тестуванням.

Архітектура Selenium WebDriver складається з чотирьох компонентів:

> драйвер конкретного браузера;

> клієнтська бібліотека Selenium;

> браузер;

> протокол JSON Wire (JavaScript Object Notation).

Selenium та його продукти: Selenium IDE, Selenium  WebDriver, Selenium Grid

Selenium Server — це сервер, який дозволяє керувати браузером мережею з віддаленої машини;

 

Selenium Grid — це частина фреймворку Selenium, яка спеціалізується на паралельному виконанні тестів у різних браузерах, операційних системах та девайсах.

Selenium Grid-кластер має два основні компоненти: hub i nodes.

Hub — сервер, який приймає запити на доступ від клієнта WebDriver і направляє тестові команди JSON на віддалені диски на nodes. Він проводить одночасне виконання тестів на декількох машинах, централізовано керуючи різними браузерами, що полегшує аналіз і порівняння результатів.

Node — віддалений пристрій, який складається з рідної ОС та віддаленого WebDriver. Він отримує запити від Hub у вигляді тестових команд JSON і виконує їх за допомогою WebDriver.

Selenium та його продукти: Selenium IDE, Selenium  WebDriver, Selenium Grid

Selenium IDE — плагін до браузера Firefox та Chrome, який може записувати дії користувача, відтворювати їх, а також генерувати код для WebDriver, в якому виконуються ті самі дії. Загалом це «Selenium-рекордер».

Тестувальники, які не вміють (або не бажають) програмувати, використовують Selenium IDE як самостійний продукт, без перетворення записаних сценаріїв на програмний код. Це, звичайно, не дає змогу розробляти досить складні тестові набори, але деяким вистачає і простих лінійних сценаріїв.

Для написання автоматизованих тестів, де ми будемо емулювати взаємодію користувача з системою, необхідно вміти знаходити локатори елементів. Найчастіше використовуються CSS-локатори та XPath.

СSS селектори

Xpath

Вибір за атрибутами:

Для вибору тегу на основі його атрибутів слід користуватися предикатами:

 

//*[@style]

Вибірка всіх елементів які мають класс style

//*[@class="chapter"]

Вибірка всіх елементів які мають class="chapter"

//h1[@class="bookTitle"]

Вибірка всіх елементів з h1 тегом що мають атрибут class="bookTitle" [4]

Selenide

Selenide - це фреймворк для автоматизованого тестування веб-додатків на основі Selenium WebDriver, що дає наступні переваги:

 

❤️ витончений API

❤️ Підтримка Ajax для стабільних тестів

❤️ потужні селектори

❤️ проста конфігурація

 

Документація: https://selenide.org/

Page Object Model

Коли ми пишемо тести для веб-сторінки, нам потрібно звертатися до елементів цієї веб-сторінки, щоб натискати посилання або визначати, що відображається. Однак, якщо ми пишемо тести, які безпосередньо маніпулюють елементами HTML, наші тести будуть нестійкими до змін інтерфейсу користувача. Об’єкт сторінки обгортає HTML-сторінку або фрагмент за допомогою програмного інтерфейсу API, що дозволяє нам керувати елементами сторінки, не копаючись та не звертаючись щоразу до елементу у HTML.

Page objects are a classic example of encapsulation - they hide the details of the UI structure and widgetry from other components (the tests).
                          (c) Martin Fowler, PageObject.

 

API testing: lesson +

Додаткове заняття:

 

1. Websocket

2. gRCP

 

це комунікаційний протокол, який забезпечує двосторонній обмін даними через одне, тривале TCP-з'єднання*. На відміну від HTTP, який працює за моделлю "запит-відповідь", WebSocket дозволяє постійний двосторонній обмін повідомленнями між клієнтом (наприклад, веб-браузером) та сервером. 

 

Основні характеристики WebSocket:

🤖  Двостороннє спілкування: клієнт і сервер можуть незалежно спілкуватися, без необхідності постійно встановлювати нові з'єднання.
🤖  Менше навантаження: менші витрати порівняно з традиційними HTTP-запитами.
🤖  Використання для реального часу: ідеально підходить для додатків, які потребують швидкого та постійного обміну даними.

* TCP-з'єднання — це комунікаційний механізм в мережах, що базується на протоколі передачі даних TCP (Transmission Control Protocol).

HTTP  базується на TCP. Коли ви відкриваєте вебсайт, HTTP використовує TCP для передачі даних між вашим комп'ютером (клієнтом) і сервером.

Основні моменти:

  1. Встановлення з'єднання: HTTP використовує TCP для надійного встановлення з'єднання. Клієнт і сервер створюють TCP-з'єднання перед тим, як почати обмін даними.

  2. Передача даних: HTTP передає веб-сторінки, зображення, відео та інші ресурси через TCP. TCP гарантує, що всі ці дані прийдуть без помилок і в правильному порядку.

  3. Закриття з'єднання: після того, як всі дані передані, TCP закриває з'єднання.

Отже, TCP забезпечує стабільність і надійність, тоді як HTTP визначає правила для обміну інформацією, наприклад, що саме передається та як це виглядає.

image source

1. Запит на встановлення з'єднання

(Handshake Request)

Перший крок — це HTTP-запит від клієнта до сервера для встановлення WebSocket-з'єднання. Це виглядає як звичайний HTTP-запит з додатковими заголовками.

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

 

GET /chat HTTP/1.1: Клієнт намагається підключитися до ресурсу /chat.
Upgrade: websocket: Цей заголовок специфічний для запитів WebSocket. Він повідомляє серверу, що клієнт хоче перейти з протоколу HTTP на протокол WebSocket.
Connection: Upgrade: Вказує, що з'єднання повинно бути оновлене.
Sec-WebSocket-Key: Унікальний ключ, який клієнт відправляє для аутентифікації з'єднання. Сервер використовує цей ключ для створення відповідного ключа для відповіді.
Sec-WebSocket-Version: Вказує версію WebSocket-протоколу (в більшості випадків це версія 13).

1. Запит на встановлення з'єднання

(Handshake Request)

Перший крок — це HTTP-запит від клієнта до сервера для встановлення WebSocket-з'єднання. Це виглядає як звичайний HTTP-запит з додатковими заголовками.

image source

2. Відповідь сервера на запит (Handshake Response)

Якщо сервер підтримує WebSocket і погоджується на з'єднання, він відповідає зі спеціальними заголовками, підтверджуючи перехід на WebSocket-протокол.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

 

HTTP/1.1 101 Switching Protocols: Це код відповіді, який вказує, що сервер погоджується на зміну протоколу.
Upgrade: websocket: Сервер підтверджує, що з'єднання буде переведено на WebSocket.
Connection: Upgrade: Вказує, що з'єднання оновлене.
Sec-WebSocket-Accept: Сервер генерує відповідний ключ на основі ключа клієнта (Sec-WebSocket-Key) для підтвердження безпеки.

image source

3. Передача даних (Data Frames)

Після успішного встановлення з'єднання, починається двостороння передача даних у вигляді фреймів (frames). Дані кодуються у форматі WebSocket frame, який може бути текстовим або бінарним.

Client: Hello, server!

 

Дані передаються у форматі фреймів WebSocket, що забезпечує мінімальні накладні витрати, оскільки не потрібно відкривати нові запити для кожного обміну даними, як у HTTP.

 

WebSocket підтримує два основні типи даних для передачі повідомлень:

  1. Текстові повідомлення (Text Frames) — це повідомлення у вигляді рядків тексту, зазвичай у форматі UTF-8. Цей тип підходить для передачі текстових даних, таких як JSON або інші текстові формати.

  2. Бінарні повідомлення (Binary Frames) — це повідомлення, які можуть містити будь-які бінарні дані, такі як зображення, аудіо, відео або серіалізовані дані.

 

Server: Hello, client!

Серіалізовані дані — це спосіб перетворення складних об'єктів або структур даних у формат, придатний для передачі через мережу або збереження у файл. Серіалізація дозволяє представити ці дані у вигляді послідовності байтів (бінарний формат) або тексту (наприклад, JSON, XML), яку потім можна відправити або зберегти.

Серіалізація — це процес перетворення об'єкта або структури даних (наприклад, масиву, об'єкта класу, словника) в послідовність байтів або символів, щоб її можна було:

- Передати через мережу.
- Зберегти у файлі або базі даних.
- Відправити в повідомленні (наприклад, через WebSocket або gRPC).

Data Frames

Фрейм складається з кількох частин:

FIN (1 біт): Це прапорець, який вказує, чи є цей фрейм останнім у повідомленні. Якщо встановлений у 1, це означає, що фрейм є останнім, і повідомлення завершено. Якщо 0 — будуть ще фрейми для цього повідомлення.

Opcode (4 біти): Вказує тип даних, які передаються:

0x0 — продовження попереднього фрейму.
0x1 — текстовий фрейм (корисне навантаження в форматі UTF-8).
0x2 — бінарний фрейм (дані в бінарному форматі).
0x8 — фрейм для закриття з'єднання.
0x9 — ping (для перевірки зв'язку).
0xA — pong (відповідь на ping).
Mask (1 біт): Цей біт вказує, чи масковане корисне навантаження (payload). У запитах від клієнта завжди встановлений у 1 (дані мають бути масковані). Для відповідей сервера він зазвичай 0.

Payload length: Це довжина корисного навантаження. Якщо довжина даних менша за 126 байтів, то ця частина займатиме 7 біт. Якщо довжина більше, то використовуються додаткові біти для представлення повної довжини.

Masking key (32 біти): Якщо Mask біт встановлений в 1, цей ключ використовується для маскування (шифрування) корисних даних. Маскування є обов'язковим для даних, які надсилає клієнт, щоб запобігти певним атакам на мережевому рівні.

Payload (змінна довжина): Це основні дані, які передаються (текст або бінарні дані).

metadata

payload

image source

4. Закриття з'єднання (Closing Frame)

Будь-яка зі сторін (клієнт або сервер) може закрити WebSocket-з'єднання. Це робиться шляхом відправлення спеціального фрейму закриття.

Frame Type: Close
Code: 1000 (Normal closure)

 

Код 1000 означає "нормальне закриття", що вказує, що з'єднання завершилося без помилок. Після відправлення цього фрейму обидві сторони більше не можуть передавати дані.

 

Інші коди закриття: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code

Що тестувати?

 

1. Тестування встановлення з'єднання

2. Тестування обміну повідомленнями

Що перевірити:

👉 Коректність відправлення та отримання повідомлень.

👉 Чи відповідає сервер на повідомлення клієнта.

👉 Чи підтримується правильний формат даних (наприклад, текст або бінарні дані).

3. Тестування помилок протоколу: Переконатися, що сервер коректно обробляє неправильні або непідтримувані повідомлення.
Що перевірити:
👉 Відправка некоректних фреймів (наприклад, неправильний формат).
👉  Як сервер реагує на некоректні або надто великі повідомлення.

4. Тестування закриття з'єднання - чекнути, що WebSocket-з'єднання закривається коректно.
Що перевірити:
👉 Закриття з'єднання з ініціативи клієнта та сервера.
👉 Чи надсилаються правильні коди закриття.
👉 Чи не залишається з'єднання відкритим після закриття.

5. Тестування навантаження (Load Testing) - k6, Gatling

6. Тестування безпеки
Що перевірити:
👉  чи з'єднання працює через wss:// (WebSocket Secure).
👉 Чи правильно працює автентифікація та авторизація при WebSocket-з'єднаннях.

 

gRPC (Google Remote Procedure Call*) — це сучасний високопродуктивний фреймворк віддалених викликів процедур. Він дозволяє додаткам працювати на різних машинах або в різних процесах і спілкуватися один з одним так, ніби вони є частинами одного додатку.

Основні особливості gRPC:

🤖 Використання HTTP/2 — gRPC працює на базі протоколу HTTP/2, який забезпечує двонаправлену потокову передачу, мультиплексування та ефективне управління з'єднаннями.

🤖 Протоколи на основі Protobuf — дані в gRPC передаються в форматі Protocol Buffers (Protobuf), що дозволяє ефективно серіалізувати й десеріалізувати повідомлення.

🤖 Підтримка різних мов програмування — gRPC підтримує багато мов програмування, таких як C++, Java, Python, Go, та інші.

🤖 Підтримка двонаправленого стримінгу — gRPC дозволяє здійснювати одночасну передачу даних у дві сторони.

Remote Procedure Call (RPC) — це метод, який дозволяє комп'ютерним програмам викликати функції або процедури, що виконуються на іншому комп'ютері, як ніби ці функції знаходяться локально. Простіше кажучи, з RPC програми можуть викликати функції на віддаленому сервері так само, як локальні функції, без необхідності турбуватися про деталі комунікації через мережу.

Як це працює:


Клієнт викликає функцію: клієнт викликає функцію на віддаленому сервері так само, як і локальну. Наприклад, клієнт може викликати метод addNumbers(3, 5), щоб додати два числа на сервері.

Запит передається через мережу: цей виклик перетворюється на повідомлення, яке містить ім'я функції, параметри і, можливо, іншу інформацію, необхідну для її виконання. Це повідомлення відправляється через мережу до сервера.

Сервер виконує функцію: сервер отримує це повідомлення, викликає відповідну функцію з переданими параметрами (у цьому випадку — addNumbers(3, 5)), виконує її і отримує результат.

Результат повертається клієнту: сервер формує відповідь з результатом і відправляє її назад клієнту.

Клієнт отримує результат: клієнт отримує відповідь і продовжує працювати з отриманим результатом (наприклад, сума чисел 3 і 5 — це 8).

👻 Клієнт (Client) - Клієнт формує запит і передає його через мережу. Клієнт використовує автоматично згенерований код на основі .proto файлу для формування запиту.

👹 Сервер (Server) - отримує RPC запит від клієнта і виконує відповідну функцію. Після виконання функції сервер повертає результат клієнту. У gRPC сервер також використовує код, згенерований на основі .proto файлу, для обробки запитів.

👾 Протокол передачі (Transport Protocol) - TCP/IP HTTP/2

👻 Формат повідомлень (Message Format)

 

  • RPC передає дані у вигляді повідомлень. Це можуть бути параметри, які клієнт відправляє на сервер, і результат, який сервер повертає.
  • У gRPC для передачі даних використовується формат Protocol Buffers (Protobuf). Це бінарний формат, який компактний і ефективний для серіалізації та десеріалізації даних.

👻 Файл визначення інтерфейсу (.proto файл)
Файл .proto у gRPC описує сервіси (функції) та структури даних (повідомлення), які можуть використовуватися для взаємодії між клієнтом і сервером. Це свого роду контракт, який визначає, які методи можуть викликати клієнти і які параметри необхідно передати.

 

syntax = "proto3";  // Використовуємо синтаксис Protocol Buffers версії 3.

package greeter;    // Пакет для організації коду.

// Визначаємо сервіс Greeter з одним методом SayHello.
service Greeter {
  // Метод SayHello приймає HelloRequest і повертає HelloReply або помилку.
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/greeter/sayhello"
      body: "*"
    };
  }
}

// Повідомлення, яке клієнт відправляє на сервер.
message HelloRequest {
  string name = 1; // Ім'я користувача, якому ми будемо казати "Привіт".
  string surname = 2;
}

// Повідомлення, яке сервер повертає клієнту.
message HelloReply {
  string message = 1;  // Привітальне повідомлення, яке сервер відправить клієнту.
}

// Визначаємо можливі помилки.
enum ErrorCode {
  UNKNOWN = 0;         // Невідома помилка.
  INVALID_NAME = 1;    // Некоректне ім'я (наприклад, порожнє).
  INTERNAL_ERROR = 2;  // Внутрішня помилка на сервері.
}

// Повідомлення для помилки.
message ErrorResponse {
  ErrorCode code = 1;    // Код помилки.
  string message = 2;    // Опис помилки.
}

Stub у gRPC — це проміжний код, який перетворює виклик функції на стороні клієнта на повідомлення, яке можна відправити на сервер. Stub на сервері, своєю чергою, отримує це повідомлення і викликає відповідну функцію. У gRPC stub автоматично генерується з файлу .proto. Це дозволяє клієнту і серверу взаємодіяти без ручного написання складного мережевого коду.

 

 


Protocol Buffers (Protobuf) — це формат серіалізації даних, розроблений Google. Він дозволяє ефективно кодувати та декодувати структури даних у бінарному форматі, що робить його швидким і компактним у порівнянні з текстовими форматами, такими як JSON або XML.

 

Основні характеристики Protobuf:
Бінарний формат: протоколи на основі Protobuf перетворюють дані у бінарний формат, що дозволяє значно зменшити розмір даних при передачі.
Ефективність: рrotobuf є дуже компактним, що робить його придатним для високошвидкісної передачі даних у мережах з низькими затримками.
Мовна незалежність: Protobuf підтримує багато мов програмування, таких як Java, C++, Python, Go, JavaScript, і дозволяє генерувати код для цих мов на основі одного .proto файлу.
Статична типізація: дані в Protobuf типізовані на рівні файлу .proto, що забезпечує жорстку структуру і виявлення помилок на ранніх етапах.


Як працює Protobuf?
1. Файл визначення .proto: файл з розширенням .proto, в якому описуються структури даних (повідомлення) і сервіси (функції, які можна викликати). Це схоже на те, як JSON чи XML описують структури, але з чіткішими типами даних.

2. Компіляція файлу .proto: Спеціальний компілятор protoc перетворює .proto файл у відповідні класи для різних мов програмування. Ці класи включають методи для серіалізації (перетворення об'єктів у бінарний формат) і десеріалізації (відновлення об'єктів із бінарного формату).

3. Серіалізація та десеріалізація:

Серіалізація: Коли ви передаєте дані, наприклад, через мережу, об'єкти кодуються в бінарний формат Protobuf. Цей процес називається серіалізацією.
Десеріалізація: Коли дані повертаються або отримуються від сервера, Protobuf перетворює ці бінарні дані назад у об'єкти для зручного використання програмою.

 

Стрімінг і додаткові функції gRPC


gRPC підтримує різні типи взаємодії між клієнтом і сервером, включаючи не тільки класичний підхід "запит-відповідь", але й стрімінг, де дані передаються постійним потоком. Ось роз'яснення цих функцій:

 

4. 

1. Unary RPC (Одноразовий виклик)
Це найпростіший тип виклику, коли клієнт відправляє один запит і отримує одну відповідь.
Приклад: клієнт викликає метод SayHello і отримує відповідь "Привіт!".
Підходить для: стандартних викликів, де потрібно лише одне повідомлення для виконання операції.

2. Server Streaming RPC (Стрімінг з сервера)
Клієнт відправляє один запит, а сервер відповідає потоком даних. Клієнт отримує кілька відповідей протягом певного часу.
Приклад: клієнт запитує історію замовлень, і сервер надсилає потоком всі замовлення по черзі.
Підходить для: великих обсягів даних або випадків, коли сервер поступово отримує результати.

3. Client Streaming RPC (Стрімінг з клієнта)
клієнт передає дані потоком (кілька запитів), а сервер надсилає одну відповідь після отримання всіх запитів.
Приклад: клієнт надсилає великий файл, розбитий на частини, і сервер повертає результат, коли всі частини файлу отримані.
Як це працює?
Клієнт відправляє потік запитів.
Сервер чекає, поки всі запити будуть отримані.
Після отримання всіх даних сервер формує відповідь і повертає її клієнту.
Підходить для: коли клієнт поступово надсилає багато даних.

4. Bidirectional Streaming RPC (Двосторонній стрімінг)
І клієнт, і сервер можуть надсилати дані потоком одночасно. Це двостороння комунікація, де обидві сторони можуть відправляти й отримувати повідомлення у будь-який час.
Приклад: Клієнт і сервер ведуть постійну сесію обміну даними в режимі реального часу, наприклад, для голосового або відеочату.

Клієнт і сервер одночасно надсилають та отримують дані.
Вони можуть передавати повідомлення незалежно один від одного, без необхідності чекати, коли інша сторона завершить відправку.
Підходить для: сценаріїв, де потрібно постійний обмін даними в режимі реального часу

Що тестувати?

 

1. Тестування встановлення з'єднання

2. Тестування обміну повідомленнями

Що перевірити:

👉 Коректність відправлення та отримання повідомлень.

👉 Чи відповідає сервер на повідомлення клієнта.

👉 Чи підтримується правильний формат даних (наприклад, текст або бінарні дані).

3. Тестування помилок протоколу: Переконатися, що сервер коректно обробляє неправильні або непідтримувані повідомлення.
Що перевірити:
👉 Відправка некоректних фреймів (наприклад, неправильний формат).
👉  Як сервер реагує на некоректні або надто великі повідомлення.

4. Тестування закриття з'єднання - чекнути, що WebSocket-з'єднання закривається коректно.
Що перевірити:
👉 Закриття з'єднання з ініціативи клієнта та сервера.
👉 Чи надсилаються правильні коди закриття.
👉 Чи не залишається з'єднання відкритим після закриття.

5. Тестування навантаження (Load Testing) - k6, Gatling

6. Тестування безпеки
Що перевірити:
👉  чи з'єднання працює через wss:// (WebSocket Secure).
👉 Чи правильно працює автентифікація та авторизація при WebSocket-з'єднаннях.

 

gRPC status codes: https://grpc.io/docs/guides/status-codes/

Welcome to Automation

By iravol

Welcome to Automation

  • 403