github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/realtime/realtime_test.go (about) 1 package realtime 2 3 import ( 4 "sync" 5 "testing" 6 "time" 7 8 "github.com/cozy/cozy-stack/pkg/prefixer" 9 "github.com/redis/go-redis/v9" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 var testingDB = prefixer.NewPrefixer(0, "testing", "testing") 14 15 type testDoc struct { 16 id string 17 doctype string 18 } 19 20 func (t *testDoc) ID() string { return t.id } 21 func (t *testDoc) DocType() string { return t.doctype } 22 func (t *testDoc) MarshalJSON() ([]byte, error) { 23 j := `{"_id":"` + t.id + `", "_type":"` + t.doctype + `"}` 24 return []byte(j), nil 25 } 26 27 func TestMemRealtime(t *testing.T) { 28 h := newMemHub() 29 c1 := h.Subscriber(testingDB) 30 c2 := h.Subscriber(testingDB) 31 c3 := h.SubscribeFirehose() 32 wg := sync.WaitGroup{} 33 34 c1.Subscribe("io.cozy.testobject") 35 c2.Subscribe("io.cozy.testobject") 36 37 wg.Add(1) 38 go func() { 39 for e := range c1.Channel { 40 assert.Equal(t, "foo", e.Doc.ID()) 41 break 42 } 43 wg.Done() 44 }() 45 46 wg.Add(1) 47 go func() { 48 for e := range c2.Channel { 49 assert.Equal(t, "foo", e.Doc.ID()) 50 break 51 } 52 wg.Done() 53 }() 54 55 wg.Add(1) 56 go func() { 57 for e := range c3.Channel { 58 assert.Equal(t, "testing", e.Domain) 59 assert.Equal(t, "foo", e.Doc.ID()) 60 break 61 } 62 wg.Done() 63 }() 64 65 time.AfterFunc(10*time.Millisecond, func() { 66 h.Publish(testingDB, EventCreate, &testDoc{doctype: "io.cozy.testobject", id: "foo"}, nil) 67 }) 68 69 wg.Wait() 70 71 c1.Close() 72 c2.Close() 73 c3.Close() 74 75 c1.Close() 76 77 h.Publish(testingDB, EventCreate, &testDoc{doctype: "io.cozy.testobject", id: "nobodywillseeme"}, nil) 78 h.Publish(testingDB, EventCreate, &testDoc{doctype: "io.cozy.testobject", id: "meneither"}, nil) 79 time.Sleep(1 * time.Millisecond) 80 81 c4 := h.Subscriber(testingDB) 82 c4.Subscribe("io.cozy.testobject") 83 c4.Subscribe("io.cozy.testobject2") 84 defer c4.Close() 85 86 wg.Add(2) 87 go func() { 88 expected := "bar" 89 for e := range c4.Channel { 90 assert.Equal(t, expected, e.Doc.ID()) 91 wg.Done() 92 if expected == "baz" { 93 break 94 } 95 expected = "baz" 96 } 97 }() 98 99 time.AfterFunc(10*time.Millisecond, func() { 100 h.Publish(testingDB, EventCreate, &testDoc{ 101 doctype: "io.cozy.testobject", 102 id: "bar", 103 }, nil) 104 }) 105 time.AfterFunc(20*time.Millisecond, func() { 106 h.Publish(testingDB, EventCreate, &testDoc{ 107 doctype: "io.cozy.testobject2", 108 id: "baz", 109 }, nil) 110 }) 111 112 wg.Wait() 113 } 114 115 func TestWatch(t *testing.T) { 116 h := newMemHub() 117 c1 := h.Subscriber(testingDB) 118 wg := sync.WaitGroup{} 119 120 c1.Watch("io.cozy.testobject", "id1") 121 c1.Watch("io.cozy.testobject", "id2") 122 123 wg.Add(1) 124 go func() { 125 for e := range c1.Channel { 126 assert.Equal(t, "id1", e.Doc.ID()) 127 break 128 } 129 for e := range c1.Channel { 130 assert.Equal(t, "id2", e.Doc.ID()) 131 break 132 } 133 wg.Done() 134 }() 135 136 time.Sleep(1 * time.Millisecond) 137 h.Publish(testingDB, EventCreate, &testDoc{ 138 doctype: "io.cozy.testobject", 139 id: "not-id1-and-not-id2", 140 }, nil) 141 142 time.Sleep(1 * time.Millisecond) 143 h.Publish(testingDB, EventCreate, &testDoc{ 144 doctype: "io.cozy.testobject", 145 id: "id1", 146 }, nil) 147 148 time.Sleep(1 * time.Millisecond) 149 h.Publish(testingDB, EventCreate, &testDoc{ 150 doctype: "io.cozy.testobject", 151 id: "id2", 152 }, nil) 153 154 wg.Wait() 155 156 c1.Subscribe("io.cozy.testobject") 157 158 wg.Add(1) 159 go func() { 160 for e := range c1.Channel { 161 assert.Equal(t, "id1", e.Doc.ID()) 162 break 163 } 164 for e := range c1.Channel { 165 assert.Equal(t, "id2", e.Doc.ID()) 166 break 167 } 168 for e := range c1.Channel { 169 assert.Equal(t, "id3", e.Doc.ID()) 170 break 171 } 172 wg.Done() 173 }() 174 175 time.Sleep(1 * time.Millisecond) 176 h.Publish(testingDB, EventCreate, &testDoc{ 177 doctype: "io.cozy.testobject", 178 id: "id1", 179 }, nil) 180 181 time.Sleep(1 * time.Millisecond) 182 h.Publish(testingDB, EventCreate, &testDoc{ 183 doctype: "io.cozy.testobject", 184 id: "id2", 185 }, nil) 186 187 time.Sleep(1 * time.Millisecond) 188 h.Publish(testingDB, EventCreate, &testDoc{ 189 doctype: "io.cozy.testobject", 190 id: "id3", 191 }, nil) 192 193 wg.Wait() 194 195 c1.Close() 196 } 197 198 func TestSubscribeWatchUnwatch(t *testing.T) { 199 h := newMemHub() 200 sub := h.Subscriber(testingDB) 201 defer sub.Close() 202 203 sub.Subscribe("io.cozy.testobject") 204 time.Sleep(1 * time.Millisecond) 205 sub.Watch("io.cozy.testobject", "id1") 206 time.Sleep(1 * time.Millisecond) 207 sub.Unwatch("io.cozy.testobject", "id1") 208 time.Sleep(1 * time.Millisecond) 209 210 h.Publish(testingDB, EventCreate, &testDoc{ 211 doctype: "io.cozy.testobject", 212 id: "id2", 213 }, nil) 214 e := <-sub.Channel 215 assert.Equal(t, "id2", e.Doc.ID()) 216 } 217 218 func TestRedisRealtime(t *testing.T) { 219 if testing.Short() { 220 t.Skip("a redis is required for this test: test skipped due to the use of --short flag") 221 } 222 223 opt, err := redis.ParseURL("redis://localhost:6379/6") 224 assert.NoError(t, err) 225 client := redis.NewClient(opt) 226 h := newRedisHub(client) 227 c1 := h.Subscriber(testingDB) 228 c2 := h.Subscriber(testingDB) 229 c3 := h.SubscribeFirehose() 230 wg := sync.WaitGroup{} 231 232 c1.Subscribe("io.cozy.testobject") 233 c2.Subscribe("io.cozy.testobject") 234 235 wg.Add(1) 236 go func() { 237 for e := range c1.Channel { 238 assert.Equal(t, "foo", e.Doc.ID()) 239 break 240 } 241 wg.Done() 242 }() 243 244 wg.Add(1) 245 go func() { 246 for e := range c2.Channel { 247 assert.Equal(t, "foo", e.Doc.ID()) 248 break 249 } 250 wg.Done() 251 }() 252 253 wg.Add(1) 254 go func() { 255 for e := range c3.Channel { 256 assert.Equal(t, "testing", e.Domain) 257 assert.Equal(t, "foo", e.Doc.ID()) 258 break 259 } 260 wg.Done() 261 }() 262 263 time.AfterFunc(10*time.Millisecond, func() { 264 h.Publish(testingDB, EventCreate, &testDoc{ 265 doctype: "io.cozy.testobject", 266 id: "foo", 267 }, nil) 268 }) 269 270 wg.Wait() 271 272 c1.Close() 273 c2.Close() 274 c3.Close() 275 276 c1.Close() 277 278 h.Publish(testingDB, EventCreate, &testDoc{ 279 doctype: "io.cozy.testobject", 280 id: "nobodywillseeme", 281 }, nil) 282 time.Sleep(100 * time.Millisecond) 283 284 h.Publish(testingDB, EventCreate, &testDoc{ 285 doctype: "io.cozy.testobject", 286 id: "meneither", 287 }, nil) 288 time.Sleep(100 * time.Millisecond) 289 290 c4 := h.Subscriber(testingDB) 291 c4.Subscribe("io.cozy.testobject") 292 c4.Subscribe("io.cozy.testobject2") 293 defer c4.Close() 294 295 wg.Add(2) 296 go func() { 297 expected := "bar" 298 for e := range c4.Channel { 299 assert.Equal(t, expected, e.Doc.ID()) 300 wg.Done() 301 if expected == "baz" { 302 break 303 } 304 expected = "baz" 305 } 306 }() 307 308 time.AfterFunc(10*time.Millisecond, func() { 309 h.Publish(testingDB, EventCreate, &testDoc{ 310 doctype: "io.cozy.testobject", 311 id: "bar", 312 }, nil) 313 }) 314 time.AfterFunc(20*time.Millisecond, func() { 315 h.Publish(testingDB, EventCreate, &testDoc{ 316 doctype: "io.cozy.testobject2", 317 id: "baz", 318 }, nil) 319 }) 320 321 wg.Wait() 322 }