github.com/graphql-editor/azure-functions-golang-worker@v0.1.0/worker/channel_test.go (about) 1 package worker_test 2 3 import ( 4 "context" 5 "net/http" 6 "os" 7 "path/filepath" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/graphql-editor/azure-functions-golang-worker/api" 13 "github.com/graphql-editor/azure-functions-golang-worker/mocks" 14 "github.com/graphql-editor/azure-functions-golang-worker/rpc" 15 "github.com/graphql-editor/azure-functions-golang-worker/worker" 16 "github.com/pkg/errors" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/mock" 19 ) 20 21 type MockFuncForChannel map[string]interface{} 22 23 func (m MockFuncForChannel) Run(ctx context.Context, logger api.Logger) { 24 m["copy"] = append(m["original"].([]byte), []byte("-copied")...) 25 m["res"] = api.Response{ 26 StatusCode: http.StatusOK, 27 Body: []byte("body"), 28 } 29 } 30 31 func TestDefaultChannel(t *testing.T) { 32 mockFunctionType := reflect.TypeOf((*MockFuncForChannel)(nil)).Elem() 33 var mockLoader mocks.TypeLoader 34 mockLoader.On("GetFunctionType", mock.Anything, mock.Anything).Return(mockFunctionType, nil) 35 var mockSender mocks.Sender 36 mockSender.On("Send", mock.Anything) 37 ch := worker.NewChannel() 38 ch.SetEventStream(&mockSender) 39 ch.SetLoader(worker.Loader{ 40 TypeLoader: &mockLoader, 41 LoadedFunctions: map[string]worker.Function{}, 42 }) 43 ch.InitRequest("mockRequestID", &rpc.WorkerInitRequest{}) 44 mockSender.AssertCalled(t, "Send", &rpc.StreamingMessage{ 45 RequestId: "mockRequestID", 46 Content: &rpc.StreamingMessage_WorkerInitResponse{ 47 WorkerInitResponse: &rpc.WorkerInitResponse{ 48 Result: &rpc.StatusResult{ 49 Status: rpc.StatusResult_Success, 50 }, 51 Capabilities: map[string]string{ 52 "RpcHttpTriggerMetadataRemoved": "true", 53 "RpcHttpBodyOnly": "true", 54 "RawHttpBodyBytes": "true", 55 }, 56 }, 57 }, 58 }) 59 ch.FunctionLoadRequest("mockRequestID", &rpc.FunctionLoadRequest{ 60 FunctionId: "mockFunctionID", 61 Metadata: &rpc.RpcFunctionMetadata{ 62 Name: "func", 63 Bindings: map[string]*rpc.BindingInfo{ 64 "trigger": &rpc.BindingInfo{ 65 Type: "httpTrigger", 66 Direction: rpc.BindingInfo_in, 67 }, 68 "original": &rpc.BindingInfo{ 69 Type: "blob", 70 Direction: rpc.BindingInfo_in, 71 }, 72 "copy": &rpc.BindingInfo{ 73 Type: "blob", 74 Direction: rpc.BindingInfo_out, 75 }, 76 "res": &rpc.BindingInfo{ 77 Type: "http", 78 Direction: rpc.BindingInfo_out, 79 }, 80 }, 81 }, 82 }) 83 mockSender.AssertCalled(t, "Send", &rpc.StreamingMessage{ 84 RequestId: "mockRequestID", 85 Content: &rpc.StreamingMessage_FunctionLoadResponse{ 86 FunctionLoadResponse: &rpc.FunctionLoadResponse{ 87 FunctionId: "mockFunctionID", 88 Result: &rpc.StatusResult{ 89 Status: rpc.StatusResult_Success, 90 }, 91 }, 92 }, 93 }) 94 ch.InvocationRequest("mockRequestID", &rpc.InvocationRequest{ 95 FunctionId: "mockFunctionID", 96 InvocationId: "mockInvocationID", 97 InputData: []*rpc.ParameterBinding{ 98 &rpc.ParameterBinding{ 99 Name: "trigger", 100 Data: &rpc.TypedData{ 101 Data: &rpc.TypedData_Http{ 102 Http: &rpc.RpcHttp{}, 103 }, 104 }, 105 }, 106 &rpc.ParameterBinding{ 107 Name: "original", 108 Data: &rpc.TypedData{ 109 Data: &rpc.TypedData_Bytes{ 110 Bytes: []byte("original"), 111 }, 112 }, 113 }, 114 }, 115 }) 116 mockSender.AssertCalled(t, "Send", mock.MatchedBy(func(v interface{}) bool { 117 msg, result := v.(*rpc.StreamingMessage) 118 if result { 119 var resp *rpc.StreamingMessage_InvocationResponse 120 resp, result = msg.Content.(*rpc.StreamingMessage_InvocationResponse) 121 if result { 122 outputData := resp.InvocationResponse.OutputData 123 resp.InvocationResponse.OutputData = nil 124 result = result && assert.Equal(t, &rpc.StreamingMessage{ 125 RequestId: "mockRequestID", 126 Content: &rpc.StreamingMessage_InvocationResponse{ 127 InvocationResponse: &rpc.InvocationResponse{ 128 InvocationId: "mockInvocationID", 129 Result: &rpc.StatusResult{ 130 Status: rpc.StatusResult_Success, 131 }, 132 }, 133 }, 134 }, v) 135 // parameter bindings do not need to be sorted 136 result = result && assert.Len(t, outputData, 2) 137 result = result && assert.Contains(t, outputData, &rpc.ParameterBinding{ 138 Name: "copy", 139 Data: &rpc.TypedData{ 140 Data: &rpc.TypedData_Bytes{ 141 Bytes: []byte("original-copied"), 142 }, 143 }, 144 }) 145 result = result && assert.Contains(t, outputData, &rpc.ParameterBinding{ 146 Name: "res", 147 Data: &rpc.TypedData{ 148 Data: &rpc.TypedData_Http{ 149 Http: &rpc.RpcHttp{ 150 StatusCode: "200", 151 Body: &rpc.TypedData{ 152 Data: &rpc.TypedData_Bytes{ 153 Bytes: []byte("body"), 154 }, 155 }, 156 }, 157 }, 158 }, 159 }) 160 resp.InvocationResponse.OutputData = outputData 161 } 162 } 163 return result 164 })) 165 } 166 167 func TestLoadingError(t *testing.T) { 168 var mockLoader mocks.TypeLoader 169 mockLoader.On("GetFunctionType", mock.Anything, mock.Anything).Return(nil, errors.WithStack(errors.New("some error"))) 170 var mockSender mocks.Sender 171 mockSender.On("Send", mock.Anything) 172 ch := worker.NewChannel() 173 ch.SetEventStream(&mockSender) 174 ch.SetLoader(worker.Loader{ 175 TypeLoader: &mockLoader, 176 LoadedFunctions: map[string]worker.Function{}, 177 }) 178 ch.FunctionLoadRequest("mockRequestID", &rpc.FunctionLoadRequest{ 179 FunctionId: "mockFunctionID", 180 Metadata: &rpc.RpcFunctionMetadata{ 181 Name: "func", 182 Bindings: map[string]*rpc.BindingInfo{ 183 "trigger": &rpc.BindingInfo{ 184 Type: "httpTrigger", 185 Direction: rpc.BindingInfo_in, 186 }, 187 "res": &rpc.BindingInfo{ 188 Type: "http", 189 Direction: rpc.BindingInfo_out, 190 }, 191 }, 192 }, 193 }) 194 mockSender.AssertCalled(t, "Send", mock.MatchedBy(func(v interface{}) bool { 195 result := true 196 msg, ok := v.(*rpc.StreamingMessage) 197 result = result && ok 198 if !result { 199 return result 200 } 201 result = result && msg.RequestId == "mockRequestID" 202 resp, ok := msg.Content.(*rpc.StreamingMessage_FunctionLoadResponse) 203 result = result && ok 204 if !result { 205 return result 206 } 207 result = result && "mockFunctionID" == resp.FunctionLoadResponse.FunctionId 208 status := resp.FunctionLoadResponse.Result 209 result = result && rpc.StatusResult_Failure == status.Status 210 result = result && status.Exception != nil 211 result = result && status.Exception.Message != "" 212 result = result && status.Exception.StackTrace != "" 213 return result 214 })) 215 } 216 217 func TestFunctionEnvironmentReloadRequest(t *testing.T) { 218 env := os.Environ() 219 pwd, _ := os.Getwd() 220 defer func() { 221 for _, e := range env { 222 var key, value string 223 splitIdx := strings.Index(e, "=") 224 if splitIdx != -1 { 225 key = e[:splitIdx] 226 value = e[splitIdx+1:] 227 } else { 228 key = e 229 } 230 os.Setenv(key, value) 231 } 232 os.Chdir(pwd) 233 }() 234 var mockSender mocks.Sender 235 mockSender.On("Send", mock.Anything) 236 237 ch := worker.NewChannel() 238 ch.SetEventStream(&mockSender) 239 240 nwd := filepath.Join(pwd, "testdir") 241 os.Mkdir(nwd, 0777) 242 243 ch.FunctionEnvironmentReloadRequest("mockRequestID", &rpc.FunctionEnvironmentReloadRequest{ 244 EnvironmentVariables: map[string]string{ 245 "VAR": "VALUE", 246 }, 247 FunctionAppDirectory: nwd, 248 }) 249 mockSender.AssertCalled(t, "Send", &rpc.StreamingMessage{ 250 RequestId: "mockRequestID", 251 Content: &rpc.StreamingMessage_FunctionEnvironmentReloadResponse{ 252 FunctionEnvironmentReloadResponse: &rpc.FunctionEnvironmentReloadResponse{ 253 Result: &rpc.StatusResult{ 254 Status: rpc.StatusResult_Success, 255 }, 256 }, 257 }, 258 }) 259 assert.Equal(t, os.Getenv("VAR"), "VALUE") 260 actualwd, _ := os.Getwd() 261 assert.Equal(t, nwd, actualwd) 262 os.Chdir(pwd) 263 os.Remove(nwd) 264 }