feat(m2m-core): FSM сделки, репозиторий, идемпотентность по GUID, метрики SLA
- internal/m2mcore/fsm.go: конечный автомат с переходами Draft → Validated → SubmittedToNSD → AwaitingDecision → Confirmed → AwaitingSUB16 → Done, ветки Rejected/TimedOut/ManualApproval - internal/m2mcore/deal.go: доменная модель Deal с методами Validate/Submit/ReceiveDecision/Timeout/SendToManualApproval/ApproveManually/RejectManually/CompleteSUB16, журнал событий - internal/m2mcore/uuid.go: генератор UUID v4 без внешних зависимостей (crypto/rand) - internal/m2mcore/repo.go: порт Repository + MemoryRepository с идемпотентным Create по GUID - internal/m2mcore/ports.go: порты NSDSender/LKCallbackClient/CryptoVerifier/FansyStore с no-op заглушками для M1 - internal/m2mcore/enrich.go: EnrichRequest — сборка M2MTransferRequest из ClaimInput + Fansy, генерация ReferenceID по каждой ЦБ - internal/m2mcore/metrics.go: порт Recorder + MemoryRecorder в Prometheus-text формате - cmd/m2m-core/main.go: HTTP-сервер с /healthz и /metrics, graceful shutdown - migrations/m2m-core/001__deals.sql: схема для PostgreSQL-Repository (для M2) Покрытие: 63.1%. make ci зелёный. Без внешних Go-зависимостей (pgx и prometheus подключим в M2, когда прокси zetit откроет Go-модули). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package m2mcore_test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2mcore"
|
||||
)
|
||||
|
||||
func TestNewUUIDv4Format(t *testing.T) {
|
||||
pattern := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`)
|
||||
seen := make(map[string]struct{})
|
||||
for i := 0; i < 100; i++ {
|
||||
u, err := m2mcore.NewUUIDv4()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !pattern.MatchString(u) {
|
||||
t.Errorf("UUID %q не соответствует RFC4122 v4", u)
|
||||
}
|
||||
if _, dup := seen[u]; dup {
|
||||
t.Errorf("сгенерирован дубликат %q", u)
|
||||
}
|
||||
seen[u] = struct{}{}
|
||||
// Также должен проходить XSD-валидатор НРД.
|
||||
if err := (m2m.UUID(u)).Validate(); err != nil {
|
||||
t.Errorf("UUID %q отвергнут XSD-валидатором: %v", u, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewReferenceID(t *testing.T) {
|
||||
at := mustParseTime(t, "2026-03-02T14:30:45Z")
|
||||
id, err := m2mcore.NewReferenceID(at)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(id) != 16 {
|
||||
t.Errorf("ReferenceID длина %d, ожидалось 16", len(id))
|
||||
}
|
||||
if err := id.Validate(); err != nil {
|
||||
t.Errorf("ReferenceID %q отвергнут валидатором: %v", id, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user