Skip to content

Commit

Permalink
Add mocks and unit tests to http and msgraph packages (#2)
Browse files Browse the repository at this point in the history
* command  + http framework

* Got a 404 on the hardcoded Mattermost Org - no Outlook

* `/msoffice viewcal` works on a personal account

- added OAuth2 credentials a config values

* PR feedback

* Add mocks and unit tests to http and msgraph packages

Added a mocks package that mocks out the functionality of some the
interfaces needed by the oauth handlers. This allows testing of each
error check within the oauth handler functions. I also added
github.com/jarcoal/httpmock as a dependecy to test both the oauth code
to token exchange and each of the calls that will be made out to the
microsoft graph API.

The tests cover how all errors are encountered for the oauth connection.
They also ensure that access token refreshing happens when the token is
expired. This tests to make sure the golang oauth client works out of
the box for this plugin.

* Update http.go and plugin.go with upstream/dev branch

* Address PR review comments

This includes moving all of the tests to their own subpackages to allow
for a cleaner workspace. Move all the mock interfaces to the test_http
package as they are only needed there currently. Update mock interfaces
to use github.com/stretchr/testify/mock to conform to Mattermost mock
standards. Also addresses any naming convention comments in this commit
as well.

* Migrate mocks to use gomock

Per @cpoile we are migrating the mocked interfaces from
stretchr/testify/mock to instead be generated and used via gomock. I
have added a new makefile target to generate the mock interfaces using
mockgen. In addition I have generated those mocks to be used in the unit
tests in the test_http package. All of those unit tests have been
updated to use the generated mocks as well.

* Use getUserRequest for oauth connect tests

* Refactor how mocks are built for tests

* Refactor test case mocks, fix minor nitpicks

Refactored the mocks to be a part of the testcase struct and then pass
those to a `setupMocks` function which allow for custom mock behavior.
This sigificantly reduces the code for setup and allows for more
customizable mocks. Also addressing some of @levb comments on package
names for the test folders and naming convention.

* Create mocks in test run function and pass to tc struct after setup

* Move redundant test case fields to testing block
  • Loading branch information
cpurta authored and levb committed Nov 21, 2019
1 parent df0d1fd commit 5bd5bd2
Show file tree
Hide file tree
Showing 16 changed files with 927 additions and 9 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ golint:
$(GOPATH)/bin/golint -set_exit_status ./...
@echo lint success

## Generates mock golang interfaces for testing
mock:
ifneq ($(HAS_SERVER),)
go install github.com/golang/mock/mockgen
mockgen -destination server/user/mock_user/mock_oauth2_store.go github.com/mattermost/mattermost-plugin-msoffice/server/user OAuth2StateStore
mockgen -destination server/utils/mock_utils/mock_bot_poster.go github.com/mattermost/mattermost-plugin-msoffice/server/utils BotPoster
mockgen -destination server/kvstore/mock_kvstore/mock_kvstore.go github.com/mattermost/mattermost-plugin-msoffice/server/kvstore KVStore
endif

## Builds the server, if it exists, including support for multiple architectures.
.PHONY: server
server:
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module github.com/mattermost/mattermost-plugin-msoffice
go 1.13

require (
github.com/golang/mock v1.2.0
github.com/gorilla/mux v1.7.3
github.com/jarcoal/httpmock v1.0.4
github.com/jkrecek/msgraph-go v0.0.0-20190328140430-9f7466d8cb1f
github.com/mattermost/mattermost-server v0.0.0-20190927121038-340287890a78
github.com/pkg/errors v0.8.1
Expand Down
33 changes: 33 additions & 0 deletions go.sum

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions server/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Handler struct {
Config *config.Config
UserStore user.Store
API plugin.API
OAuth2StateStore user.OAuth2StateStore
BotPoster utils.BotPoster
IsAuthorizedAdmin func(userId string) (bool, error)
root *mux.Router
Expand All @@ -35,8 +36,8 @@ func (h *Handler) InitRouter() {

oauth2 := h.root.PathPrefix("/oauth2").Subrouter()
oauth2.Use(authorizationRequired)
oauth2.HandleFunc("/connect", h.oauth2Connect).Methods("GET")
oauth2.HandleFunc("/complete", h.oauth2Complete).Methods("GET")
oauth2.HandleFunc("/connect", h.OAuth2Connect).Methods("GET")
oauth2.HandleFunc("/complete", h.OAuth2Complete).Methods("GET")

h.root.Handle("{anything:.*}", http.NotFoundHandler())
return
Expand Down
5 changes: 2 additions & 3 deletions server/http/oauth2_complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/mattermost/mattermost-plugin-msoffice/server/user"
)

func (h *Handler) oauth2Complete(w http.ResponseWriter, r *http.Request) {
func (h *Handler) OAuth2Complete(w http.ResponseWriter, r *http.Request) {
authedUserID := r.Header.Get("Mattermost-User-ID")
if authedUserID == "" {
http.Error(w, "Not authorized", http.StatusUnauthorized)
Expand All @@ -31,8 +31,7 @@ func (h *Handler) oauth2Complete(w http.ResponseWriter, r *http.Request) {
}

state := r.URL.Query().Get("state")
stateStore := user.NewOAuth2StateStore(h.API)
err := stateStore.Verify(state)
err := h.OAuth2StateStore.Verify(state)
if err != nil {
http.Error(w, "missing stored state: "+err.Error(), http.StatusBadRequest)
return
Expand Down
6 changes: 2 additions & 4 deletions server/http/oauth2_connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ import (
"github.com/mattermost/mattermost-server/model"

"github.com/mattermost/mattermost-plugin-msoffice/server/msgraph"
"github.com/mattermost/mattermost-plugin-msoffice/server/user"
)

func (h *Handler) oauth2Connect(w http.ResponseWriter, r *http.Request) {
func (h *Handler) OAuth2Connect(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get("Mattermost-User-ID")
if userID == "" {
http.Error(w, "Not authorized", http.StatusUnauthorized)
Expand All @@ -24,8 +23,7 @@ func (h *Handler) oauth2Connect(w http.ResponseWriter, r *http.Request) {

conf := msgraph.GetOAuth2Config(h.Config)
state := fmt.Sprintf("%v_%v", model.NewId()[0:15], userID)
stateStore := user.NewOAuth2StateStore(h.API)
err := stateStore.Store(state)
err := h.OAuth2StateStore.Store(state)
if err != nil {
h.jsonError(w, err)
return
Expand Down
41 changes: 41 additions & 0 deletions server/http/testhttp/mock_response_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package testhttp

import "net/http"

var (
_ http.ResponseWriter = &mockResponseWriter{}
)

func defaultMockResponseWriter() *mockResponseWriter {
return &mockResponseWriter{
HeaderMap: make(http.Header),
Bytes: make([]byte, 0),
Err: nil,
StatusCode: http.StatusOK,
}
}

type mockResponseWriter struct {
HeaderMap http.Header
Bytes []byte
Err error
StatusCode int
}

func (rw *mockResponseWriter) Header() http.Header {
return rw.HeaderMap
}

func (rw *mockResponseWriter) Write(bytes []byte) (int, error) {
if rw.Err != nil {
return 0, rw.Err
}

rw.Bytes = append(rw.Bytes, bytes...)

return len(rw.Bytes), nil
}

func (rw *mockResponseWriter) WriteHeader(statusCode int) {
rw.StatusCode = statusCode
}
Loading

0 comments on commit 5bd5bd2

Please sign in to comment.