mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-27 05:16:22 +02:00
226 lines
6.3 KiB
Go
226 lines
6.3 KiB
Go
package peer
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const testGenomeHash = "f1cf104ff9cfd71c6d3a2e5b8e7f9d0a4b6c8e1f3a5b7d9e2c4f6a8b0d2e4f6"
|
|
|
|
func TestNewRegistry(t *testing.T) {
|
|
r := NewRegistry("node-alpha", 0)
|
|
assert.NotEmpty(t, r.SelfID())
|
|
assert.Equal(t, "node-alpha", r.NodeName())
|
|
assert.Equal(t, 0, r.PeerCount())
|
|
}
|
|
|
|
func TestTrustLevel_String(t *testing.T) {
|
|
tests := []struct {
|
|
level TrustLevel
|
|
want string
|
|
}{
|
|
{TrustUnknown, "UNKNOWN"},
|
|
{TrustPending, "PENDING"},
|
|
{TrustVerified, "VERIFIED"},
|
|
{TrustRejected, "REJECTED"},
|
|
{TrustExpired, "EXPIRED"},
|
|
{TrustLevel(99), "INVALID"},
|
|
}
|
|
for _, tt := range tests {
|
|
assert.Equal(t, tt.want, tt.level.String())
|
|
}
|
|
}
|
|
|
|
func TestHandshake_MatchingGenomes(t *testing.T) {
|
|
alpha := NewRegistry("alpha", 30*time.Minute)
|
|
beta := NewRegistry("beta", 30*time.Minute)
|
|
|
|
// Alpha initiates handshake.
|
|
req := HandshakeRequest{
|
|
FromPeerID: alpha.SelfID(),
|
|
FromNode: alpha.NodeName(),
|
|
GenomeHash: testGenomeHash,
|
|
Timestamp: time.Now().Unix(),
|
|
Nonce: "nonce123",
|
|
}
|
|
|
|
// Beta processes it.
|
|
resp, err := beta.ProcessHandshake(req, testGenomeHash)
|
|
require.NoError(t, err)
|
|
assert.True(t, resp.Match, "Same hash must match")
|
|
assert.Equal(t, TrustVerified, resp.Trust)
|
|
assert.True(t, beta.IsTrusted(alpha.SelfID()))
|
|
assert.Equal(t, 1, beta.PeerCount())
|
|
|
|
// Alpha completes handshake with response.
|
|
err = alpha.CompleteHandshake(*resp, testGenomeHash)
|
|
require.NoError(t, err)
|
|
assert.True(t, alpha.IsTrusted(beta.SelfID()))
|
|
assert.Equal(t, 1, alpha.PeerCount())
|
|
}
|
|
|
|
func TestHandshake_MismatchedGenomes(t *testing.T) {
|
|
alpha := NewRegistry("alpha", 30*time.Minute)
|
|
beta := NewRegistry("beta", 30*time.Minute)
|
|
|
|
req := HandshakeRequest{
|
|
FromPeerID: alpha.SelfID(),
|
|
FromNode: alpha.NodeName(),
|
|
GenomeHash: "deadbeef_bad_hash",
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
|
|
resp, err := beta.ProcessHandshake(req, testGenomeHash)
|
|
require.NoError(t, err)
|
|
assert.False(t, resp.Match, "Different hashes must not match")
|
|
assert.Equal(t, TrustRejected, resp.Trust)
|
|
assert.False(t, beta.IsTrusted(alpha.SelfID()))
|
|
}
|
|
|
|
func TestHandshake_SelfHandshake_Blocked(t *testing.T) {
|
|
r := NewRegistry("self-node", 30*time.Minute)
|
|
req := HandshakeRequest{
|
|
FromPeerID: r.SelfID(),
|
|
FromNode: r.NodeName(),
|
|
GenomeHash: testGenomeHash,
|
|
}
|
|
_, err := r.ProcessHandshake(req, testGenomeHash)
|
|
assert.ErrorIs(t, err, ErrSelfHandshake)
|
|
}
|
|
|
|
func TestGetPeer_NotFound(t *testing.T) {
|
|
r := NewRegistry("node", 30*time.Minute)
|
|
_, err := r.GetPeer("nonexistent")
|
|
assert.ErrorIs(t, err, ErrPeerNotFound)
|
|
}
|
|
|
|
func TestListPeers_Empty(t *testing.T) {
|
|
r := NewRegistry("node", 30*time.Minute)
|
|
peers := r.ListPeers()
|
|
assert.Empty(t, peers)
|
|
}
|
|
|
|
func TestRecordSync(t *testing.T) {
|
|
alpha := NewRegistry("alpha", 30*time.Minute)
|
|
beta := NewRegistry("beta", 30*time.Minute)
|
|
|
|
// Establish trust.
|
|
req := HandshakeRequest{
|
|
FromPeerID: beta.SelfID(),
|
|
FromNode: beta.NodeName(),
|
|
GenomeHash: testGenomeHash,
|
|
}
|
|
_, err := alpha.ProcessHandshake(req, testGenomeHash)
|
|
require.NoError(t, err)
|
|
|
|
// Record sync.
|
|
err = alpha.RecordSync(beta.SelfID(), 5)
|
|
require.NoError(t, err)
|
|
|
|
peer, err := alpha.GetPeer(beta.SelfID())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 5, peer.FactCount)
|
|
assert.False(t, peer.LastSyncAt.IsZero())
|
|
}
|
|
|
|
func TestRecordSync_UnknownPeer(t *testing.T) {
|
|
r := NewRegistry("node", 30*time.Minute)
|
|
err := r.RecordSync("unknown_peer", 1)
|
|
assert.ErrorIs(t, err, ErrPeerNotFound)
|
|
}
|
|
|
|
func TestCheckTimeouts_ExpiresOldPeers(t *testing.T) {
|
|
r := NewRegistry("node", 1*time.Millisecond) // Ultra-short timeout
|
|
|
|
// Add a peer via handshake.
|
|
req := HandshakeRequest{
|
|
FromPeerID: "peer_old",
|
|
FromNode: "old-node",
|
|
GenomeHash: testGenomeHash,
|
|
}
|
|
_, err := r.ProcessHandshake(req, testGenomeHash)
|
|
require.NoError(t, err)
|
|
assert.True(t, r.IsTrusted("peer_old"))
|
|
|
|
// Wait for timeout.
|
|
time.Sleep(5 * time.Millisecond)
|
|
|
|
// Check timeouts — should expire and create backup.
|
|
facts := []SyncFact{
|
|
{ID: "f1", Content: "test fact", Level: 0, Source: "test"},
|
|
}
|
|
backups := r.CheckTimeouts(facts)
|
|
assert.Len(t, backups, 1)
|
|
assert.Equal(t, "peer_old", backups[0].PeerID)
|
|
assert.Equal(t, "timeout", backups[0].Reason)
|
|
|
|
// Peer should now be expired.
|
|
assert.False(t, r.IsTrusted("peer_old"))
|
|
}
|
|
|
|
func TestGeneBackup_SaveAndRetrieve(t *testing.T) {
|
|
r := NewRegistry("node", 1*time.Millisecond)
|
|
|
|
req := HandshakeRequest{
|
|
FromPeerID: "peer_backup_test",
|
|
FromNode: "backup-node",
|
|
GenomeHash: testGenomeHash,
|
|
}
|
|
_, err := r.ProcessHandshake(req, testGenomeHash)
|
|
require.NoError(t, err)
|
|
|
|
time.Sleep(5 * time.Millisecond)
|
|
|
|
facts := []SyncFact{
|
|
{ID: "gene1", Content: "survival invariant", Level: 0, IsGene: true, Source: "genome"},
|
|
}
|
|
r.CheckTimeouts(facts)
|
|
|
|
// Retrieve backup.
|
|
backup, ok := r.GetBackup("peer_backup_test")
|
|
require.True(t, ok)
|
|
assert.Equal(t, "peer_backup_test", backup.PeerID)
|
|
assert.Len(t, backup.Facts, 1)
|
|
assert.Equal(t, "gene1", backup.Facts[0].ID)
|
|
|
|
// Clear backup after recovery.
|
|
r.ClearBackup("peer_backup_test")
|
|
_, ok = r.GetBackup("peer_backup_test")
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestStats(t *testing.T) {
|
|
r := NewRegistry("stats-node", 30*time.Minute)
|
|
|
|
// Add two peers.
|
|
r.ProcessHandshake(HandshakeRequest{FromPeerID: "p1", FromNode: "n1", GenomeHash: testGenomeHash}, testGenomeHash) //nolint
|
|
r.ProcessHandshake(HandshakeRequest{FromPeerID: "p2", FromNode: "n2", GenomeHash: "bad_hash"}, testGenomeHash) //nolint
|
|
|
|
stats := r.Stats()
|
|
assert.Equal(t, 2, stats["total_peers"])
|
|
byTrust := stats["by_trust"].(map[string]int)
|
|
assert.Equal(t, 1, byTrust["VERIFIED"])
|
|
assert.Equal(t, 1, byTrust["REJECTED"])
|
|
}
|
|
|
|
func TestTrustedCount(t *testing.T) {
|
|
r := NewRegistry("node", 30*time.Minute)
|
|
assert.Equal(t, 0, r.TrustedCount())
|
|
|
|
r.ProcessHandshake(HandshakeRequest{FromPeerID: "p1", FromNode: "n1", GenomeHash: testGenomeHash}, testGenomeHash) //nolint
|
|
r.ProcessHandshake(HandshakeRequest{FromPeerID: "p2", FromNode: "n2", GenomeHash: testGenomeHash}, testGenomeHash) //nolint
|
|
r.ProcessHandshake(HandshakeRequest{FromPeerID: "p3", FromNode: "n3", GenomeHash: "wrong"}, testGenomeHash) //nolint
|
|
|
|
assert.Equal(t, 2, r.TrustedCount())
|
|
}
|
|
|
|
func TestPeerInfo_IsAlive(t *testing.T) {
|
|
p := &PeerInfo{LastSeen: time.Now()}
|
|
assert.True(t, p.IsAlive(1*time.Hour))
|
|
|
|
p.LastSeen = time.Now().Add(-2 * time.Hour)
|
|
assert.False(t, p.IsAlive(1*time.Hour))
|
|
}
|