func TestMakeNewRelease(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() var d deployCmd info := deployInfo{ ReleaseName: "v2", ParseVersion: "latest", Checksums: deployFileData{ Cloud: map[string]string{"main.js": "4ece160cc8e5e828ee718e7367cf5d37"}, Public: map[string]string{"index.html": "9e2354a0ebac5852bc674026137c8612"}, }, Versions: deployFileData{ Cloud: map[string]string{"main.js": "f2"}, Public: map[string]string{"index.html": "f2"}, }, } ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/deploy") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, &info))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} res, err := d.makeNewRelease(&deployInfo{}, h.Env) ensure.Nil(t, err) ensure.DeepEqual(t, info, res) }
func TestUploadSourceFilesChanged(t *testing.T) { t.Parallel() h := createParseProject(t) defer h.Stop() u := &uploader{ DirName: "cloud", Suffixes: map[string]struct{}{".js": {}}, EndPoint: "uploads", Env: h.Env, PrevChecksums: map[string]string{ "main.js": "d41d8cd98f00b204e9800998ecf8427e", }, PrevVersions: map[string]string{ "main.js": "f1", }, } ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/uploads") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`{"version": "f2"}`)), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} var d deployCmd checksums, versions, err := d.uploadSourceFiles(u) ensure.Nil(t, err) ensure.DeepEqual(t, checksums, map[string]string{"main.js": "4ece160cc8e5e828ee718e7367cf5d37"}) ensure.DeepEqual(t, versions, map[string]string{"main.js": "f2"}) }
func TestGetPrevDeplInfo(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() var d deployCmd info := &deployInfo{ ReleaseName: "v1", ParseVersion: "latest", Checksums: deployFileData{ Cloud: map[string]string{"main.js": "d41d8cd98f00b204e9800998ecf8427e"}, Public: map[string]string{"index.html": "d41d8cd98f00b204e9800998ecf8427e"}, }, Versions: deployFileData{ Cloud: map[string]string{"main.js": "f1"}, Public: map[string]string{"index.html": "f1"}, }, } ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/deploy") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, info))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} res, err := d.getPrevDeplInfo(h.Env) ensure.Nil(t, err) ensure.DeepEqual(t, res, info) }
func TestSetupAndConfigure(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) h.MakeEmptyRoot() defer h.Stop() n := &newCmd{} h.Env.Type = parsecli.ParseFormat h.Env.In = ioutil.NopCloser(strings.NewReader("\n")) code, err := n.setupSample(h.Env, "myapp", &parsecli.ParseAppConfig{ApplicationID: "a"}, true, false) ensure.Nil(t, err) ensure.True(t, code) type jsSDKVersion struct { JS []string `json:"js"` } ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/jsVersions") rows := jsSDKVersion{JS: []string{"1.5.0", "1.6.0"}} return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} err = n.configureSample( &addCmd{MakeDefault: true}, "yolo", (&parsecli.ParseAppConfig{ApplicationID: "a"}).WithInternalMasterKey("m"), nil, h.Env, ) ensure.Nil(t, err) }
func TestLatestVersion(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/supported") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser( jsonpipe.Encode( map[string]string{"version": "2.0.2"}, ), ), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} u := new(updateCmd) latestVersion, err := u.latestVersion(h.Env) ensure.Nil(t, err) ensure.DeepEqual(t, latestVersion, "2.0.2") downloadURL, err := u.getDownloadURL(h.Env) ensure.StringContains(t, downloadURL, "https://github.com/ParsePlatform/parse-cli/releases/download/release_2.0.2") }
func setupForDeploy(t testing.TB, info *deployInfo) *parsecli.Harness { h := createParseProject(t) ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { switch r.URL.Path { case "/1/deploy": return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, info))), }, nil case "/1/scripts", "/1/hosted_files": return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`{"version":"f2"}`)), }, nil case "/1/jsVersions": return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`{"js":["1.0","2.0"]}`)), }, nil default: return &http.Response{ StatusCode: http.StatusExpectationFailed, Body: ioutil.NopCloser(strings.NewReader(`{"error": "something is wrong"}`)), }, nil } }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} return h }
func TestReleasesCmdError(t *testing.T) { h, c := newReleasesCmdHarness(t) defer h.Stop() ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { return nil, stackerr.New("Throws error") }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} ensure.NotNil(t, c.run(h.Env, &parsecli.Context{})) }
func newJsSdkHarnessError(t testing.TB) *parsecli.Harness { h := parsecli.NewHarness(t) ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/jsVersions") return &http.Response{ StatusCode: http.StatusExpectationFailed, Body: ioutil.NopCloser(strings.NewReader(`{"error":"something is wrong"}`)), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} return h }
func newJsSdkHarness(t testing.TB) *parsecli.Harness { h := parsecli.NewHarness(t) ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/jsVersions") rows := jsSDKVersion{JS: []string{"1.2.8", "1.2.9", "1.2.10", "1.2.11", "0.2.0"}} return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} return h }
func TestDeployRetries(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() info := &struct { ReleaseName string `json:"releaseName,omitempty"` Description string `json:"description,omitempty"` ParseVersion string `json:"parseVersion,omitempty"` Checksums map[string]string `json:"checksums,omitempty"` Versions map[string]string `json:"userFiles,omitempty"` }{ ReleaseName: "v1", ParseVersion: "latest", Checksums: map[string]string{"main.js": "d41d8cd98f00b204e9800998ecf8427e"}, Versions: map[string]string{"main.js": "f1"}, } ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/deploy") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, info))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} d := &deployCmd{Retries: 1} ctx := parsecli.Context{Config: defaultParseConfig} ctx.Config.GetProjectConfig().Parse.JSSDK = "latest" ensure.Err(t, d.run(h.Env, &ctx), regexp.MustCompile("no such file or directory")) ensure.DeepEqual(t, h.Err.String(), "") h.Err.Reset() d.Retries = 2 ensure.Err(t, d.run(h.Env, &ctx), regexp.MustCompile("no such file or directory")) ensure.DeepEqual( t, h.Err.String(), "Deploy failed with error:\nlstat cloud: no such file or directory\nWill retry in 0 seconds.\n\n", ) h.Err.Reset() d.Retries = 5 ensure.Err(t, d.run(h.Env, &ctx), regexp.MustCompile("no such file or directory")) errStr := "Deploy failed with error:\nlstat cloud: no such file or directory\nWill retry in 0 seconds.\n\n" errStr += strings.Repeat("Sorry, deploy failed again with same error.\nWill retry in 0 seconds.\n\n", 3) ensure.DeepEqual(t, h.Err.String(), errStr) }
func TestUploadFiles(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() h.MakeEmptyRoot() createRandomFiles(t, h) names := []string{"a", "b"} ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { switch filepath.Base(r.URL.Path) { case names[0]: ensure.NotNil(t, r.Header) ensure.DeepEqual(t, r.Header.Get("Key"), "Value") ensure.DeepEqual(t, r.Header.Get("Content-Type"), "application/octet-stream") ensure.DeepEqual(t, r.Header.Get("Content-MD5"), "4JnleFGzGppuArF6N50EWg==") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`{"status":"success"}`)), }, nil case names[1]: ensure.NotNil(t, r.Header) ensure.DeepEqual(t, r.Header.Get("Key"), "Value") ensure.DeepEqual(t, r.Header.Get("Content-Type"), "application/octet-stream") ensure.DeepEqual(t, r.Header.Get("Content-MD5"), "Fv43qsp6mnGCJlC00VkOcA==") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`{"status":"success"}`)), }, nil default: return &http.Response{ StatusCode: http.StatusInternalServerError, Body: ioutil.NopCloser(strings.NewReader(`{"error":"something is wrong"}`)), }, nil } }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} var filenames []string for _, name := range names { filenames = append(filenames, filepath.Join(h.Env.Root, name)) } ensure.Nil(t, uploadSymbolFiles(filenames[:], map[string]string{"Key": "Value"}, true, h.Env)) for _, filename := range filenames { _, err := os.Lstat(filename) ensure.NotNil(t, err) ensure.True(t, os.IsNotExist(err)) } }
func TestGetPrevDeplInfoError(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() var d deployCmd ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/deploy") return &http.Response{ StatusCode: http.StatusExpectationFailed, Body: ioutil.NopCloser(strings.NewReader(`{"error": "something is wrong"}`)), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} _, err := d.getPrevDeplInfo(h.Env) ensure.Err(t, err, regexp.MustCompile("something is wrong")) }
func TestRollbackError(t *testing.T) { t.Parallel() var r rollbackCmd h := parsecli.NewHarness(t) defer h.Stop() var res struct{ Error string } res.Error = "something is wrong" ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/deploy") return &http.Response{ StatusCode: http.StatusExpectationFailed, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, &res))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} ensure.Err(t, r.run(h.Env, nil), regexp.MustCompile("something is wrong")) }
func TestLogWithoutFollow(t *testing.T) { t.Parallel() l := logsCmd{level: "INFO"} h := parsecli.NewHarness(t) defer h.Stop() ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/scriptlog") rows := []logResponse{{Message: "foo bar"}, {Message: "baz"}} return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} err := l.run(h.Env, &parsecli.Context{}) ensure.Nil(t, err) ensure.DeepEqual(t, h.Out.String(), "baz\nfoo bar\n") }
func newRollbackCmdHarness(t testing.TB) *parsecli.Harness { h := parsecli.NewHarness(t) ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/deploy") var req, res rollbackInfo ensure.Nil(t, json.NewDecoder(r.Body).Decode(&req)) if req.ReleaseName == "" { res.ReleaseName = "v0" } else { res.ReleaseName = req.ReleaseName } return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, &res))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} return h }
func TestUploadFileHttpError(t *testing.T) { t.Parallel() h := createParseProject(t) defer h.Stop() var d deployCmd ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/uploads") return &http.Response{ StatusCode: http.StatusExpectationFailed, Body: ioutil.NopCloser(strings.NewReader(`{"error": "something is wrong"}`)), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} dirRoot := filepath.Join(h.Env.Root, "cloud") _, err := d.uploadFile(filepath.Join(dirRoot, "main.js"), "uploads", h.Env, func(name string) string { return "main.js" }) ensure.Err(t, err, regexp.MustCompile("something is wrong")) }
func newDownloadHarness(t testing.TB) (*parsecli.Harness, *downloadCmd) { h := createParseProject(t) ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.FormValue("version"), "version") switch { case scriptPath.MatchString(r.URL.Path): return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`"content"`)), }, nil case hostedPath.MatchString(r.URL.Path): return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(`[1, 1, 2, 3, 5, 8, 13]`)), }, nil default: return &http.Response{ StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(strings.NewReader(`{"error": "something is wrong"}`)), }, nil } }) h.MakeEmptyRoot() h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} d := &downloadCmd{ release: &deployInfo{ Versions: deployFileData{ Cloud: map[string]string{"main.js": "version"}, Public: map[string]string{"index.html": "version"}, }, Checksums: deployFileData{ Cloud: map[string]string{"main.js": "9a0364b9e99bb480dd25e1f0284c8555"}, Public: map[string]string{"index.html": "ea46dea1ca5f0b7a728aa3c2a87ae8a1"}, }, }, } return h, d }
func TestIsSupportedError(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/supported") return &http.Response{ StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser( jsonpipe.Encode( map[string]string{"error": "not supported"}, ), ), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} _, err := checkIfSupported(h.Env, "2.0.2", "parse") ensure.Err(t, err, regexp.MustCompile("not supported")) }
func TestIsSupportedWarning(t *testing.T) { t.Parallel() h := parsecli.NewHarness(t) defer h.Stop() ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/supported") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser( jsonpipe.Encode( map[string]string{"warning": "please update"}, ), ), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} message, err := checkIfSupported(h.Env, "2.0.2", "parse") ensure.Nil(t, err) ensure.DeepEqual(t, message, "please update") }
func newAddCmdHarness(t testing.TB) (*parsecli.Harness, []*parsecli.App) { h := parsecli.NewHarness(t) defer h.Stop() apps := []*parsecli.App{ { Name: "A", ApplicationID: "appId.A", MasterKey: "masterKey.A", }, { Name: "B", ApplicationID: "appId.B", MasterKey: "masterKey.B", }, } res := map[string][]*parsecli.App{"results": apps} ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/apps") email := r.Header.Get("X-Parse-Email") password := r.Header.Get("X-Parse-Password") token := r.Header.Get("X-Parse-Account-Key") if !((email == "email" && password == "password") || (token == "token")) { return &http.Response{ StatusCode: http.StatusUnauthorized, Body: ioutil.NopCloser(strings.NewReader(`{"error": "incorrect credentials"}`)), }, nil } return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, &res))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} return h, apps }
func TestReleasesCmd(t *testing.T) { h, r := newReleasesCmdHarness(t) defer h.Stop() rows := []releasesResponse{ {Version: "v1", Description: "version 1", Timestamp: "time 1"}, {Version: "v2", Description: "version 2", Timestamp: "time 2"}, } ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { ensure.DeepEqual(t, r.URL.Path, "/1/releases") return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} ensure.Nil(t, r.run(h.Env, &parsecli.Context{})) expected := `Name Description Date v1 version 1 time 1 v2 version 2 time 2 ` ensure.DeepEqual(t, h.Out.String(), expected) }
func newFunctionsHarness(t testing.TB) *parsecli.Harness { h := parsecli.NewHarness(t) defer h.Stop() ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { var body interface{} var params map[string]interface{} if r.Body != nil { err := json.NewDecoder(r.Body).Decode(¶ms) if err != nil { return &http.Response{StatusCode: http.StatusInternalServerError}, err } } switch r.Method { case "GET": if r.URL.Path == "/1/hooks/functions/foo" { body = map[string]interface{}{ "results": []map[string]interface{}{ {"functionName": "foo"}, {"functionName": "foo", "url": "https://api.example.com/foo"}, }, } } else if r.URL.Path == defaultFunctionsURL { body = map[string]interface{}{ "results": []map[string]interface{}{ {"functionName": "foo"}, {"functionName": "foo", "url": "https://api.example.com/foo"}, {"functionName": "bar", "url": "https://api.example.com/bar"}, }, } } else { return &http.Response{StatusCode: http.StatusBadRequest}, errors.New("no such function hook is defined") } case "POST": ensure.DeepEqual(t, r.URL.Path, defaultFunctionsURL) switch params["functionName"] { case "foo": body = map[string]interface{}{ "functionName": "foo", "url": "https://api.example.com/foo", "warning": "function foo already exists", } case "bar": body = map[string]interface{}{ "functionName": "bar", "url": "https://api.example.com/bar", } default: return &http.Response{StatusCode: http.StatusInternalServerError}, errors.New("invalid function name") } case "PUT": if params["__op"] == "Delete" && strings.HasPrefix(r.URL.Path, "/foo") { ensure.DeepEqual(t, r.URL.Path, "/1/hooks/functions/foo") body = map[string]interface{}{"functionName": "foo"} } else { switch strings.Replace(r.URL.Path, "/1/hooks/functions/", "", 1) { case "foo": ensure.DeepEqual(t, r.URL.Path, "/1/hooks/functions/foo") body = map[string]interface{}{ "functionName": "foo", "url": "https://api.example.com/_foo", "warning": "function foo already exists", } case "bar": ensure.DeepEqual(t, r.URL.Path, "/1/hooks/functions/bar") body = map[string]interface{}{ "functionName": "bar", "url": "https://api.example.com/_bar", } default: return &http.Response{StatusCode: http.StatusInternalServerError}, errors.New("invalid function name") } } } return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(jsonpipe.Encode(body)), }, nil }) h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} return h }
func TestLogWithFollow(t *testing.T) { t.Parallel() l := logsCmd{level: "INFO", follow: true} h := parsecli.NewHarness(t) defer h.Stop() var round int64 round1Time := parseTime{ISO: "iso1", Type: "type1"} round1TimeStr := jsonStr(t, round1Time) round3Time := parseTime{ISO: "iso2", Type: "type2"} round3TimeStr := jsonStr(t, round3Time) ht := parsecli.TransportFunc(func(r *http.Request) (*http.Response, error) { switch atomic.AddInt64(&round, 1) { case 1: // expect no timestamp, return some data ensure.DeepEqual(t, r.FormValue("startTime"), "") rows := []logResponse{{Message: "foo bar", Timestamp: round1Time}} return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil case 2: // expect the timestamp from case 1, return no new data ensure.DeepEqual(t, r.FormValue("startTime"), round1TimeStr) rows := []logResponse{} return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil case 3: // expect the timestamp from case 1, return some new data ensure.DeepEqual(t, r.FormValue("startTime"), round1TimeStr) rows := []logResponse{{Message: "baz", Timestamp: round3Time}} return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(strings.NewReader(jsonStr(t, rows))), }, nil case 4: // expect the timestamp from case 3, return error ensure.DeepEqual(t, r.FormValue("startTime"), round3TimeStr) return &http.Response{ StatusCode: http.StatusInternalServerError, Status: http.StatusText(http.StatusInternalServerError), Body: ioutil.NopCloser(strings.NewReader("a")), }, nil } panic("unexpected request") }) stop := make(chan struct{}) go func() { for { select { case <-stop: return default: h.Clock.Add(logFollowSleepDuration) } } }() h.Env.ParseAPIClient = &parsecli.ParseAPIClient{APIClient: &parse.Client{Transport: ht}} err := l.run(h.Env, &parsecli.Context{}) close(stop) ensure.Err(t, err, regexp.MustCompile(`parse: error with status=500 and body="a"`)) ensure.DeepEqual(t, h.Out.String(), "foo bar\nbaz\n") ensure.DeepEqual(t, h.Err.String(), "") ensure.DeepEqual(t, atomic.LoadInt64(&round), int64(4)) }