nwc_test.go raw
1 package nwc_test
2
3 import (
4 "context"
5 "testing"
6 "time"
7
8 "next.orly.dev/pkg/protocol/nwc"
9 "next.orly.dev/pkg/nostr/ws"
10 )
11
12 func TestNWCClientCreation(t *testing.T) {
13 uri := "nostr+walletconnect://816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b?relay=wss://relay.getalby.com/v1&secret=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
14
15 c, err := nwc.NewClient(uri)
16 if err != nil {
17 t.Fatal(err)
18 }
19
20 if c == nil {
21 t.Fatal("client should not be nil")
22 }
23 }
24
25 func TestNWCInvalidURI(t *testing.T) {
26 invalidURIs := []string{
27 "invalid://test",
28 "nostr+walletconnect://",
29 "nostr+walletconnect://invalid",
30 "nostr+walletconnect://816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b",
31 "nostr+walletconnect://816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b?relay=invalid",
32 }
33
34 for _, uri := range invalidURIs {
35 _, err := nwc.NewClient(uri)
36 if err == nil {
37 t.Fatalf("expected error for invalid URI: %s", uri)
38 }
39 }
40 }
41
42 func TestNWCRelayConnection(t *testing.T) {
43 ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
44 defer cancel()
45
46 rc, err := ws.RelayConnect(ctx, "wss://relay.getalby.com/v1")
47 if err != nil {
48 t.Fatalf("relay connection failed: %v", err)
49 }
50 defer rc.Close()
51
52 t.Log("relay connection successful")
53 }
54
55 func TestNWCRequestTimeout(t *testing.T) {
56 uri := "nostr+walletconnect://816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b?relay=wss://relay.getalby.com/v1&secret=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
57
58 c, err := nwc.NewClient(uri)
59 if err != nil {
60 t.Fatal(err)
61 }
62
63 ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)
64 defer cancel()
65
66 var r map[string]any
67 err = c.Request(ctx, "get_info", nil, &r)
68
69 if err == nil {
70 t.Log("wallet responded")
71 return
72 }
73
74 expectedErrors := []string{
75 "no response from wallet",
76 "subscription closed",
77 "timeout waiting for response",
78 "context deadline exceeded",
79 }
80
81 errorFound := false
82 for _, expected := range expectedErrors {
83 if contains(err.Error(), expected) {
84 errorFound = true
85 break
86 }
87 }
88
89 if !errorFound {
90 t.Fatalf("unexpected error: %v", err)
91 }
92
93 t.Logf("proper timeout handling: %v", err)
94 }
95
96 func contains(s, substr string) bool {
97 return len(s) >= len(substr) && (s == substr || (len(s) > len(substr) &&
98 (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr ||
99 findInString(s, substr))))
100 }
101
102 func findInString(s, substr string) bool {
103 for i := 0; i <= len(s)-len(substr); i++ {
104 if s[i:i+len(substr)] == substr {
105 return true
106 }
107 }
108 return false
109 }
110
111 func TestNWCEncryption(t *testing.T) {
112 uri := "nostr+walletconnect://816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b?relay=wss://relay.getalby.com/v1&secret=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
113
114 c, err := nwc.NewClient(uri)
115 if err != nil {
116 t.Fatal(err)
117 }
118
119 // We can't directly access private fields, but we can test the client creation
120 // check conversation key generation
121 if c == nil {
122 t.Fatal("client creation should succeed with valid URI")
123 }
124
125 // Test passed
126 }
127
128 func TestNWCEventFormat(t *testing.T) {
129 uri := "nostr+walletconnect://816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b?relay=wss://relay.getalby.com/v1&secret=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
130
131 c, err := nwc.NewClient(uri)
132 if err != nil {
133 t.Fatal(err)
134 }
135
136 // Test client creation
137 // The Request method will create proper NWC events with:
138 // - Kind 23194 for requests
139 // - Proper encryption tag
140 // - Signed with client key
141
142 ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
143 defer cancel()
144
145 var r map[string]any
146 err = c.Request(ctx, "get_info", nil, &r)
147
148 // We expect this to fail due to inactive connection, but it should fail
149 // after creating and sending NWC event
150 if err == nil {
151 t.Log("wallet responded")
152 return
153 }
154
155 // Verify it failed for the right reason (connection/response issue, not formatting)
156 validFailures := []string{
157 "subscription closed",
158 "no response from wallet",
159 "context deadline exceeded",
160 "timeout waiting for response",
161 }
162
163 validFailure := false
164 for _, failure := range validFailures {
165 if contains(err.Error(), failure) {
166 validFailure = true
167 break
168 }
169 }
170
171 if !validFailure {
172 t.Fatalf("unexpected error type (suggests formatting issue): %v", err)
173 }
174
175 // Test passed
176 }
177