diff options
Diffstat (limited to 'src/mongo/gotools/vendor/src/github.com/smartystreets/goconvey/web/server/api/server_test.go')
-rw-r--r-- | src/mongo/gotools/vendor/src/github.com/smartystreets/goconvey/web/server/api/server_test.go | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/src/mongo/gotools/vendor/src/github.com/smartystreets/goconvey/web/server/api/server_test.go b/src/mongo/gotools/vendor/src/github.com/smartystreets/goconvey/web/server/api/server_test.go new file mode 100644 index 00000000000..bd48e308015 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/smartystreets/goconvey/web/server/api/server_test.go @@ -0,0 +1,462 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" + "github.com/smartystreets/goconvey/web/server/contract" + "github.com/smartystreets/goconvey/web/server/messaging" +) + +const initialRoot = "/root/gopath/src/github.com/smartystreets/project" +const nonexistentRoot = "I don't exist" +const unreadableContent = "!!error!!" + +func TestHTTPServer(t *testing.T) { + // TODO: fix the skipped tests... + + Convey("Subject: HttpServer responds to requests appropriately", t, func() { + fixture := newServerFixture() + + Convey("Before any update is recieved", func() { + Convey("When the update is requested", func() { + update, _ := fixture.RequestLatest() + + Convey("No panic should occur", func() { + So(func() { fixture.RequestLatest() }, ShouldNotPanic) + }) + + Convey("The update will be empty", func() { + So(update, ShouldResemble, new(contract.CompleteOutput)) + }) + }) + }) + + Convey("Given an update is received", func() { + fixture.ReceiveUpdate("", &contract.CompleteOutput{Revision: "asdf"}) + + Convey("When the update is requested", func() { + update, response := fixture.RequestLatest() + + Convey("The server returns it", func() { + So(update, ShouldResemble, &contract.CompleteOutput{Revision: "asdf"}) + }) + + Convey("The server returns 200", func() { + So(response.Code, ShouldEqual, http.StatusOK) + }) + + Convey("The server should include important cache-related headers", func() { + So(len(response.HeaderMap), ShouldEqual, 4) + So(response.HeaderMap["Content-Type"][0], ShouldEqual, "application/json") + So(response.HeaderMap["Cache-Control"][0], ShouldEqual, "no-cache, no-store, must-revalidate") + So(response.HeaderMap["Pragma"][0], ShouldEqual, "no-cache") + So(response.HeaderMap["Expires"][0], ShouldEqual, "0") + }) + }) + }) + + Convey("When the root watch is queried", func() { + root, status := fixture.QueryRootWatch(false) + + SkipConvey("The server returns it", func() { + So(root, ShouldEqual, initialRoot) + }) + + Convey("The server returns HTTP 200 - OK", func() { + So(status, ShouldEqual, http.StatusOK) + }) + }) + + SkipConvey("When the root watch is adjusted", func() { + + Convey("But the request has no root parameter", func() { + status, body := fixture.AdjustRootWatchMalformed() + + Convey("The server returns HTTP 400 - Bad Input", func() { + So(status, ShouldEqual, http.StatusBadRequest) + }) + + Convey("The body should contain a helpful error message", func() { + So(body, ShouldEqual, "No 'root' query string parameter included!") + }) + + Convey("The server should not change the existing root", func() { + root, _ := fixture.QueryRootWatch(false) + So(root, ShouldEqual, initialRoot) + }) + }) + + Convey("But the root parameter is empty", func() { + status, body := fixture.AdjustRootWatch("") + + Convey("The server returns HTTP 400 - Bad Input", func() { + So(status, ShouldEqual, http.StatusBadRequest) + }) + + Convey("The server should provide a helpful error message", func() { + So(body, ShouldEqual, "You must provide a non-blank path.") + }) + + Convey("The server should not change the existing root", func() { + root, _ := fixture.QueryRootWatch(false) + So(root, ShouldEqual, initialRoot) + }) + }) + + Convey("And the new root exists", func() { + status, body := fixture.AdjustRootWatch(initialRoot + "/package") + + Convey("The server returns HTTP 200 - OK", func() { + So(status, ShouldEqual, http.StatusOK) + }) + + Convey("The body should NOT contain any error message or content", func() { + So(body, ShouldEqual, "") + }) + + Convey("The server informs the watcher of the new root", func() { + root, _ := fixture.QueryRootWatch(false) + So(root, ShouldEqual, initialRoot+"/package") + }) + }) + + Convey("And the new root does NOT exist", func() { + status, body := fixture.AdjustRootWatch(nonexistentRoot) + + Convey("The server returns HTTP 404 - Not Found", func() { + So(status, ShouldEqual, http.StatusNotFound) + }) + + Convey("The body should contain a helpful error message", func() { + So(body, ShouldEqual, fmt.Sprintf("Directory does not exist: '%s'", nonexistentRoot)) + }) + + Convey("The server should not change the existing root", func() { + root, _ := fixture.QueryRootWatch(false) + So(root, ShouldEqual, initialRoot) + }) + }) + }) + + SkipConvey("When a packge is ignored", func() { + + Convey("But the request has no path parameter", func() { + status, body := fixture.IgnoreMalformed() + + Convey("The server returns HTTP 400 - Bad Input", func() { + So(status, ShouldEqual, http.StatusBadRequest) + }) + + Convey("The body should contain a helpful error message", func() { + So(body, ShouldEqual, "No 'paths' query string parameter included!") + }) + + SkipConvey("The server should not ignore anything", func() { + // So(fixture.watcher.ignored, ShouldEqual, "") + }) + }) + + Convey("But the request is blank", func() { + status, body := fixture.Ignore("") + + Convey("The server returns HTTP 400 - Bad Input", func() { + So(status, ShouldEqual, http.StatusBadRequest) + }) + + Convey("The body should contain a helpful error message", func() { + So(body, ShouldEqual, "You must provide a non-blank path.") + }) + }) + + Convey("And the request is well formed", func() { + status, _ := fixture.Ignore(initialRoot) + + SkipConvey("The server informs the watcher", func() { + // So(fixture.watcher.ignored, ShouldEqual, initialRoot) + }) + Convey("The server returns HTTP 200 - OK", func() { + So(status, ShouldEqual, http.StatusOK) + }) + }) + }) + + SkipConvey("When a package is reinstated", func() { + Convey("But the request has no path parameter", func() { + status, body := fixture.ReinstateMalformed() + + Convey("The server returns HTTP 400 - Bad Input", func() { + So(status, ShouldEqual, http.StatusBadRequest) + }) + + Convey("The body should contain a helpful error message", func() { + So(body, ShouldEqual, "No 'paths' query string parameter included!") + }) + + SkipConvey("The server should not ignore anything", func() { + // So(fixture.watcher.reinstated, ShouldEqual, "") + }) + }) + + Convey("But the request is blank", func() { + status, body := fixture.Reinstate("") + + Convey("The server returns HTTP 400 - Bad Input", func() { + So(status, ShouldEqual, http.StatusBadRequest) + }) + + Convey("The body should contain a helpful error message", func() { + So(body, ShouldEqual, "You must provide a non-blank path.") + }) + }) + + Convey("And the request is well formed", func() { + status, _ := fixture.Reinstate(initialRoot) + + SkipConvey("The server informs the watcher", func() { + // So(fixture.watcher.reinstated, ShouldEqual, initialRoot) + }) + Convey("The server returns HTTP 200 - OK", func() { + So(status, ShouldEqual, http.StatusOK) + }) + }) + }) + + Convey("When the status of the executor is requested", func() { + fixture.executor.status = "blah blah blah" + statusCode, statusBody := fixture.RequestExecutorStatus() + + Convey("The server asks the executor its status and returns it", func() { + So(statusBody, ShouldEqual, "blah blah blah") + }) + + Convey("The server returns HTTP 200 - OK", func() { + So(statusCode, ShouldEqual, http.StatusOK) + }) + }) + + Convey("When a manual execution of the test packages is requested", func() { + status := fixture.ManualExecution() + update, _ := fixture.RequestLatest() + + SkipConvey("The server invokes the executor using the watcher's listing and save the result", func() { + So(update, ShouldResemble, &contract.CompleteOutput{Revision: initialRoot}) + }) + + Convey("The server returns HTTP 200 - OK", func() { + So(status, ShouldEqual, http.StatusOK) + }) + }) + + SkipConvey("When the pause setting is toggled via the server", func() { + paused := fixture.TogglePause() + + SkipConvey("The pause channel buffer should have a true value", func() { + // var value bool + // select { + // case value = <-fixture.pauseUpdate: + // default: + // } + // So(value, ShouldBeTrue) + }) + + Convey("The latest results should show that the server is paused", func() { + fixture.ReceiveUpdate("", &contract.CompleteOutput{Revision: "asdf"}) + update, _ := fixture.RequestLatest() + + So(update.Paused, ShouldBeTrue) + }) + + Convey("The toggle handler should return its new status", func() { + So(paused, ShouldEqual, "true") + }) + }) + }) +} + +/********* Server Fixture *********/ + +type ServerFixture struct { + server *HTTPServer + watcher chan messaging.WatcherCommand + executor *FakeExecutor + statusUpdate chan bool +} + +func (self *ServerFixture) ReceiveUpdate(root string, update *contract.CompleteOutput) { + self.server.ReceiveUpdate(root, update) +} + +func (self *ServerFixture) RequestLatest() (*contract.CompleteOutput, *httptest.ResponseRecorder) { + request, _ := http.NewRequest("GET", "http://localhost:8080/results", nil) + response := httptest.NewRecorder() + + self.server.Results(response, request) + + decoder := json.NewDecoder(strings.NewReader(response.Body.String())) + update := new(contract.CompleteOutput) + decoder.Decode(update) + return update, response +} + +func (self *ServerFixture) QueryRootWatch(newclient bool) (string, int) { + url := "http://localhost:8080/watch" + if newclient { + url += "?newclient=1" + } + request, _ := http.NewRequest("GET", url, nil) + response := httptest.NewRecorder() + + self.server.Watch(response, request) + + return strings.TrimSpace(response.Body.String()), response.Code +} + +func (self *ServerFixture) AdjustRootWatchMalformed() (status int, body string) { + request, _ := http.NewRequest("POST", "http://localhost:8080/watch", nil) + response := httptest.NewRecorder() + + self.server.Watch(response, request) + + status, body = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) AdjustRootWatch(newRoot string) (status int, body string) { + escapedRoot := url.QueryEscape(newRoot) + request, _ := http.NewRequest("POST", "http://localhost:8080/watch?root="+escapedRoot, nil) + response := httptest.NewRecorder() + + self.server.Watch(response, request) + + status, body = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) IgnoreMalformed() (status int, body string) { + request, _ := http.NewRequest("POST", "http://localhost:8080/ignore", nil) + response := httptest.NewRecorder() + + self.server.Ignore(response, request) + + status, body = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) Ignore(folder string) (status int, body string) { + escapedFolder := url.QueryEscape(folder) + request, _ := http.NewRequest("POST", "http://localhost:8080/ignore?paths="+escapedFolder, nil) + response := httptest.NewRecorder() + + self.server.Ignore(response, request) + + status, body = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) ReinstateMalformed() (status int, body string) { + request, _ := http.NewRequest("POST", "http://localhost:8080/reinstate", nil) + response := httptest.NewRecorder() + + self.server.Reinstate(response, request) + + status, body = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) Reinstate(folder string) (status int, body string) { + escapedFolder := url.QueryEscape(folder) + request, _ := http.NewRequest("POST", "http://localhost:8080/reinstate?paths="+escapedFolder, nil) + response := httptest.NewRecorder() + + self.server.Reinstate(response, request) + + status, body = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) SetExecutorStatus(status string) { + // self.executor.status = status + // select { + // case self.executor.statusUpdate <- make(chan string): + // default: + // } +} + +func (self *ServerFixture) RequestExecutorStatus() (code int, status string) { + request, _ := http.NewRequest("GET", "http://localhost:8080/status", nil) + response := httptest.NewRecorder() + + self.server.Status(response, request) + + code, status = response.Code, strings.TrimSpace(response.Body.String()) + return +} + +func (self *ServerFixture) ManualExecution() int { + request, _ := http.NewRequest("POST", "http://localhost:8080/execute", nil) + response := httptest.NewRecorder() + + self.server.Execute(response, request) + nap, _ := time.ParseDuration("100ms") + time.Sleep(nap) + return response.Code +} + +func (self *ServerFixture) TogglePause() string { + request, _ := http.NewRequest("POST", "http://localhost:8080/pause", nil) + response := httptest.NewRecorder() + + self.server.TogglePause(response, request) + + return response.Body.String() +} + +func newServerFixture() *ServerFixture { + self := new(ServerFixture) + self.watcher = make(chan messaging.WatcherCommand) + // self.watcher.SetRootWatch(initialRoot) + statusUpdate := make(chan chan string) + self.executor = newFakeExecutor("", statusUpdate) + self.server = NewHTTPServer("initial-working-dir", self.watcher, self.executor, statusUpdate) + return self +} + +/********* Fake Executor *********/ + +type FakeExecutor struct { + status string + executed bool + statusFlag bool + statusUpdate chan chan string +} + +func (self *FakeExecutor) Status() string { + return self.status +} + +func (self *FakeExecutor) ClearStatusFlag() bool { + hasNewStatus := self.statusFlag + self.statusFlag = false + return hasNewStatus +} + +func (self *FakeExecutor) ExecuteTests(watched []*contract.Package) *contract.CompleteOutput { + output := new(contract.CompleteOutput) + output.Revision = watched[0].Path + return output +} + +func newFakeExecutor(status string, statusUpdate chan chan string) *FakeExecutor { + self := new(FakeExecutor) + self.status = status + self.statusUpdate = statusUpdate + return self +} |