bridge_test.go raw

   1  package bridge
   2  
   3  import (
   4  	"context"
   5  	"sync"
   6  	"testing"
   7  	"time"
   8  
   9  	"next.orly.dev/pkg/nostr/crypto/keys"
  10  	"github.com/stretchr/testify/assert"
  11  	"github.com/stretchr/testify/require"
  12  )
  13  
  14  // Ensure Bridge wg field is accessible for testing.
  15  var _ = (*sync.WaitGroup)(nil)
  16  
  17  func TestBridge_StartStop(t *testing.T) {
  18  	sk, err := keys.GenerateSecretKey()
  19  	require.NoError(t, err)
  20  
  21  	cfg := &Config{
  22  		NSEC:    keys.GenerateSecretKeyHex(),
  23  		DataDir: t.TempDir(),
  24  	}
  25  	_ = sk // we use GenerateSecretKeyHex for a quick hex key
  26  
  27  	b := New(cfg, nil)
  28  
  29  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  30  	defer cancel()
  31  
  32  	require.NoError(t, b.Start(ctx))
  33  	assert.NotNil(t, b.Signer())
  34  	assert.Len(t, b.Signer().Pub(), 32)
  35  	assert.Equal(t, IdentityFromConfig, b.IdentitySource())
  36  
  37  	b.Stop()
  38  }
  39  
  40  func TestBridge_StartWithDBGetter(t *testing.T) {
  41  	sk, err := keys.GenerateSecretKey()
  42  	require.NoError(t, err)
  43  
  44  	cfg := &Config{
  45  		DataDir: t.TempDir(),
  46  	}
  47  
  48  	dbGetter := func() ([]byte, error) {
  49  		return sk, nil
  50  	}
  51  
  52  	b := New(cfg, dbGetter)
  53  
  54  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  55  	defer cancel()
  56  
  57  	require.NoError(t, b.Start(ctx))
  58  	assert.Equal(t, IdentityFromDB, b.IdentitySource())
  59  	assert.NotNil(t, b.Signer())
  60  
  61  	b.Stop()
  62  }
  63  
  64  func TestBridge_StartFileGeneration(t *testing.T) {
  65  	cfg := &Config{
  66  		DataDir: t.TempDir(),
  67  	}
  68  
  69  	b := New(cfg, nil)
  70  
  71  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  72  	defer cancel()
  73  
  74  	require.NoError(t, b.Start(ctx))
  75  	assert.Equal(t, IdentityFromFile, b.IdentitySource())
  76  	assert.NotNil(t, b.Signer())
  77  	assert.Len(t, b.Signer().Pub(), 32)
  78  
  79  	b.Stop()
  80  }
  81  
  82  func TestBridge_StartWithDomain(t *testing.T) {
  83  	cfg := &Config{
  84  		NSEC:    keys.GenerateSecretKeyHex(),
  85  		DataDir: t.TempDir(),
  86  		Domain:  "bridge.example.com",
  87  	}
  88  
  89  	b := New(cfg, nil)
  90  
  91  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  92  	defer cancel()
  93  
  94  	require.NoError(t, b.Start(ctx))
  95  	b.Stop()
  96  }
  97  
  98  func TestBridge_StopWithoutStart(t *testing.T) {
  99  	cfg := &Config{
 100  		DataDir: t.TempDir(),
 101  	}
 102  	b := New(cfg, nil)
 103  	// Should not panic
 104  	b.Stop()
 105  }
 106  
 107  func TestBridge_RelayWatchLoop(t *testing.T) {
 108  	// relayWatchLoop just blocks on ctx.Done — verify it exits cleanly
 109  	cfg := &Config{
 110  		NSEC:    keys.GenerateSecretKeyHex(),
 111  		DataDir: t.TempDir(),
 112  	}
 113  	b := New(cfg, nil)
 114  	b.ctx, b.cancel = context.WithCancel(context.Background())
 115  	b.wg.Add(1)
 116  	go b.relayWatchLoop()
 117  
 118  	// Cancel should cause relayWatchLoop to exit
 119  	b.cancel()
 120  	b.wg.Wait() // will hang if relayWatchLoop doesn't exit
 121  }
 122  
 123  func TestIdentitySourceString(t *testing.T) {
 124  	tests := []struct {
 125  		source IdentitySource
 126  		want   string
 127  	}{
 128  		{IdentityFromConfig, "config"},
 129  		{IdentityFromDB, "database"},
 130  		{IdentityFromFile, "file"},
 131  		{IdentitySource(99), "unknown"},
 132  	}
 133  	for _, tt := range tests {
 134  		got := identitySourceString(tt.source)
 135  		if got != tt.want {
 136  			t.Errorf("identitySourceString(%d) = %q, want %q", tt.source, got, tt.want)
 137  		}
 138  	}
 139  }
 140  
 141  func TestBridge_StartWithRelayURL(t *testing.T) {
 142  	// Starting with a RelayURL that can't connect should fail
 143  	cfg := &Config{
 144  		NSEC:     keys.GenerateSecretKeyHex(),
 145  		DataDir:  t.TempDir(),
 146  		RelayURL: "wss://nonexistent.example.com",
 147  	}
 148  
 149  	b := New(cfg, nil)
 150  
 151  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
 152  	defer cancel()
 153  
 154  	err := b.Start(ctx)
 155  	if err == nil {
 156  		b.Stop()
 157  		t.Fatal("expected error connecting to nonexistent relay")
 158  	}
 159  }
 160  
 161  func TestBridge_StartBadDataDir(t *testing.T) {
 162  	cfg := &Config{
 163  		NSEC:    keys.GenerateSecretKeyHex(),
 164  		DataDir: "/dev/null/impossible/path",
 165  	}
 166  
 167  	b := New(cfg, nil)
 168  
 169  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
 170  	defer cancel()
 171  
 172  	err := b.Start(ctx)
 173  	if err == nil {
 174  		b.Stop()
 175  		t.Fatal("expected error for impossible data dir")
 176  	}
 177  }
 178  
 179  func TestBridge_StartInvalidNSEC(t *testing.T) {
 180  	cfg := &Config{
 181  		NSEC:    "nsecINVALID",
 182  		DataDir: t.TempDir(),
 183  	}
 184  
 185  	b := New(cfg, nil)
 186  
 187  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
 188  	defer cancel()
 189  
 190  	err := b.Start(ctx)
 191  	if err == nil {
 192  		b.Stop()
 193  		t.Fatal("expected error from invalid NSEC")
 194  	}
 195  }
 196  
 197  func TestBridge_StopWithRelay(t *testing.T) {
 198  	// Test Stop when relay is set but not connected (just Close on RelayConn)
 199  	cfg := &Config{
 200  		NSEC:    keys.GenerateSecretKeyHex(),
 201  		DataDir: t.TempDir(),
 202  	}
 203  
 204  	b := New(cfg, nil)
 205  
 206  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 207  	defer cancel()
 208  
 209  	require.NoError(t, b.Start(ctx))
 210  
 211  	// Manually set relay to test Stop path
 212  	b.relay = NewRelayConn("wss://example.com", nil)
 213  
 214  	b.Stop()
 215  }
 216