1package broker
2
3import (
4 "context"
5 "testing"
6 "time"
7
8 "github.com/bnema/vev/internal/ports"
9 "github.com/stretchr/testify/require"
10)
11
12func TestRegistryBindsAuthenticatedIdentityDurablyAndIdempotently(t *testing.T) {
13 store := newMembershipStore()
14 registry, err := NewRegistryWithConfig(1, store, nil, newManualClock(time.Unix(1, 0)), nil, RegistryConfig{MembershipMode: MembershipMutable, ObservationDisabled: true})
15 require.NoError(t, err)
16 policy := poolPolicy()
17 registration, err := registry.AddHost(context.Background(), "user@example.test", policy)
18 require.NoError(t, err)
19 request := ports.BrokerIdentityBindingRequest{Fence: ports.BrokerEndpointFence{Registration: registration}, Policy: policy, Identity: "daemon-1"}
20
21 identity, err := registry.BindAuthenticatedIdentity(context.Background(), request)
22 require.NoError(t, err)
23 require.Equal(t, ports.BrokerDaemonIdentity("daemon-1"), identity)
24 require.Equal(t, ports.BrokerDaemonIdentity("daemon-1"), store.hosts.Hosts[0].Identity)
25 revision := store.hosts.Revision
26
27 identity, err = registry.BindAuthenticatedIdentity(context.Background(), request)
28 require.NoError(t, err)
29 require.Equal(t, ports.BrokerDaemonIdentity("daemon-1"), identity)
30 require.Equal(t, revision, store.hosts.Revision, "idempotent binding must not rewrite authority")
31
32 request.Identity = "daemon-2"
33 _, err = registry.BindAuthenticatedIdentity(context.Background(), request)
34 require.Error(t, err)
35 require.Equal(t, ports.BrokerDaemonIdentity("daemon-1"), store.hosts.Hosts[0].Identity)
36}
37
38func TestRegistryRejectsAuthenticatedIdentityAcrossIncompatiblePolicies(t *testing.T) {
39 store := newMembershipStore()
40 registry, err := NewRegistryWithConfig(1, store, nil, newManualClock(time.Unix(1, 0)), nil, RegistryConfig{MembershipMode: MembershipMutable, ObservationDisabled: true})
41 require.NoError(t, err)
42 policy := poolPolicy()
43 first, err := registry.AddHost(context.Background(), "first.example.test", policy)
44 require.NoError(t, err)
45 _, err = registry.BindAuthenticatedIdentity(context.Background(), ports.BrokerIdentityBindingRequest{Fence: ports.BrokerEndpointFence{Registration: first}, Policy: policy, Identity: "shared-daemon"})
46 require.NoError(t, err)
47
48 incompatible := policy
49 incompatible.Isolation = "other-isolation"
50 second, err := registry.AddHost(context.Background(), "second.example.test", incompatible)
51 require.NoError(t, err)
52 _, err = registry.BindAuthenticatedIdentity(context.Background(), ports.BrokerIdentityBindingRequest{Fence: ports.BrokerEndpointFence{Registration: second}, Policy: incompatible, Identity: "shared-daemon"})
53 var typed ports.BrokerError
54 require.ErrorAs(t, err, &typed)
55 require.Equal(t, ports.BrokerErrorConflictingPolicy, typed.Code)
56}
57
58func TestRegistryIdentityBindingIsFencedAcrossRemoveReadd(t *testing.T) {
59 store := newMembershipStore()
60 registry, err := NewRegistryWithConfig(1, store, nil, newManualClock(time.Unix(1, 0)), nil, RegistryConfig{MembershipMode: MembershipMutable, ObservationDisabled: true})
61 require.NoError(t, err)
62 policy := poolPolicy()
63 old, err := registry.AddHost(context.Background(), "user@example.test", policy)
64 require.NoError(t, err)
65 removed, err := registry.RemoveHost(context.Background(), old)
66 require.NoError(t, err)
67 require.True(t, removed)
68 fresh, err := registry.AddHost(context.Background(), old.Endpoint, policy)
69 require.NoError(t, err)
70 require.False(t, fresh.Equal(old))
71
72 _, err = registry.BindAuthenticatedIdentity(context.Background(), ports.BrokerIdentityBindingRequest{Fence: ports.BrokerEndpointFence{Registration: old}, Policy: policy, Identity: "stale"})
73 require.Error(t, err)
74 identity, err := registry.BindAuthenticatedIdentity(context.Background(), ports.BrokerIdentityBindingRequest{Fence: ports.BrokerEndpointFence{Registration: fresh}, Policy: policy, Identity: "fresh"})
75 require.NoError(t, err)
76 require.Equal(t, ports.BrokerDaemonIdentity("fresh"), identity)
77}