subscription_test.go raw
1 package bridge
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7 "time"
8 )
9
10 func TestSubscription_IsActive(t *testing.T) {
11 active := &Subscription{
12 PubkeyHex: "abc123",
13 ExpiresAt: time.Now().Add(24 * time.Hour),
14 CreatedAt: time.Now(),
15 }
16 if !active.IsActive() {
17 t.Error("subscription should be active")
18 }
19
20 expired := &Subscription{
21 PubkeyHex: "abc123",
22 ExpiresAt: time.Now().Add(-1 * time.Hour),
23 CreatedAt: time.Now().Add(-25 * time.Hour),
24 }
25 if expired.IsActive() {
26 t.Error("subscription should be expired")
27 }
28 }
29
30 func TestMemorySubscriptionStore_CRUD(t *testing.T) {
31 store := NewMemorySubscriptionStore()
32
33 sub := &Subscription{
34 PubkeyHex: "aabbccdd",
35 ExpiresAt: time.Now().Add(30 * 24 * time.Hour),
36 CreatedAt: time.Now(),
37 }
38
39 // Save
40 if err := store.Save(sub); err != nil {
41 t.Fatalf("Save: %v", err)
42 }
43
44 // Get
45 got, err := store.Get("aabbccdd")
46 if err != nil {
47 t.Fatalf("Get: %v", err)
48 }
49 if got.PubkeyHex != "aabbccdd" {
50 t.Errorf("PubkeyHex = %q", got.PubkeyHex)
51 }
52
53 // Get not found
54 _, err = store.Get("nonexistent")
55 if err == nil {
56 t.Error("expected error for nonexistent key")
57 }
58
59 // List
60 subs, err := store.List()
61 if err != nil {
62 t.Fatalf("List: %v", err)
63 }
64 if len(subs) != 1 {
65 t.Errorf("List returned %d, want 1", len(subs))
66 }
67
68 // Delete
69 if err := store.Delete("aabbccdd"); err != nil {
70 t.Fatalf("Delete: %v", err)
71 }
72 _, err = store.Get("aabbccdd")
73 if err == nil {
74 t.Error("expected error after delete")
75 }
76
77 // List after delete
78 subs, err = store.List()
79 if err != nil {
80 t.Fatalf("List after delete: %v", err)
81 }
82 if len(subs) != 0 {
83 t.Errorf("List returned %d after delete, want 0", len(subs))
84 }
85 }
86
87 func TestMemorySubscriptionStore_Overwrite(t *testing.T) {
88 store := NewMemorySubscriptionStore()
89
90 sub1 := &Subscription{
91 PubkeyHex: "aabbccdd",
92 ExpiresAt: time.Now().Add(30 * 24 * time.Hour),
93 CreatedAt: time.Now(),
94 InvoiceHash: "hash1",
95 }
96 sub2 := &Subscription{
97 PubkeyHex: "aabbccdd",
98 ExpiresAt: time.Now().Add(60 * 24 * time.Hour),
99 CreatedAt: time.Now(),
100 InvoiceHash: "hash2",
101 }
102
103 store.Save(sub1)
104 store.Save(sub2)
105
106 got, _ := store.Get("aabbccdd")
107 if got.InvoiceHash != "hash2" {
108 t.Errorf("InvoiceHash = %q, want hash2 (overwrite)", got.InvoiceHash)
109 }
110 }
111
112 func TestFileSubscriptionStore_Persistence(t *testing.T) {
113 dir := t.TempDir()
114
115 // Create store and save
116 store1, err := NewFileSubscriptionStore(dir)
117 if err != nil {
118 t.Fatalf("NewFileSubscriptionStore: %v", err)
119 }
120
121 sub := &Subscription{
122 PubkeyHex: "aabbccdd",
123 ExpiresAt: time.Now().Add(30 * 24 * time.Hour).Truncate(time.Second),
124 CreatedAt: time.Now().Truncate(time.Second),
125 InvoiceHash: "testhash",
126 }
127
128 if err := store1.Save(sub); err != nil {
129 t.Fatalf("Save: %v", err)
130 }
131
132 // Verify file exists
133 path := filepath.Join(dir, "subscriptions.json")
134 if _, err := os.Stat(path); err != nil {
135 t.Fatalf("file not created: %v", err)
136 }
137
138 // Create a new store from same dir — should load persisted data
139 store2, err := NewFileSubscriptionStore(dir)
140 if err != nil {
141 t.Fatalf("NewFileSubscriptionStore (reload): %v", err)
142 }
143
144 got, err := store2.Get("aabbccdd")
145 if err != nil {
146 t.Fatalf("Get after reload: %v", err)
147 }
148 if got.InvoiceHash != "testhash" {
149 t.Errorf("InvoiceHash = %q after reload, want testhash", got.InvoiceHash)
150 }
151
152 // Delete and verify persistence
153 if err := store2.Delete("aabbccdd"); err != nil {
154 t.Fatalf("Delete: %v", err)
155 }
156
157 store3, err := NewFileSubscriptionStore(dir)
158 if err != nil {
159 t.Fatalf("NewFileSubscriptionStore (after delete): %v", err)
160 }
161 _, err = store3.Get("aabbccdd")
162 if err == nil {
163 t.Error("expected error: subscription should be deleted after reload")
164 }
165 }
166
167 func TestFileSubscriptionStore_MultipleSubscriptions(t *testing.T) {
168 dir := t.TempDir()
169
170 store, err := NewFileSubscriptionStore(dir)
171 if err != nil {
172 t.Fatalf("NewFileSubscriptionStore: %v", err)
173 }
174
175 for i, pk := range []string{"aaaa", "bbbb", "cccc"} {
176 sub := &Subscription{
177 PubkeyHex: pk,
178 ExpiresAt: time.Now().Add(time.Duration(i+1) * 24 * time.Hour),
179 CreatedAt: time.Now(),
180 }
181 if err := store.Save(sub); err != nil {
182 t.Fatalf("Save %s: %v", pk, err)
183 }
184 }
185
186 subs, err := store.List()
187 if err != nil {
188 t.Fatalf("List: %v", err)
189 }
190 if len(subs) != 3 {
191 t.Errorf("List returned %d, want 3", len(subs))
192 }
193
194 // Delete middle one
195 if err := store.Delete("bbbb"); err != nil {
196 t.Fatalf("Delete bbbb: %v", err)
197 }
198
199 subs, err = store.List()
200 if err != nil {
201 t.Fatalf("List after delete: %v", err)
202 }
203 if len(subs) != 2 {
204 t.Errorf("List after delete returned %d, want 2", len(subs))
205 }
206 }
207
208 func TestFileSubscriptionStore_FlushToReadOnlyDir(t *testing.T) {
209 // Create a store with a path in a read-only directory
210 store := &FileSubscriptionStore{
211 path: "/dev/null/impossible/subscriptions.json",
212 subs: make(map[string]*Subscription),
213 }
214
215 sub := &Subscription{
216 PubkeyHex: "test",
217 ExpiresAt: time.Now().Add(24 * time.Hour),
218 CreatedAt: time.Now(),
219 }
220
221 err := store.Save(sub)
222 if err == nil {
223 t.Error("expected error saving to impossible path")
224 }
225 }
226
227 func TestFileSubscriptionStore_DeleteFlushError(t *testing.T) {
228 store := &FileSubscriptionStore{
229 path: "/dev/null/impossible/subscriptions.json",
230 subs: map[string]*Subscription{
231 "test": {PubkeyHex: "test", ExpiresAt: time.Now().Add(time.Hour)},
232 },
233 }
234
235 err := store.Delete("test")
236 if err == nil {
237 t.Error("expected error from flush on impossible path")
238 }
239 }
240
241 func TestFileSubscriptionStore_EmptyDir(t *testing.T) {
242 dir := t.TempDir()
243
244 store, err := NewFileSubscriptionStore(dir)
245 if err != nil {
246 t.Fatalf("NewFileSubscriptionStore: %v", err)
247 }
248
249 subs, err := store.List()
250 if err != nil {
251 t.Fatalf("List empty: %v", err)
252 }
253 if len(subs) != 0 {
254 t.Errorf("List empty returned %d, want 0", len(subs))
255 }
256 }
257