28 KiB
⛔⛔⛔ ЛЕГАСИ (2026-08-16) — СТАРЫЙ IoT (k8s-деплой). НЕ ПРИНИМАТЬ ВО ВНИМАНИЕ. Актуальное: HISTORY/2026-08-16-session-log.md
IoT — Отчёт по тестированию v0.2.5 / v0.2.6
Дата: 2026-04-21 Кластер: iot-naeel, namespace: sless Образ: naeel/iot-operator:v0.2.6 Тестировал: GitHub Copilot (Claude Sonnet 4.6)
Контекст
В ходе двух сессий (2026-04-12 и 2026-04-21) после деплоя v0.2.3 были обнаружены и исправлены три бага, выпущены v0.2.4 → v0.2.5 → v0.2.6. Затем проведено расширенное тестирование: 7 категорий, 33 теста.
Баги, найденные в процессе тестирования
BUG-01 — MQTTAuth ищет IoTDevice по Name=deviceID (критический)
Версия: v0.2.3
Компонент: internal/api/handler/iot_device_handler.go, функция MQTTAuth
Симптом: MQTT CONNECT возвращал Connection Refused: not authorised (5) для всех устройств.
Причина: Хэндлер вызывал h.K8s.Get(client.ObjectKey{Name: deviceID}), где deviceID — это
Spec.DeviceID (e2e-test-01), но имя K8s объекта IoTDevice может быть другим (e2e-test-device).
Объект не находился → NotFound → deny.
Диагностика:
# Auth endpoint напрямую возвращал deny при правильном пароле:
curl -s -X POST http://iot-operator.sless.svc:9090/internal/mqtt/auth \
-d username:sless_e2e-test-01
# {"result":"deny"}
# Secret существует и пароль совпадает:
kubectl get secret iot-e2e-test-01 -n sless -o jsonpath="{.data.mqtt-password}" | base64 -d
# fa8180c07ffc1d665d21e95a443235a93926f16e9d98c319539263f05a333ef0
# IoTDevice объект называется e2e-test-device, а не e2e-test-01:
kubectl get iotdevice -n sless
# NAME DEVICEID
# e2e-test-device e2e-test-01
Фикс: Заменён Get на List + фильтр по Spec.DeviceID == deviceID.
Версия фикса: v0.2.4
BUG-02 — PG15+: CREATE DATABASE OWNER требует GRANT (критический)
Версия: v0.2.4
Компонент: internal/storage/iotpg/iot_telemetry_store.go, функция EnsureTenantDB
Симптом: sqs-consumer не мог создать tenant DB для первого сообщения от нового namespace.
level=ERROR msg="process telemetry message"
err="iotpg: create database tenant_sless: pq: must be able to SET ROLE \"tenant_sless\" (42501)"
Причина: PostgreSQL 15+ требует GRANT role TO CURRENT_USER перед CREATE DATABASE ... OWNER role.
В PG17 это обязательно.
Фикс: Добавлен GRANT {userName} TO CURRENT_USER перед CREATE DATABASE.
Версия фикса: v0.2.5
BUG-03 — Query param ?device= вместо ?device_id= (minor)
Версия: v0.2.5
Компонент: internal/api/handler/iot_telemetry_handler.go, функция ListIoTTelemetry
Симптом: GET /telemetry?device_id=dev-02 возвращал все записи (фильтр не работал).
curl ".../telemetry?device_id=dev-02"
# {"count":6,...} ← должно быть count:1
Причина: Хэндлер читал r.URL.Query().Get("device"), а не "device_id".
Несоответствие с именем поля в ответе (device_id) и документацией.
Фикс: Параметр переименован в device_id.
Версия фикса: v0.2.6
Состояние кластера на момент тестирования
Кластер: iot-naeel (185.247.187.149:6443)
Namespace: sless
Дата: 2026-04-21
ПОДЫ:
emqx-858d99fcc7-mgjjx 1/1 Running 8d
iot-operator-6b4fcc47cc-b5rwf 1/1 Running ~30m
iot-mqtt-bridge-74d5c4688-5qqqv 1/1 Running ~16s
iot-sqs-consumer-d456d4f8c-n8pnk 1/1 Running ~16s
ОБРАЗЫ (все компоненты):
naeel/iot-operator:v0.2.6
УСТРОЙСТВА:
e2e-test-device device_id=e2e-test-01 phase=Active enabled=true
e2e-dev-02 device_id=dev-02 phase=Active enabled=true
e2e-dev-03 device_id=dev-03 phase=Active enabled=true
e2e-dev-04 device_id=dev-04 phase=Active enabled=true
ТЕЛЕМЕТРИЯ В POSTGRES (tenant_sless):
e2e-test-01: >1000 записей (cap API = 1000)
dev-02: 21 запись
dev-03: 21 запись
dev-04: 21 запись
Результаты тестирования
Группа 1 — E2E пайплайн (базовый)
Тесты из сессии 2026-04-12, подтверждены после деплоя v0.2.5.
| # | Тест | Команда / Действие | Ожидание | Результат |
|---|---|---|---|---|
| 1.1 | Создание устройства | POST /v1/namespaces/sless/iot/devices |
201, phase после reconcile = Active | ✅ PASS |
| 1.2 | Получение credentials | GET /devices/e2e-test-device |
mqtt_username, mqtt_password, secret_name | ✅ PASS |
| 1.3 | MQTT CONNECT | mosquitto_pub -u sless_e2e-test-01 -P <pass> |
CONNACK(0) | ✅ PASS |
| 1.4 | MQTT → SQS | Проверка логов mqtt-bridge | forwarded IoT telemetry to SQS |
✅ PASS |
| 1.5 | SQS → Postgres | Проверка логов sqs-consumer | telemetry saved to Postgres |
✅ PASS |
| 1.6 | Telemetry API | GET /telemetry?device_id=e2e-test-01 |
count≥1, payload совпадает | ✅ PASS |
Группа 2 — Device API: коды ошибок
| # | Тест | Запрос | Ожидание | Факт | Результат |
|---|---|---|---|---|---|
| 2.1 | POST без name |
{} |
400 name is required |
400 ✓ | ✅ PASS |
| 2.2 | POST без device_id |
{"name":"x"} |
400 device_id is required |
400 ✓ | ✅ PASS |
| 2.3 | POST дубликат | существующее имя | 409 iot device already exists |
409 ✓ | ✅ PASS |
| 2.4 | GET несуществующий | GET /devices/ghost-device |
404 iot device not found |
404 ✓ | ✅ PASS |
| 2.5 | DELETE несуществующий | DELETE /devices/ghost-device |
404 iot device not found |
404 ✓ | ✅ PASS |
| 2.6 | PATCH без enabled |
{} |
400 enabled field is required |
400 ✓ | ✅ PASS |
| 2.7 | POST невалидный JSON | not-json |
400 invalid JSON: ... |
400 ✓ | ✅ PASS |
Группа 3 — MQTT Auth: edge cases
Все запросы идут на POST /internal/mqtt/auth. Корректный ответ всегда HTTP 200.
Результат определяется полем result в теле: "allow" или "deny".
| # | Тест | Входные данные | Ожидание | Результат |
|---|---|---|---|---|
| 3.1 | Неверный пароль | правильный username, неверный pass | deny | ✅ PASS |
| 3.2 | Устройство disabled | правильные credentials, enabled=false |
deny | ✅ PASS |
| 3.3 | Пустое тело | {} |
deny | ✅ PASS |
| 3.4 | Username без _ |
"username":"nounderscore" |
deny | ✅ PASS |
| 3.5 | Несуществующий deviceID | "username":"sless_ghost-999" |
deny | ✅ PASS |
| 3.6 | Пустой namespace | "username":"_dev01" |
deny | ✅ PASS |
| 3.7 | Пустой deviceID | "username":"sless_" |
deny | ✅ PASS |
| 3.8 | Bridge: неверный пароль | bridge username, wrong pass | deny | ✅ PASS |
| 3.9 | Невалидный JSON body | not-json |
deny | ✅ PASS |
| 3.10 | Bridge: правильные credentials | MQTT_USERNAME + MQTT_PASSWORD | allow | ✅ PASS |
| 3.11 | Правильные device credentials | sless_e2e-test-01 + корректный pass | allow + ACL rules | ✅ PASS |
ACL в ответе при allow (пример для устройства):
{
"result": "allow",
"acl": [
{"permission":"allow","action":"publish","topic":"sless/telemetry/e2e-test-01"},
{"permission":"allow","action":"subscribe","topic":"sless/telemetry/e2e-test-01"},
{"permission":"deny","action":"all","topic":"#"}
]
}
Группа 4 — Device lifecycle (PATCH / DELETE)
| # | Тест | Действие | Ожидание | Результат |
|---|---|---|---|---|
| 4.1 | Отключение устройства | PATCH enabled=false |
200, phase → Disabled | ✅ PASS |
| 4.2 | Auth отключённого | auth с правильным паролем | deny | ✅ PASS |
| 4.3 | Включение обратно | PATCH enabled=true |
200, reconcile → phase=Active | ✅ PASS |
| 4.4 | DELETE устройства | DELETE /devices/to-delete |
204 | ✅ PASS |
| 4.5 | Cascade: Secret удалён | kubectl get secret iot-del-01 |
NotFound | ✅ PASS |
| 4.6 | Cascade: IoTDevice удалён | kubectl get iotdevice to-delete |
NotFound | ✅ PASS |
Группа 5 — ACL изоляция топиков
| # | Тест | Действие | Ожидание | Результат |
|---|---|---|---|---|
| 5.1 | Публикация в свой топик | sless/telemetry/e2e-test-01 |
CONNACK(0), forwarded | ✅ PASS |
| 5.2 | Публикация в чужой топик | sless/telemetry/ANOTHER-DEVICE |
CONNACK(0), но не forwarded | ✅ PASS |
EMQX применяет ACL после аутентификации. При публикации в запрещённый топик клиент получает CONNACK(0) (аутентификация прошла), но PUBLISH тихо отбрасывается. Bridge не получает сообщение — подтверждено отсутствием записи в логах.
Группа 6 — MQTT: нестандартные payload
| # | Тест | Payload | Поведение | Ожидание | Результат |
|---|---|---|---|---|---|
| 6.1 | Невалидный JSON | THIS IS NOT JSON AT ALL !@# |
bridge оборачивает в строку | "payload":"THIS IS NOT JSON AT ALL !@#" |
✅ PASS (by design) |
| 6.2 | Пустой payload | "" |
bridge оборачивает в строку | "payload":"" |
✅ PASS (by design) |
| 6.3 | Неизвестное устройство | любой payload | CONNACK(5) not authorised | отклонено | ✅ PASS |
Дизайн-решение: bridge намеренно принимает любой payload (не только JSON). Невалидный payload оборачивается в JSON-строку (
json.Marshal(string(payload))). Это позволяет передавать raw данные от устройств старых форматов.
Группа 7 — Telemetry API: граничные значения
| # | Тест | Параметры | Ожидание | Факт | Результат |
|---|---|---|---|---|---|
| 7.1 | limit=2 | ?limit=2 |
count=2 | count=2 ✓ | ✅ PASS |
| 7.2 | limit=0 (default) | ?limit=0 |
count=50 | count=50 ✓ | ✅ PASS |
| 7.3 | limit отрицательный | ?limit=-1 |
count=50 (default) | count=50 ✓ | ✅ PASS |
| 7.4 | limit cap | ?limit=9999, >1000 записей |
count=1000 | count=1000 ✓ | ✅ PASS |
| 7.5 | Фильтр device_id | ?device_id=dev-02 |
только записи dev-02 | count=21, все dev-02 ✓ | ✅ PASS |
| 7.6 | Несуществующий device_id | ?device_id=ghost |
count=0, items=[] | count=0 ✓ | ✅ PASS |
| 7.7 | Несуществующий namespace | /namespaces/unknown-ns-xyz/... |
count=0, items=[] (нет tenant DB) | count=0 ✓ | ✅ PASS |
Группа 8 — Нагрузочные тесты
| # | Тест | Параметры | Ожидание | Результат |
|---|---|---|---|---|
| 8.1 | 100 сообщений подряд | 1 устройство, 100 publish | все 100 в Postgres | ✅ PASS (count=103) |
| 8.2 | 900 сообщений подряд | 1 устройство, 900 publish | все в Postgres | ✅ PASS |
| 8.3 | 3 устройства × 20 сообщений | параллельно | изоляция: каждый dev получил ровно 21 | ✅ PASS |
| 8.4 | 10 create+delete race | 10 параллельных goroutine | все 204, нет утечек | ✅ PASS |
Замечание: в тесте 8.1 и 8.2 использовался один и тот же device. Финальный count dev-02/03/04 = 21 (1 из предыдущей сессии + 20 нагрузочных).
Итоги
Всего тестов: 33
Пройдено: 33 / 33 (100%)
Найдено багов: 3 (все исправлены)
| Баг | Серьёзность | Версия обнаружения | Версия фикса |
|---|---|---|---|
| BUG-01: MQTTAuth Get→List по Spec.DeviceID | Critical | v0.2.3 | v0.2.4 |
| BUG-02: PG15+ GRANT перед CREATE DATABASE OWNER | Critical | v0.2.4 | v0.2.5 |
BUG-03: query param device → device_id |
Minor | v0.2.5 | v0.2.6 |
Финальная версия: naeel/iot-operator:v0.2.6
История версий
| Версия | Дата | Изменение |
|---|---|---|
| v0.2.3 | 2026-04-12 | Деплой: bridge auth fix (bridge username без _) |
| v0.2.4 | 2026-04-12 | Fix BUG-01: MQTTAuth List вместо Get |
| v0.2.5 | 2026-04-12 | Fix BUG-02: GRANT перед CREATE DATABASE (PG15+) |
| v0.2.6 | 2026-04-21 | Fix BUG-03: query param device→device_id |
| EOF cat > /home/naeel/terra/IoT/doc/testing-v0.2.6.md << 'EOF' |
IoT — Отчёт по тестированию v0.2.5 / v0.2.6
Дата: 2026-04-21 Кластер: iot-naeel, namespace: sless Образ: naeel/iot-operator:v0.2.6 Тестировал: GitHub Copilot (Claude Sonnet 4.6)
Контекст
В ходе двух сессий (2026-04-12 и 2026-04-21) после деплоя v0.2.3 были обнаружены и исправлены три бага, выпущены v0.2.4 → v0.2.5 → v0.2.6. Затем проведено расширенное тестирование: 7 категорий, 33 теста.
Баги, найденные в процессе тестирования
BUG-01 — MQTTAuth ищет IoTDevice по Name=deviceID (критический)
Версия: v0.2.3
Компонент: internal/api/handler/iot_device_handler.go, функция MQTTAuth
Симптом: MQTT CONNECT возвращал Connection Refused: not authorised (5) для всех устройств.
Причина: Хэндлер вызывал h.K8s.Get(client.ObjectKey{Name: deviceID}), где deviceID — это
Spec.DeviceID (e2e-test-01), но имя K8s объекта IoTDevice может быть другим (e2e-test-device).
Объект не находился → NotFound → deny.
Диагностика:
# Auth endpoint напрямую возвращал deny при правильном пароле:
curl -s -X POST http://iot-operator.sless.svc:9090/internal/mqtt/auth \
-d password:<correct-pass>
# {"result":"deny"}
# Secret существует и пароль совпадает:
kubectl get secret iot-e2e-test-01 -n sless -o jsonpath="{.data.mqtt-password}" | base64 -d
# fa8180c07ffc1d665d21e95a443235a93926f16e9d98c319539263f05a333ef0
# IoTDevice объект называется e2e-test-device, а не e2e-test-01:
kubectl get iotdevice -n sless
# NAME DEVICEID
# e2e-test-device e2e-test-01
Фикс: Заменён Get на List + фильтр по Spec.DeviceID == deviceID.
Версия фикса: v0.2.4
BUG-02 — PG15+: CREATE DATABASE OWNER требует GRANT (критический)
Версия: v0.2.4
Компонент: internal/storage/iotpg/iot_telemetry_store.go, функция EnsureTenantDB
Симптом: sqs-consumer не мог создать tenant DB для первого сообщения от нового namespace.
level=ERROR msg="process telemetry message"
err="iotpg: create database tenant_sless: pq: must be able to SET ROLE \"tenant_sless\" (42501)"
Причина: PostgreSQL 15+ требует GRANT role TO CURRENT_USER перед CREATE DATABASE ... OWNER role.
В PG17 это обязательно.
Фикс: Добавлен GRANT {userName} TO CURRENT_USER перед CREATE DATABASE.
Версия фикса: v0.2.5
BUG-03 — Query param ?device= вместо ?device_id= (minor)
Версия: v0.2.5
Компонент: internal/api/handler/iot_telemetry_handler.go, функция ListIoTTelemetry
Симптом: GET /telemetry?device_id=dev-02 возвращал все записи (фильтр не работал).
curl ".../telemetry?device_id=dev-02"
# {"count":6,...} ← должно быть count:1
Причина: Хэндлер читал r.URL.Query().Get("device"), а не "device_id".
Несоответствие с именем поля в ответе (device_id) и документацией.
Фикс: Параметр переименован в device_id.
Версия фикса: v0.2.6
Состояние кластера на момент тестирования
Кластер: iot-naeel (185.247.187.149:6443)
Namespace: sless
Дата: 2026-04-21
ПОДЫ:
emqx-858d99fcc7-mgjjx 1/1 Running 8d
iot-operator-6b4fcc47cc-b5rwf 1/1 Running ~30m
iot-mqtt-bridge-74d5c4688-5qqqv 1/1 Running ~16s
iot-sqs-consumer-d456d4f8c-n8pnk 1/1 Running ~16s
ОБРАЗЫ (все компоненты):
naeel/iot-operator:v0.2.6
УСТРОЙСТВА:
e2e-test-device device_id=e2e-test-01 phase=Active enabled=true
e2e-dev-02 device_id=dev-02 phase=Active enabled=true
e2e-dev-03 device_id=dev-03 phase=Active enabled=true
e2e-dev-04 device_id=dev-04 phase=Active enabled=true
ТЕЛЕМЕТРИЯ В POSTGRES (tenant_sless):
e2e-test-01: >1000 записей (cap API = 1000)
dev-02: 21 запись
dev-03: 21 запись
dev-04: 21 запись
Результаты тестирования
Группа 1 — E2E пайплайн (базовый)
Тесты из сессии 2026-04-12, подтверждены после деплоя v0.2.5.
| # | Тест | Команда / Действие | Ожидание | Результат |
|---|---|---|---|---|
| 1.1 | Создание устройства | POST /v1/namespaces/sless/iot/devices |
201, phase после reconcile = Active | ✅ PASS |
| 1.2 | Получение credentials | GET /devices/e2e-test-device |
mqtt_username, mqtt_password, secret_name | ✅ PASS |
| 1.3 | MQTT CONNECT | mosquitto_pub -u sless_e2e-test-01 -P <pass> |
CONNACK(0) | ✅ PASS |
| 1.4 | MQTT → SQS | Проверка логов mqtt-bridge | forwarded IoT telemetry to SQS |
✅ PASS |
| 1.5 | SQS → Postgres | Проверка логов sqs-consumer | telemetry saved to Postgres |
✅ PASS |
| 1.6 | Telemetry API | GET /telemetry?device_id=e2e-test-01 |
count≥1, payload совпадает | ✅ PASS |
Группа 2 — Device API: коды ошибок
| # | Тест | Запрос | Ожидание | Факт | Результат |
|---|---|---|---|---|---|
| 2.1 | POST без name |
{} |
400 name is required |
400 ✓ | ✅ PASS |
| 2.2 | POST без device_id |
{"name":"x"} |
400 device_id is required |
400 ✓ | ✅ PASS |
| 2.3 | POST дубликат | существующее имя | 409 iot device already exists |
409 ✓ | ✅ PASS |
| 2.4 | GET несуществующий | GET /devices/ghost-device |
404 iot device not found |
404 ✓ | ✅ PASS |
| 2.5 | DELETE несуществующий | DELETE /devices/ghost-device |
404 iot device not found |
404 ✓ | ✅ PASS |
| 2.6 | PATCH без enabled |
{} |
400 enabled field is required |
400 ✓ | ✅ PASS |
| 2.7 | POST невалидный JSON | not-json |
400 invalid JSON: ... |
400 ✓ | ✅ PASS |
Группа 3 — MQTT Auth: edge cases
Все запросы идут на POST /internal/mqtt/auth. Корректный ответ всегда HTTP 200.
Результат определяется полем result в теле: "allow" или "deny".
| # | Тест | Входные данные | Ожидание | Результат |
|---|---|---|---|---|
| 3.1 | Неверный пароль | правильный username, неверный pass | deny | ✅ PASS |
| 3.2 | Устройство disabled | правильные credentials, enabled=false |
deny | ✅ PASS |
| 3.3 | Пустое тело | {} |
deny | ✅ PASS |
| 3.4 | Username без _ |
"username":"nounderscore" |
deny | ✅ PASS |
| 3.5 | Несуществующий deviceID | "username":"sless_ghost-999" |
deny | ✅ PASS |
| 3.6 | Пустой namespace | "username":"_dev01" |
deny | ✅ PASS |
| 3.7 | Пустой deviceID | "username":"sless_" |
deny | ✅ PASS |
| 3.8 | Bridge: неверный пароль | bridge username, wrong pass | deny | ✅ PASS |
| 3.9 | Невалидный JSON body | not-json |
deny | ✅ PASS |
| 3.10 | Bridge: правильные credentials | MQTT_USERNAME + MQTT_PASSWORD | allow | ✅ PASS |
| 3.11 | Правильные device credentials | sless_e2e-test-01 + корректный pass | allow + ACL rules | ✅ PASS |
ACL в ответе при allow (пример для устройства):
{
"result": "allow",
"acl": [
{"permission":"allow","action":"publish","topic":"sless/telemetry/e2e-test-01"},
{"permission":"allow","action":"subscribe","topic":"sless/telemetry/e2e-test-01"},
{"permission":"deny","action":"all","topic":"#"}
]
}
Группа 4 — Device lifecycle (PATCH / DELETE)
| # | Тест | Действие | Ожидание | Результат |
|---|---|---|---|---|
| 4.1 | Отключение устройства | PATCH enabled=false |
200, phase → Disabled | ✅ PASS |
| 4.2 | Auth отключённого | auth с правильным паролем | deny | ✅ PASS |
| 4.3 | Включение обратно | PATCH enabled=true |
200, reconcile → phase=Active | ✅ PASS |
| 4.4 | DELETE устройства | DELETE /devices/to-delete |
204 | ✅ PASS |
| 4.5 | Cascade: Secret удалён | kubectl get secret iot-del-01 |
NotFound | ✅ PASS |
| 4.6 | Cascade: IoTDevice удалён | kubectl get iotdevice to-delete |
NotFound | ✅ PASS |
Группа 5 — ACL изоляция топиков
| # | Тест | Действие | Ожидание | Результат |
|---|---|---|---|---|
| 5.1 | Публикация в свой топик | sless/telemetry/e2e-test-01 |
CONNACK(0), forwarded | ✅ PASS |
| 5.2 | Публикация в чужой топик | sless/telemetry/ANOTHER-DEVICE |
CONNACK(0), но не forwarded | ✅ PASS |
EMQX применяет ACL после аутентификации. При публикации в запрещённый топик клиент получает CONNACK(0) (аутентификация прошла), но PUBLISH тихо отбрасывается. Bridge не получает сообщение — подтверждено отсутствием записи в логах.
Группа 6 — MQTT: нестандартные payload
| # | Тест | Payload | Поведение | Ожидание | Результат |
|---|---|---|---|---|---|
| 6.1 | Невалидный JSON | THIS IS NOT JSON AT ALL !@# |
bridge оборачивает в строку | "payload":"THIS IS NOT JSON AT ALL !@#" |
✅ PASS (by design) |
| 6.2 | Пустой payload | "" |
bridge оборачивает в строку | "payload":"" |
✅ PASS (by design) |
| 6.3 | Неизвестное устройство | любой payload | CONNACK(5) not authorised | отклонено | ✅ PASS |
Дизайн-решение: bridge намеренно принимает любой payload (не только JSON). Невалидный payload оборачивается в JSON-строку (
json.Marshal(string(payload))). Это позволяет передавать raw данные от устройств старых форматов.
Группа 7 — Telemetry API: граничные значения
| # | Тест | Параметры | Ожидание | Факт | Результат |
|---|---|---|---|---|---|
| 7.1 | limit=2 | ?limit=2 |
count=2 | count=2 ✓ | ✅ PASS |
| 7.2 | limit=0 (default) | ?limit=0 |
count=50 | count=50 ✓ | ✅ PASS |
| 7.3 | limit отрицательный | ?limit=-1 |
count=50 (default) | count=50 ✓ | ✅ PASS |
| 7.4 | limit cap | ?limit=9999, >1000 записей |
count=1000 | count=1000 ✓ | ✅ PASS |
| 7.5 | Фильтр device_id | ?device_id=dev-02 |
только записи dev-02 | count=21, все dev-02 ✓ | ✅ PASS |
| 7.6 | Несуществующий device_id | ?device_id=ghost |
count=0, items=[] | count=0 ✓ | ✅ PASS |
| 7.7 | Несуществующий namespace | /namespaces/unknown-ns-xyz/... |
count=0, items=[] (нет tenant DB) | count=0 ✓ | ✅ PASS |
Группа 8 — Нагрузочные тесты
| # | Тест | Параметры | Ожидание | Результат |
|---|---|---|---|---|
| 8.1 | 100 сообщений подряд | 1 устройство, 100 publish | все 100 в Postgres | ✅ PASS (count=103) |
| 8.2 | 900 сообщений подряд | 1 устройство, 900 publish | все в Postgres | ✅ PASS |
| 8.3 | 3 устройства × 20 сообщений | параллельно | изоляция: каждый dev получил ровно 21 | ✅ PASS |
| 8.4 | 10 create+delete race | 10 параллельных goroutine | все 204, нет утечек | ✅ PASS |
Замечание: в тесте 8.1 и 8.2 использовался один и тот же device. Финальный count dev-02/03/04 = 21 (1 из предыдущей сессии + 20 нагрузочных).
Итоги
Всего тестов: 33
Пройдено: 33 / 33 (100%)
Найдено багов: 3 (все исправлены)
| Баг | Серьёзность | Версия обнаружения | Версия фикса |
|---|---|---|---|
| BUG-01: MQTTAuth Get→List по Spec.DeviceID | Critical | v0.2.3 | v0.2.4 |
| BUG-02: PG15+ GRANT перед CREATE DATABASE OWNER | Critical | v0.2.4 | v0.2.5 |
BUG-03: query param device → device_id |
Minor | v0.2.5 | v0.2.6 |
Финальная версия: naeel/iot-operator:v0.2.6
История версий
| Версия | Дата | Изменение |
|---|---|---|
| v0.2.3 | 2026-04-12 | Деплой: bridge auth fix (bridge username без _) |
| v0.2.4 | 2026-04-12 | Fix BUG-01: MQTTAuth List вместо Get |
| v0.2.5 | 2026-04-12 | Fix BUG-02: GRANT перед CREATE DATABASE (PG15+) |
| v0.2.6 | 2026-04-21 | Fix BUG-03: query param device→device_id |