clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

main
tinyrelay / internal / relaycmd / runner_test.go
runner_test.go 2.2K
1package relaycmd23import (4 "context"5 "io"6 "sync"7 "testing"8 "time"9)1011func TestParseDefaultsAndEnvironment(t *testing.T) {12 t.Setenv("TINY_RELAY_DATA_DIR", "/tmp/relay-data")13 opts, err := parse([]string{"--name", "Example"})14 if err != nil {15 t.Fatal(err)16 }17 if opts.dataDir != "/tmp/relay-data" || opts.listen != "127.0.0.1:7447" {18 t.Fatalf("defaults = %#v", opts)19 }20 if opts.maxMessageBytes != 1<<20 || opts.maxPendingBytes != 4<<20 {21 t.Fatalf("limits = %#v", opts)22 }23 if opts.name != "Example" {24 t.Fatalf("name = %q", opts.name)25 }26}2728func TestParseNormalizesRelayURL(t *testing.T) {29 tests := []struct {30 input string31 want string32 }{33 {"http://relay.example/", "ws://relay.example"},34 {"https://relay.example/nostr/", "wss://relay.example/nostr"},35 {"ws://relay.example", "ws://relay.example"},36 {"wss://relay.example/", "wss://relay.example"},37 }38 for _, test := range tests {39 t.Run(test.input, func(t *testing.T) {40 opts, err := parse([]string{"--public-url", test.input})41 if err != nil {42 t.Fatal(err)43 }44 if opts.publicURL != test.want {45 t.Fatalf("public URL = %q, want %q", opts.publicURL, test.want)46 }47 })48 }49}5051func TestParseRejectsInvalidArguments(t *testing.T) {52 for _, args := range [][]string{53 {"--public-url", "ftp://relay.example"},54 {"--owner", "not-a-public-key"},55 {"--max-message-bytes", "0"},56 {"--max-pending-bytes", "-1"},57 {"extra"},58 } {59 if _, err := parse(args); err == nil {60 t.Fatalf("accepted %v", args)61 }62 }63}6465func TestRunStopsWithContext(t *testing.T) {66 ctx, cancel := context.WithCancel(context.Background())67 defer cancel()68 output := &signalWriter{ready: make(chan struct{})}69 done := make(chan error, 1)70 go func() {71 done <- Run(ctx, []string{"--data-dir", t.TempDir(), "--listen", "127.0.0.1:0"}, output)72 }()7374 select {75 case <-output.ready:76 case <-time.After(5 * time.Second):77 t.Fatal("runner did not start")78 }79 cancel()80 select {81 case err := <-done:82 if err != nil {83 t.Fatal(err)84 }85 case <-time.After(5 * time.Second):86 t.Fatal("runner did not stop")87 }88}8990type signalWriter struct {91 ready chan struct{}92 once sync.Once93}9495var _ io.Writer = (*signalWriter)(nil)9697func (w *signalWriter) Write(p []byte) (int, error) {98 w.once.Do(func() { close(w.ready) })99 return len(p), nil100}