diff --git a/.github/workflows/e2e_test_check.yml b/.github/workflows/e2e_test_check.yml index 4739346..fe0ea5d 100644 --- a/.github/workflows/e2e_test_check.yml +++ b/.github/workflows/e2e_test_check.yml @@ -41,7 +41,7 @@ jobs: needs: job_1 strategy: matrix: - go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x] + go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x] runs-on: ubuntu-latest steps: - name: Install Go diff --git a/.github/workflows/style_check.yml b/.github/workflows/style_check.yml index ebc98ba..568d0d4 100644 --- a/.github/workflows/style_check.yml +++ b/.github/workflows/style_check.yml @@ -13,7 +13,7 @@ jobs: name: vet and gofmt strategy: matrix: - go-version: [1.13.x, 1.14.x] + go-version: [1.13.x, 1.14.x, 1.15.x] runs-on: ubuntu-latest steps: - name: Install Go diff --git a/.github/workflows/ut_check.yml b/.github/workflows/ut_check.yml index 0b9d311..417d0d2 100644 --- a/.github/workflows/ut_check.yml +++ b/.github/workflows/ut_check.yml @@ -13,7 +13,7 @@ jobs: name: go test strategy: matrix: - go-version: [1.13.x, 1.14.x] + go-version: [1.13.x, 1.14.x, 1.15.x] runs-on: ubuntu-latest steps: - name: Install Go diff --git a/cmd/clear.go b/cmd/clear.go index 65cfa6e..2e5aea2 100644 --- a/cmd/clear.go +++ b/cmd/clear.go @@ -18,9 +18,10 @@ package cmd import ( "fmt" - log "github.com/sirupsen/logrus" "os" + log "github.com/sirupsen/logrus" + "github.com/qiniu/goc/pkg/cover" "github.com/spf13/cobra" ) @@ -37,7 +38,11 @@ goc clear goc clear --center=http://192.168.1.1:8080 `, Run: func(cmd *cobra.Command, args []string) { - res, err := cover.NewWorker(center).Clear() + p := cover.ProfileParam{ + Service: svrList, + Address: addrList, + } + res, err := cover.NewWorker(center).Clear(p) if err != nil { log.Fatalf("call host %v failed, err: %v, response: %v", center, err, string(res)) } @@ -47,5 +52,7 @@ goc clear --center=http://192.168.1.1:8080 func init() { addBasicFlags(clearCmd.Flags()) + clearCmd.Flags().StringSliceVarP(&svrList, "service", "", nil, "service name to clear profile, see 'goc list' for all services.") + clearCmd.Flags().StringSliceVarP(&addrList, "address", "", nil, "address to clear profile, see 'goc list' for all addresses.") rootCmd.AddCommand(clearCmd) } diff --git a/pkg/cover/client.go b/pkg/cover/client.go index 04123e0..c45166f 100644 --- a/pkg/cover/client.go +++ b/pkg/cover/client.go @@ -33,7 +33,7 @@ import ( // Action provides methods to contact with the covered service under test type Action interface { Profile(param ProfileParam) ([]byte, error) - Clear() ([]byte, error) + Clear(param ProfileParam) ([]byte, error) InitSystem() ([]byte, error) ListServices() ([]byte, error) RegisterService(svr Service) ([]byte, error) @@ -97,10 +97,9 @@ func (c *client) Profile(param ProfileParam) ([]byte, error) { return nil, fmt.Errorf("use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately") } - body, err := json.Marshal(param) - if err != nil { - return nil, fmt.Errorf("json.Marshal failed, param: %v, err:%v", param, err) - } + // the json.Marshal function can return two types of errors: UnsupportedTypeError or UnsupportedValueError + // so no need to check here + body, _ := json.Marshal(param) res, profile, err := c.do("POST", u, "application/json", bytes.NewReader(body)) if err != nil && isNetworkError(err) { @@ -113,11 +112,18 @@ func (c *client) Profile(param ProfileParam) ([]byte, error) { return profile, err } -func (c *client) Clear() ([]byte, error) { +func (c *client) Clear(param ProfileParam) ([]byte, error) { u := fmt.Sprintf("%s%s", c.Host, CoverProfileClearAPI) - _, resp, err := c.do("POST", u, "", nil) + if len(param.Service) != 0 && len(param.Address) != 0 { + return nil, fmt.Errorf("use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately") + } + + // the json.Marshal function can return two types of errors: UnsupportedTypeError or UnsupportedValueError + // so no need to check here + body, _ := json.Marshal(param) + _, resp, err := c.do("POST", u, "application/json", bytes.NewReader(body)) if err != nil && isNetworkError(err) { - _, resp, err = c.do("POST", u, "", nil) + _, resp, err = c.do("POST", u, "application/json", bytes.NewReader(body)) } return resp, err } diff --git a/pkg/cover/client_test.go b/pkg/cover/client_test.go index 1663599..e5cc340 100644 --- a/pkg/cover/client_test.go +++ b/pkg/cover/client_test.go @@ -189,3 +189,16 @@ func TestClientDo(t *testing.T) { _, _, err := c.do(" ", "http://127.0.0.1:7777", "", nil) // a invalid method assert.Contains(t, err.Error(), "invalid method") } + +func TestClientClearWithInvalidParam(t *testing.T) { + p := ProfileParam{ + Service: []string{"goc"}, + Address: []string{"http://127.0.0.1:777"}, + } + c := &client{ + client: http.DefaultClient, + } + _, err := c.Clear(p) + assert.Error(t, err) + assert.Contains(t, err.Error(), "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately") +} diff --git a/pkg/cover/server.go b/pkg/cover/server.go index 6746ae9..93a50c1 100644 --- a/pkg/cover/server.go +++ b/pkg/cover/server.go @@ -217,17 +217,26 @@ func filterProfile(coverFile []string, profiles []*cover.Profile) ([]*cover.Prof } func clear(c *gin.Context) { - svrsUnderTest := DefaultStore.GetAll() - for svc, addrs := range svrsUnderTest { - for _, addr := range addrs { - pp, err := NewWorker(addr).Clear() - if err != nil { - c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) - return - } - fmt.Fprintf(c.Writer, "Register service %s: %s coverage counter %s", svc, addr, string(pp)) - } + var body ProfileParam + if err := c.ShouldBind(&body); err != nil { + c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) + return } + svrsUnderTest := DefaultStore.GetAll() + filterAddrList, err := filterAddrs(body.Service, body.Address, true, svrsUnderTest) + if err != nil { + c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) + return + } + for _, addr := range filterAddrList { + pp, err := NewWorker(addr).Clear(ProfileParam{}) + if err != nil { + c.JSON(http.StatusExpectationFailed, gin.H{"error": err.Error()}) + return + } + fmt.Fprintf(c.Writer, "Register service %s coverage counter %s", addr, string(pp)) + } + } func initSystem(c *gin.Context) { diff --git a/pkg/cover/server_test.go b/pkg/cover/server_test.go index 62b75d4..fb7feb7 100644 --- a/pkg/cover/server_test.go +++ b/pkg/cover/server_test.go @@ -1,6 +1,8 @@ package cover import ( + "bytes" + "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -186,13 +188,36 @@ func TestClearService(t *testing.T) { router := GocServer(os.Stdout) - // get profile with invalid force parameter + // clear profile with non-exist port w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/v1/cover/clear", nil) + req, _ := http.NewRequest("POST", "/v1/cover/clear", bytes.NewBuffer([]byte(`{}`))) + req.Header.Set("Content-Type", "application/json") router.ServeHTTP(w, req) assert.Equal(t, http.StatusExpectationFailed, w.Code) assert.Contains(t, w.Body.String(), "invalid port") + + // clear profile with invalid service + w = httptest.NewRecorder() + req, _ = http.NewRequest("POST", "/v1/cover/clear", nil) + req.Header.Set("Content-Type", "application/json") + router.ServeHTTP(w, req) + assert.Equal(t, http.StatusExpectationFailed, w.Code) + assert.Contains(t, w.Body.String(), "invalid request") + + // clear profile with service and address set at at the same time + p := ProfileParam{ + Service: []string{"goc"}, + Address: []string{"http://127.0.0.1:3333"}, + } + encoded, err := json.Marshal(p) + assert.NoError(t, err) + w = httptest.NewRecorder() + req, _ = http.NewRequest("POST", "/v1/cover/clear", bytes.NewBuffer(encoded)) + req.Header.Set("Content-Type", "application/json") + router.ServeHTTP(w, req) + assert.Equal(t, http.StatusExpectationFailed, w.Code) + assert.Contains(t, w.Body.String(), "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately") } func TestInitService(t *testing.T) { diff --git a/tests/clear.bats b/tests/clear.bats index 24583c7..f8cb637 100755 --- a/tests/clear.bats +++ b/tests/clear.bats @@ -38,6 +38,7 @@ setup_file() { } teardown_file() { + rm *_profile_listen_addr kill -9 $GOC_PID kill -9 $GOCC_PID kill -9 $SAMPLE_PID @@ -65,4 +66,37 @@ teardown_file() { [[ "$output" == *"coverage counter clear call successfully"* ]] wait $profile_pid +} + +@test "test clear by service name" { + goc build --output=./test-service + ./test-service 3>&- & + TEST_SERVICE=$! + sleep 1 + + # clear by wrong service name + run goc clear --service="test-servicej" + [ "$status" -eq 0 ] + [ "$output" = "" ] + + # check by goc profile, as the last step is wrong + # the coverage count should be 1 + run goc profile --coverfile="simple-project/a/a.go" --force + info clear3 output: $output + [ "$status" -eq 0 ] + [[ "$output" =~ "example.com/simple-project/a/a.go:4.12,6.2 1 1" ]] + + # clear by right service name + run goc clear --service="test-service" + [ "$status" -eq 0 ] + [[ "$output" =~ "coverage counter clear call successfully" ]] + + # check by goc profile, the coverage count should be reset to 0 + run goc profile --coverfile="simple-project/a/a.go" --force + info clear4 output: $output + [ "$status" -eq 0 ] + [[ "$output" =~ "example.com/simple-project/a/a.go:4.12,6.2 1 0" ]] + + + kill -9 $TEST_SERVICE } \ No newline at end of file