github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+incompatible/plugin/rpcplugin/hooks_test.go (about) 1 package rpcplugin 2 3 import ( 4 "io" 5 "io/ioutil" 6 "net/http" 7 "net/http/httptest" 8 "strings" 9 "sync" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/mock" 14 "github.com/stretchr/testify/require" 15 16 "github.com/mattermost/mattermost-server/model" 17 "github.com/mattermost/mattermost-server/plugin" 18 "github.com/mattermost/mattermost-server/plugin/plugintest" 19 ) 20 21 func testHooksRPC(hooks interface{}, f func(*RemoteHooks)) error { 22 r1, w1 := io.Pipe() 23 r2, w2 := io.Pipe() 24 25 c1 := NewMuxer(NewReadWriteCloser(r1, w2), false) 26 defer c1.Close() 27 28 c2 := NewMuxer(NewReadWriteCloser(r2, w1), true) 29 defer c2.Close() 30 31 id, server := c1.Serve() 32 go ServeHooks(hooks, server, c1) 33 34 remote, err := ConnectHooks(c2.Connect(id), c2) 35 if err != nil { 36 return err 37 } 38 defer remote.Close() 39 40 f(remote) 41 return nil 42 } 43 44 func TestHooks(t *testing.T) { 45 var api plugintest.API 46 var hooks plugintest.Hooks 47 defer hooks.AssertExpectations(t) 48 49 assert.NoError(t, testHooksRPC(&hooks, func(remote *RemoteHooks) { 50 hooks.On("OnActivate", mock.AnythingOfType("*rpcplugin.RemoteAPI")).Return(nil) 51 assert.NoError(t, remote.OnActivate(&api)) 52 53 hooks.On("OnDeactivate").Return(nil) 54 assert.NoError(t, remote.OnDeactivate()) 55 56 hooks.On("OnConfigurationChange").Return(nil) 57 assert.NoError(t, remote.OnConfigurationChange()) 58 59 hooks.On("ServeHTTP", mock.AnythingOfType("*rpcplugin.RemoteHTTPResponseWriter"), mock.AnythingOfType("*http.Request")).Run(func(args mock.Arguments) { 60 w := args.Get(0).(http.ResponseWriter) 61 r := args.Get(1).(*http.Request) 62 assert.Equal(t, "/foo", r.URL.Path) 63 assert.Equal(t, "POST", r.Method) 64 body, err := ioutil.ReadAll(r.Body) 65 assert.NoError(t, err) 66 assert.Equal(t, "asdf", string(body)) 67 assert.Equal(t, "header", r.Header.Get("Test-Header")) 68 w.Write([]byte("bar")) 69 }) 70 71 w := httptest.NewRecorder() 72 r, err := http.NewRequest("POST", "/foo", strings.NewReader("asdf")) 73 r.Header.Set("Test-Header", "header") 74 assert.NoError(t, err) 75 remote.ServeHTTP(w, r) 76 77 resp := w.Result() 78 defer resp.Body.Close() 79 assert.Equal(t, http.StatusOK, resp.StatusCode) 80 body, err := ioutil.ReadAll(resp.Body) 81 assert.NoError(t, err) 82 assert.Equal(t, "bar", string(body)) 83 84 hooks.On("ExecuteCommand", &model.CommandArgs{ 85 Command: "/foo", 86 }).Return(&model.CommandResponse{ 87 Text: "bar", 88 }, nil) 89 commandResponse, appErr := hooks.ExecuteCommand(&model.CommandArgs{ 90 Command: "/foo", 91 }) 92 assert.Equal(t, "bar", commandResponse.Text) 93 assert.Nil(t, appErr) 94 })) 95 } 96 97 func TestHooks_Concurrency(t *testing.T) { 98 var hooks plugintest.Hooks 99 defer hooks.AssertExpectations(t) 100 101 assert.NoError(t, testHooksRPC(&hooks, func(remote *RemoteHooks) { 102 ch := make(chan bool) 103 104 hooks.On("ServeHTTP", mock.AnythingOfType("*rpcplugin.RemoteHTTPResponseWriter"), mock.AnythingOfType("*http.Request")).Run(func(args mock.Arguments) { 105 r := args.Get(1).(*http.Request) 106 if r.URL.Path == "/1" { 107 <-ch 108 } else { 109 ch <- true 110 } 111 }) 112 113 rec := httptest.NewRecorder() 114 115 wg := sync.WaitGroup{} 116 wg.Add(2) 117 118 go func() { 119 req, err := http.NewRequest("GET", "/1", nil) 120 require.NoError(t, err) 121 remote.ServeHTTP(rec, req) 122 wg.Done() 123 }() 124 125 go func() { 126 req, err := http.NewRequest("GET", "/2", nil) 127 require.NoError(t, err) 128 remote.ServeHTTP(rec, req) 129 wg.Done() 130 }() 131 132 wg.Wait() 133 })) 134 } 135 136 type testHooks struct { 137 mock.Mock 138 } 139 140 func (h *testHooks) OnActivate(api plugin.API) error { 141 return h.Called(api).Error(0) 142 } 143 144 func TestHooks_PartiallyImplemented(t *testing.T) { 145 var api plugintest.API 146 var hooks testHooks 147 defer hooks.AssertExpectations(t) 148 149 assert.NoError(t, testHooksRPC(&hooks, func(remote *RemoteHooks) { 150 implemented, err := remote.Implemented() 151 assert.NoError(t, err) 152 assert.Equal(t, []string{"OnActivate"}, implemented) 153 154 hooks.On("OnActivate", mock.AnythingOfType("*rpcplugin.RemoteAPI")).Return(nil) 155 assert.NoError(t, remote.OnActivate(&api)) 156 157 assert.NoError(t, remote.OnDeactivate()) 158 })) 159 } 160 161 type benchmarkHooks struct{} 162 163 func (*benchmarkHooks) OnDeactivate() error { return nil } 164 165 func (*benchmarkHooks) ServeHTTP(w http.ResponseWriter, r *http.Request) { 166 ioutil.ReadAll(r.Body) 167 w.Header().Set("Foo-Header", "foo") 168 http.Error(w, "foo", http.StatusBadRequest) 169 } 170 171 func BenchmarkHooks_OnDeactivate(b *testing.B) { 172 var hooks benchmarkHooks 173 174 if err := testHooksRPC(&hooks, func(remote *RemoteHooks) { 175 b.ResetTimer() 176 for n := 0; n < b.N; n++ { 177 remote.OnDeactivate() 178 } 179 b.StopTimer() 180 }); err != nil { 181 b.Fatal(err.Error()) 182 } 183 } 184 185 func BenchmarkHooks_ServeHTTP(b *testing.B) { 186 var hooks benchmarkHooks 187 188 if err := testHooksRPC(&hooks, func(remote *RemoteHooks) { 189 b.ResetTimer() 190 for n := 0; n < b.N; n++ { 191 w := httptest.NewRecorder() 192 r, _ := http.NewRequest("POST", "/foo", strings.NewReader("12345678901234567890")) 193 remote.ServeHTTP(w, r) 194 } 195 b.StopTimer() 196 }); err != nil { 197 b.Fatal(err.Error()) 198 } 199 } 200 201 func BenchmarkHooks_Unimplemented(b *testing.B) { 202 var hooks testHooks 203 204 if err := testHooksRPC(&hooks, func(remote *RemoteHooks) { 205 b.ResetTimer() 206 for n := 0; n < b.N; n++ { 207 remote.OnDeactivate() 208 } 209 b.StopTimer() 210 }); err != nil { 211 b.Fatal(err.Error()) 212 } 213 }