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  }