Files
bare/internal/config/config_test.go
T
mayatnikovandClaude Opus 5 cfd0ec0ce9 Пуши на iOS: sub уезжал как mailto:mailto:, Apple отвечал BadJwtToken
webpush-go в getVAPIDAuthorizationHeader приписывает "mailto:" всему,
что не начинается с "https:". В /etc/bare/env лежит правильная по
спецификации VAPID форма mailto:admin@xmatic.team, и в JWT уходил
sub = mailto:mailto:admin@xmatic.team. Apple отвергал токен, пуши
не работали ни разу за всё время.

Проверено экспериментом на живом endpoint: mailto:<адрес> → 201,
https://<хост> → 201, mailto:mailto:<адрес> → 403 BadJwtToken,
голый адрес без схемы → 403. Значит убрать схему из конфига нельзя:
адаптируется код, а не операционный файл.

Субъект нормализуется один раз при создании отправщика, форма
BARE_VAPID_SUBJECT проверяется при старте — негодное значение валит
запуск с понятной ошибкой, а не выключает пуши молча.

ADR-064: в журнал пишется код причины от push-сервиса. Без него отказ
был неотличим от любого другого 403, и дефект прожил незамеченным
весь этап 4 и всю закалку. Адрес подписки в журнал по-прежнему
не попадает: наружу идёт только короткий код из латиницы и цифр.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
2026-08-23 07:54:16 +03:00

59 lines
2.0 KiB
Go

package config
import (
"strings"
"testing"
)
// Форма VAPID-субъекта проверяется на старте: с мусором в нём сервер
// поднимается, подписки ставятся, а push-сервис отвергает каждый токен —
// поломка без единой строки в журнале (docs/deploy.md).
func TestVAPIDSubject(t *testing.T) {
cases := []struct {
subject string
keys bool
ok bool
}{
{"mailto:admin@xmatic.team", true, true},
{"https://bare.xmatic.team", true, true},
{"https://bare.xmatic.team/", true, true},
// Голый адрес — не URI: RFC 8292 требует схему, и APNs отвергает
// токен без неё.
{"admin@xmatic.team", true, false},
{"mailto:", true, false},
{"mailto:admin", true, false},
{"mailto:@xmatic.team", true, false},
{"mailto:admin@xmatic.team, second@xmatic.team", true, false},
{"https://", true, false},
{"http://bare.xmatic.team", true, false},
{"bare.xmatic.team", true, false},
// Ключи заданы, субъекта нет: пуши настроить хотели и не настроили.
{"", true, false},
// Ни ключей, ни субъекта — локальный запуск без пушей.
{"", false, true},
}
for _, c := range cases {
t.Setenv("BARE_VAPID_SUBJECT", c.subject)
if c.keys {
t.Setenv("BARE_VAPID_PUBLIC", "public")
t.Setenv("BARE_VAPID_PRIVATE", "private")
} else {
t.Setenv("BARE_VAPID_PUBLIC", "")
t.Setenv("BARE_VAPID_PRIVATE", "")
}
_, err := Load()
if c.ok && err != nil {
t.Errorf("BARE_VAPID_SUBJECT=%q (ключи: %v): %v", c.subject, c.keys, err)
}
if !c.ok {
if err == nil {
t.Errorf("BARE_VAPID_SUBJECT=%q (ключи: %v): принят", c.subject, c.keys)
continue
}
if !strings.Contains(err.Error(), "BARE_VAPID_SUBJECT") {
t.Errorf("ошибка не называет переменную: %v", err)
}
}
}
}