anz-bank / sysl-go

Communication library used by SYSL-generated code written in Go.
Apache License 2.0
10 stars 14 forks source link

A unit testing friendly way to access the downstream HTTP response code in BFF handlers #220

Closed uynap closed 4 years ago

uynap commented 4 years ago

Accessing the downstream HTTP response code is supported in current Sysl generated code, but it's hard to have unit testing.

Current way: In the BFF endpoint that you want to access the downstream HTTP response code, firstly call ctx = common.ProvisionRestResult(ctx) Then, make the downstream call. Followed by using the common.GetRestResult(ctx) to retrieve the response information.

The problem is I can't mock the downstream response because the ProvisionRestResult method always reset the response inside of the handler.

Suggested approaches

I think the most easier solution is adding the ctx = common.ProvisionRestResult(ctx) to the endpoint's wrapper in the bff/servicehandler.go. It's a kind of initiating a context key and it's not a breaking change to others.

anz-rfc commented 4 years ago

My understanding of this situation is as follows:

(a) there is hand-written code to handle the PostFoo endpoint (b) PostFoo endpoint needs to call PostBarr on some downstream system (c) the hand-written PostFoo handler code needs to access the raw common.RestResult (e.g. to get the raw HTTP status code), instead of using the type returned by the generated client code. (d) we want to unit test the hand-written PostFoo handler code by mocking the PostFooClient's PostBarr method.

E.g. the situation might look something like:

// hand-written code to handle the PostFoo endpoint:

func (h *Handler) PostFoo(ctx context.Context, req *app.PostFooRequest, client app.PostFooClient) (*app.PostFooResponse, error) {
    ctx = common.ProvisionRestResult(h.ctx)  // step [1]

    request := ...etc... // build PostBarrRequest to send to downstream Barr system
    response, err := client.PostBarr(h.ctx, &request)
    if err != nil {
        return nil, err
    }
    responseHeader := common.GetRestResult(h.ctx)  // step [3]
    switch (responseHeader.StatusCode) { // step [4]
    case http.StatusCreated:
        return something, nil
    case http.StatusAccepted:
        return somethingElse, nil
    }
    return nil, errors.New("unexpected response from some other service")
}

// generated code, inside generated client for PostBarr operation of the downstream Barr system

func (s *Client) PostBarr(ctx context.Context, req *PostBarrRequest) (*PostBarrResponse, error) {
    // ...etc...

    result, err := restlib.DoHTTPRequest(...etc...)
    restlib.OnRestResultHTTPResult(ctx, result, err) // step [2]

    // ...etc...
}

my understanding is that it is possible to unit test PostFoo by mocking (s *Client) PostBarr(ctx context.Context, req *PostBarrRequest) (*PostBarrResponse, error) -- the mock of (s *Client) PostBarr(ctx context.Context, req *PostBarrRequest) (*PostBarrResponse, error) can call restlib.OnRestResultHTTPResult(ctx, result, err), but it needs to do it after step [1] but before step [3].

uynap commented 4 years ago

Thanks @anz-rfc . The restlib.SetRestResult makes everything working. Thanks for the help.