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  }