github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/watcherx/websocket_test.go (about) 1 package watcherx 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net/http/httptest" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/ory/x/logrusx" 15 16 "github.com/sirupsen/logrus/hooks/test" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 20 "github.com/ory/herodot" 21 "github.com/ory/x/urlx" 22 ) 23 24 func TestWatchWebsocket(t *testing.T) { 25 t.Run("case=forwards events", func(t *testing.T) { 26 ctx, c, dir, cancel := setup(t) 27 defer cancel() 28 29 hook := &test.Hook{} 30 l := logrusx.New("", "", logrusx.WithHook(hook)) 31 32 fn := filepath.Join(dir, "some.file") 33 f, err := os.Create(fn) 34 require.NoError(t, err) 35 36 url, err := urlx.Parse("file://" + fn) 37 require.NoError(t, err) 38 t.Log(url) 39 handler, err := WatchAndServeWS(ctx, url, herodot.NewJSONWriter(l)) 40 require.NoError(t, err) 41 s := httptest.NewServer(handler) 42 43 u := urlx.ParseOrPanic("ws" + strings.TrimLeft(s.URL, "http")) 44 _, err = WatchWebsocket(ctx, u, c) 45 require.NoError(t, err) 46 47 _, err = fmt.Fprint(f, "content here") 48 require.NoError(t, err) 49 require.NoError(t, f.Close()) 50 assertChange(t, <-c, "content here", u.String()+fn) 51 52 require.NoError(t, os.Remove(fn)) 53 assertRemove(t, <-c, u.String()+fn) 54 55 assert.Len(t, hook.Entries, 0, "%+v", hook.Entries) 56 }) 57 58 t.Run("case=client closes itself on context cancel", func(t *testing.T) { 59 ctx1, c, dir, cancel1 := setup(t) 60 defer cancel1() 61 62 hook := &test.Hook{} 63 l := logrusx.New("", "", logrusx.WithHook(hook)) 64 65 fn := filepath.Join(dir, "some.file") 66 67 handler, err := WatchAndServeWS(ctx1, urlx.ParseOrPanic("file://"+fn), herodot.NewJSONWriter(l)) 68 require.NoError(t, err) 69 s := httptest.NewServer(handler) 70 71 ctx2, cancel2 := context.WithCancel(context.Background()) 72 u := urlx.ParseOrPanic("ws" + strings.TrimLeft(s.URL, "http")) 73 _, err = WatchWebsocket(ctx2, u, c) 74 require.NoError(t, err) 75 76 cancel2() 77 78 e, ok := <-c 79 assert.False(t, ok, "%#v", e) 80 81 assert.Len(t, hook.Entries, 0, "%+v", hook.Entries) 82 }) 83 84 t.Run("case=quits client watcher when server connection is closed", func(t *testing.T) { 85 ctxClient, c, dir, cancel := setup(t) 86 defer cancel() 87 88 hook := &test.Hook{} 89 l := logrusx.New("", "", logrusx.WithHook(hook)) 90 91 fn := filepath.Join(dir, "some.file") 92 93 ctxServe, cancelServe := context.WithCancel(context.Background()) 94 handler, err := WatchAndServeWS(ctxServe, urlx.ParseOrPanic("file://"+fn), herodot.NewJSONWriter(l)) 95 require.NoError(t, err) 96 s := httptest.NewServer(handler) 97 98 u := urlx.ParseOrPanic("ws" + strings.TrimLeft(s.URL, "http")) 99 _, err = WatchWebsocket(ctxClient, u, c) 100 require.NoError(t, err) 101 102 cancelServe() 103 104 e, ok := <-c 105 assert.False(t, ok, "%#v", e) 106 107 assert.Len(t, hook.Entries, 0, "%+v", hook.Entries) 108 }) 109 110 t.Run("case=successive watching works after client connection is closed", func(t *testing.T) { 111 ctxServer, c, dir, cancel := setup(t) 112 defer cancel() 113 114 hook := &test.Hook{} 115 l := logrusx.New("", "", logrusx.WithHook(hook)) 116 117 fn := filepath.Join(dir, "some.file") 118 119 handler, err := WatchAndServeWS(ctxServer, urlx.ParseOrPanic("file://"+fn), herodot.NewJSONWriter(l)) 120 require.NoError(t, err) 121 s := httptest.NewServer(handler) 122 123 ctxClient1, cancelClient1 := context.WithCancel(context.Background()) 124 u := urlx.ParseOrPanic("ws" + strings.TrimLeft(s.URL, "http")) 125 _, err = WatchWebsocket(ctxClient1, u, c) 126 require.NoError(t, err) 127 128 cancelClient1() 129 130 _, ok := <-c 131 assert.False(t, ok) 132 133 ctxClient2, cancelClient2 := context.WithCancel(context.Background()) 134 defer cancelClient2() 135 c2 := make(EventChannel) 136 _, err = WatchWebsocket(ctxClient2, u, c2) 137 require.NoError(t, err) 138 139 f, err := os.Create(fn) 140 require.NoError(t, err) 141 require.NoError(t, f.Close()) 142 143 assertChange(t, <-c2, "", u.String()+fn) 144 145 assert.Len(t, hook.Entries, 0, "%+v", hook.Entries) 146 }) 147 148 t.Run("case=broadcasts to multiple client connections", func(t *testing.T) { 149 ctxServer, c1, dir, cancel := setup(t) 150 defer cancel() 151 152 hook := &test.Hook{} 153 l := logrusx.New("", "", logrusx.WithHook(hook)) 154 155 fn := filepath.Join(dir, "some.file") 156 157 handler, err := WatchAndServeWS(ctxServer, urlx.ParseOrPanic("file://"+fn), herodot.NewJSONWriter(l)) 158 require.NoError(t, err) 159 s := httptest.NewServer(handler) 160 161 ctxClient1, cancelClient1 := context.WithCancel(context.Background()) 162 defer cancelClient1() 163 164 u := urlx.ParseOrPanic("ws" + strings.TrimLeft(s.URL, "http")) 165 _, err = WatchWebsocket(ctxClient1, u, c1) 166 require.NoError(t, err) 167 168 ctxClient2, cancelClient2 := context.WithCancel(context.Background()) 169 defer cancelClient2() 170 c2 := make(EventChannel) 171 _, err = WatchWebsocket(ctxClient2, u, c2) 172 require.NoError(t, err) 173 174 f, err := os.Create(fn) 175 require.NoError(t, err) 176 require.NoError(t, f.Close()) 177 178 assertChange(t, <-c1, "", u.String()+fn) 179 assertChange(t, <-c2, "", u.String()+fn) 180 181 assert.Len(t, hook.Entries, 0, "%+v", hook.Entries) 182 }) 183 184 t.Run("case=sends event when requested", func(t *testing.T) { 185 ctxServer, c, dir, cancel := setup(t) 186 defer cancel() 187 188 // buffered channel to allow usage of DispatchNow().done 189 c = make(EventChannel, 1) 190 191 hook := &test.Hook{} 192 l := logrusx.New("", "", logrusx.WithHook(hook)) 193 194 fn := filepath.Join(dir, "some.file") 195 initialContent := "initial content" 196 require.NoError(t, ioutil.WriteFile(fn, []byte(initialContent), 0600)) 197 198 handler, err := WatchAndServeWS(ctxServer, urlx.ParseOrPanic("file://"+fn), herodot.NewJSONWriter(l)) 199 require.NoError(t, err) 200 s := httptest.NewServer(handler) 201 202 ctxClient, cancelClient := context.WithCancel(context.Background()) 203 defer cancelClient() 204 205 u := urlx.ParseOrPanic("ws" + strings.TrimLeft(s.URL, "http")) 206 d, err := WatchWebsocket(ctxClient, u, c) 207 require.NoError(t, err) 208 done, err := d.DispatchNow() 209 require.NoError(t, err) 210 211 // wait for d.DispatchNow to be done 212 select { 213 case <-time.After(time.Second): 214 t.Logf("Waiting for done timed out. %+v", <-c) 215 t.FailNow() 216 case eventsSend := <-done: 217 assert.Equal(t, 1, eventsSend) 218 } 219 220 assertChange(t, <-c, initialContent, u.String()+fn) 221 222 assert.Len(t, hook.Entries, 0, "%+v", hook.Entries) 223 }) 224 }