Білінг: запит читав старі імена колонок usage_now
All checks were successful
CI / hygiene (push) Successful in 10s
CI / web (push) Successful in 58s
CI / server (push) Successful in 2m35s
CI / agent (push) Successful in 2m1s

Агент свідомо перейменував вихідні колонки функції на devices_used,
maps_used, agents_used, users_used — щоб `devices` поруч із таблицею
inv.devices не давало неоднозначності. Запит у Go лишився на старих
іменах.

Код збирався, локальні тести були зелені (пропускались без
NETPULSE_TEST_DSN), а перше ж звернення до бази падало з
«column "devices" does not exist». Сторінка білінгу не показала б
нічого.

Знайдено пісочницею: установка зі свіжого клону, 63 міграції на чистій
базі, потім тести з живою БД. Це той самий клас, що ловився весь день —
зелена перевірка доводить рівно те, що перевіряє, а тест, який
мовчки пропускається, не перевіряє нічого.

Заодно записано в шапці api_test.go умову, якої той набір вимагає:
база має бути порожньою. Користувачі в продукті глобальні, і на вже
поставленій системі seedUser натрапляє на власника admin.
This commit is contained in:
byrsapty 2026-08-27 21:30:51 +03:00
parent ca143a616b
commit c8b9d08c0b
2 changed files with 26 additions and 1 deletions

View file

@ -1,6 +1,24 @@
// Наскрізний тест API: справжня БД, справжній HTTP, справжній WebSocket.
//
// NETPULSE_TEST_DSN="postgres://netpulse:netpulse@localhost/netpulse_it" go test ./...
//
// БАЗА МАЄ БУТИ ПОРОЖНЬОЮ — лише зі схемою, без даних.
//
// Кабінети тут унікальні (slug із наносекундами), а от КОРИСТУВАЧІ в
// цьому продукті глобальні: одна людина може працювати в кількох
// кабінетах, тому `core.users.username` унікальний наскрізно, і
// CreateUser при зіткненні свідомо підхоплює наявного, НЕ переписуючи
// пароль (users.go, і це правильно: інакше адмін одного кабінету міняв
// би пароль людини в чужому).
//
// Наслідок для тестів: на базі, де вже стоїть робоча інсталяція,
// seedUser("admin@example.com") натрапляє на власника з ім'ям `admin`,
// пароль лишається чужим, вхід не вдається — і тест падає на 401 у
// зовсім іншому місці. Це не поломка продукту, а невиконана умова.
//
// Так знайшлось: набір прогнали проти пісочниці установника, тобто
// проти ВЖЕ поставленої системи. CI і `./netpulse sandbox once` дають
// чисту базу, тому там усе зелене.
package httpapi_test
import (

View file

@ -298,7 +298,14 @@ func (s *Store) TenantUsage(ctx context.Context, tenantID string) (Usage, error)
var u Usage
err := s.InTenantTx(ctx, tenantID, func(tx pgx.Tx) error {
return tx.QueryRow(ctx,
`SELECT devices, maps, agents, users FROM bill.usage_now($1)`, tenantID).
// Суфікс `_used` — не косметика: функція повертає колонки з
// цими іменами саме тому, що `devices` поруч із таблицею
// inv.devices дає неоднозначність (пояснення в 0069). Тут
// імена спершу лишились старими, і це знайшлось лише в
// пісочниці: код збирався, а перший же запит до бази падав
// з «column "devices" does not exist».
`SELECT devices_used, maps_used, agents_used, users_used
FROM bill.usage_now($1)`, tenantID).
Scan(&u.Devices, &u.Maps, &u.Agents, &u.Users)
})
return u, err