Files
m 20d361a886
build / backend (push) Has been cancelled
build / node-agent (push) Has been cancelled
build / worker (push) Has been cancelled
рабочий вариант, но скороть 10 МБит
2026-05-22 21:46:49 +03:00

281 lines
11 KiB
Go

package mesh
import (
"context"
"crypto/ed25519"
"testing"
"time"
)
func TestFabricRegistryGossipRecordRequiresTrustedSignature(t *testing.T) {
now := time.Date(2026, 5, 18, 10, 0, 0, 0, time.UTC)
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
record := testFabricRegistryGossipRecord(now, 10)
issuer := FabricRegistryTrustedIssuer{
IssuerID: "authority-1",
Role: FabricRegistryAuthorityControl,
PublicKey: publicKey,
Scopes: []string{FabricRegistryScopeCluster},
Services: []string{FabricRegistryServiceControlAPI},
}
signed, err := SignFabricRegistryGossipRecord(record, issuer, privateKey)
if err != nil {
t.Fatalf("sign record: %v", err)
}
if _, err := VerifyFabricRegistryGossipRecord(signed, FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}); err != nil {
t.Fatalf("verify signed record: %v", err)
}
tampered := signed
tampered.Endpoints[0].Address = "quic://10.10.10.10:19443"
if _, err := VerifyFabricRegistryGossipRecord(tampered, FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}); err == nil {
t.Fatal("tampered record verified")
}
}
func TestFabricRegistryRejectsDisallowedEndpointAndExpiredRecord(t *testing.T) {
now := time.Date(2026, 5, 18, 10, 0, 0, 0, time.UTC)
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
issuer := FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: FabricRegistryAuthorityControl, PublicKey: publicKey}
record := testFabricRegistryGossipRecord(now, 10)
record.Endpoints[0].Address = "https://control.example.test/api/v1"
signed, err := SignFabricRegistryGossipRecord(record, issuer, privateKey)
if err != nil {
t.Fatalf("sign record: %v", err)
}
if _, err := VerifyFabricRegistryGossipRecord(signed, FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{
{IssuerID: "authority-1", Role: FabricRegistryAuthorityControl, PublicKey: publicKey},
},
Now: now,
}); err == nil {
t.Fatal("compat HTTP endpoint was accepted")
}
expired := testFabricRegistryGossipRecord(now.Add(-2*time.Hour), 11)
expired.ExpiresAt = now.Add(-time.Minute)
expiredSigned, err := SignFabricRegistryGossipRecord(expired, issuer, privateKey)
if err != nil {
t.Fatalf("sign expired record: %v", err)
}
if _, err := VerifyFabricRegistryGossipRecord(expiredSigned, FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{
{IssuerID: "authority-1", Role: FabricRegistryAuthorityControl, PublicKey: publicKey},
},
Now: now,
}); err == nil {
t.Fatal("expired record was accepted")
}
}
func TestFabricRegistryKeepsActiveRecordUntilNewerVerified(t *testing.T) {
now := time.Date(2026, 5, 18, 10, 0, 0, 0, time.UTC)
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
issuer := FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: FabricRegistryAuthorityControl, PublicKey: publicKey}
policy := FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}
registry := NewFabricRegistry()
active, err := SignFabricRegistryGossipRecord(testFabricRegistryGossipRecord(now, 10), issuer, privateKey)
if err != nil {
t.Fatalf("sign active: %v", err)
}
entry, changed, err := registry.ApplyGossipRecord(active, policy, true)
if err != nil || !changed || entry.State != FabricRegistryActive {
t.Fatalf("apply active entry changed=%t entry=%+v err=%v", changed, entry, err)
}
old := testFabricRegistryGossipRecord(now.Add(time.Minute), 9)
old.Endpoints[0].Address = "quic://192.0.2.9:19443"
oldSigned, err := SignFabricRegistryGossipRecord(old, issuer, privateKey)
if err != nil {
t.Fatalf("sign old: %v", err)
}
entry, changed, err = registry.ApplyGossipRecord(oldSigned, policy, true)
if err != nil {
t.Fatalf("apply old: %v", err)
}
if changed || entry.Record.Epoch != 10 || entry.Record.Endpoints[0].Address != "quic://192.0.2.10:19443" {
t.Fatalf("older record replaced active entry: changed=%t entry=%+v", changed, entry)
}
newer := testFabricRegistryGossipRecord(now.Add(2*time.Minute), 11)
newer.Endpoints[0].Address = "quic://192.0.2.11:19443"
newerSigned, err := SignFabricRegistryGossipRecord(newer, issuer, privateKey)
if err != nil {
t.Fatalf("sign newer: %v", err)
}
policy.Now = now.Add(2 * time.Minute)
entry, changed, err = registry.ApplyGossipRecord(newerSigned, policy, false)
if err != nil || !changed || entry.State != FabricRegistryCandidate {
t.Fatalf("apply newer candidate changed=%t entry=%+v err=%v", changed, entry, err)
}
activeRecord, ok := registry.Active("cluster-1", FabricRegistryServiceControlAPI, FabricRegistryScopeCluster, "", policy.Now)
if !ok || activeRecord.Endpoints[0].Address != "quic://192.0.2.10:19443" {
t.Fatalf("unverified newer candidate displaced active fallback: ok=%t record=%+v", ok, activeRecord)
}
if !registry.MarkLiveVerified("cluster-1", FabricRegistryServiceControlAPI, FabricRegistryScopeCluster, "", policy.Now.Add(time.Second)) {
t.Fatal("mark live verified failed")
}
activeRecord, ok = registry.Active("cluster-1", FabricRegistryServiceControlAPI, FabricRegistryScopeCluster, "", policy.Now.Add(time.Second))
if !ok || activeRecord.Endpoints[0].Address != "quic://192.0.2.11:19443" {
t.Fatalf("newer verified record not active: ok=%t record=%+v", ok, activeRecord)
}
}
func TestFabricRegistryResolveServicePrefersVerifiedScopedRegionalEndpoint(t *testing.T) {
now := time.Date(2026, 5, 18, 10, 0, 0, 0, time.UTC)
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
issuer := FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: FabricRegistryAuthorityControl, PublicKey: publicKey}
policy := FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}
registry := NewFabricRegistry()
clusterRecord := testFabricRegistryGossipRecord(now, 10)
clusterRecord.Endpoints = []FabricRegistryEndpoint{
{EndpointID: "control-eu", Address: "quic://eu.example.test:19443", Transport: "direct_quic", Region: "eu", Priority: 10, Weight: 1},
{EndpointID: "control-us", Address: "quic://us.example.test:19443", Transport: "direct_quic", Region: "us", Priority: 10, Weight: 10},
}
signedCluster, err := SignFabricRegistryGossipRecord(clusterRecord, issuer, privateKey)
if err != nil {
t.Fatalf("sign cluster record: %v", err)
}
if _, _, err := registry.ApplyGossipRecord(signedCluster, policy, true); err != nil {
t.Fatalf("apply cluster record: %v", err)
}
orgRecord := testFabricRegistryGossipRecord(now.Add(time.Minute), 11)
orgRecord.Scope = FabricRegistryScopeOrganization
orgRecord.OrganizationID = "org-1"
orgRecord.Endpoints = []FabricRegistryEndpoint{
{EndpointID: "control-org", Address: "quic://org.example.test:19443", Transport: "direct_quic", Region: "eu", Priority: 1, Weight: 1},
}
signedOrg, err := SignFabricRegistryGossipRecord(orgRecord, issuer, privateKey)
if err != nil {
t.Fatalf("sign org record: %v", err)
}
policy.Now = now.Add(time.Minute)
if _, _, err := registry.ApplyGossipRecord(signedOrg, policy, false); err != nil {
t.Fatalf("apply org candidate: %v", err)
}
resolved := registry.ResolveService(FabricRegistryResolveRequest{
ClusterID: "cluster-1",
Service: FabricRegistryServiceControlAPI,
Scope: FabricRegistryScopeOrganization,
OrganizationID: "org-1",
PreferredRegion: "us",
Now: now.Add(time.Minute),
})
if !resolved.Found || resolved.Scope != FabricRegistryScopeCluster || resolved.Endpoints[0].EndpointID != "control-us" {
t.Fatalf("expected cluster fallback with preferred region endpoint, got %+v", resolved)
}
if !registry.MarkLiveVerified("cluster-1", FabricRegistryServiceControlAPI, FabricRegistryScopeOrganization, "org-1", now.Add(2*time.Minute)) {
t.Fatal("mark org live verified failed")
}
resolved = registry.ResolveService(FabricRegistryResolveRequest{
ClusterID: "cluster-1",
Service: FabricRegistryServiceControlAPI,
Scope: FabricRegistryScopeOrganization,
OrganizationID: "org-1",
Now: now.Add(2 * time.Minute),
})
if !resolved.Found || resolved.Scope != FabricRegistryScopeOrganization || resolved.Endpoints[0].EndpointID != "control-org" {
t.Fatalf("expected verified organization record, got %+v", resolved)
}
snapshot := registry.Snapshot(now.Add(2 * time.Minute))
if snapshot.Active != 2 || snapshot.Candidate != 0 {
t.Fatalf("unexpected snapshot: %+v", snapshot)
}
}
func TestFabricRegistryVerifyCandidatesPromotesAfterQUICPong(t *testing.T) {
now := time.Date(2026, 5, 18, 10, 0, 0, 0, time.UTC)
tlsConfig := testQUICTLSConfig(t)
listener := startQUICFabricEchoServerWithTLS(t, tlsConfig)
defer listener.Close()
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
issuer := FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: FabricRegistryAuthorityControl, PublicKey: publicKey}
policy := FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}
record := testFabricRegistryGossipRecord(now, 12)
record.Endpoints[0].Address = "quic://" + listener.Addr().String()
record.Endpoints[0].PeerCertSHA256 = testQUICCertSHA256(t, tlsConfig)
signed, err := SignFabricRegistryGossipRecord(record, issuer, privateKey)
if err != nil {
t.Fatalf("sign record: %v", err)
}
registry := NewFabricRegistry()
if entry, changed, err := registry.ApplyGossipRecord(signed, policy, false); err != nil || !changed || entry.State != FabricRegistryCandidate {
t.Fatalf("apply candidate changed=%t entry=%+v err=%v", changed, entry, err)
}
results := registry.VerifyCandidates(context.Background(), NewQUICFabricTransport(nil), FabricRegistryLiveProbeRequest{
ClusterID: "cluster-1",
Timeout: 3 * time.Second,
Now: now.Add(time.Second),
MaxCandidates: 1,
})
if len(results) != 1 || results[0].Status != "reachable" || !results[0].Promoted {
t.Fatalf("unexpected live probe results: %+v", results)
}
if _, ok := registry.Active("cluster-1", FabricRegistryServiceControlAPI, FabricRegistryScopeCluster, "", now.Add(time.Second)); !ok {
t.Fatal("candidate was not promoted to active")
}
}
func testFabricRegistryGossipRecord(now time.Time, epoch int64) FabricRegistryGossipRecord {
return FabricRegistryGossipRecord{
SchemaVersion: FabricRegistryGossipRecordSchema,
ClusterID: "cluster-1",
Service: FabricRegistryServiceControlAPI,
Scope: FabricRegistryScopeCluster,
Epoch: epoch,
Generation: "gen",
IssuedAt: now,
ExpiresAt: now.Add(10 * time.Minute),
IssuerNodeID: "authority-1",
IssuerRole: FabricRegistryAuthorityControl,
Endpoints: []FabricRegistryEndpoint{
{
EndpointID: "control-a",
Address: "quic://192.0.2.10:19443",
Transport: "direct_quic",
Reachability: "public",
ConnectivityMode: "direct",
Priority: 1,
},
},
}
}