You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by GitBox <gi...@apache.org> on 2021/02/04 07:16:06 UTC
[GitHub] [apisix-dashboard] starsz commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
starsz commented on a change in pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#discussion_r569993804
##########
File path: api/internal/handler/service/service_test.go
##########
@@ -18,274 +18,832 @@
package service
import (
- "encoding/json"
- "strings"
+ "fmt"
+ "net/http"
"testing"
- "time"
"github.com/shiningrush/droplet"
+ "github.com/shiningrush/droplet/data"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
- "github.com/apisix/manager-api/internal/conf"
"github.com/apisix/manager-api/internal/core/entity"
- "github.com/apisix/manager-api/internal/core/storage"
"github.com/apisix/manager-api/internal/core/store"
+ "github.com/apisix/manager-api/internal/handler"
)
-func TestService(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
-
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Service
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "normal",
+ giveInput: &GetInput{ID: "s1"},
+ wantGetKey: "s1",
+ giveRet: &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ },
+ wantRet: &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "store get failed",
+ giveInput: &GetInput{ID: "failed_key"},
+ wantGetKey: "failed_key",
+ giveErr: fmt.Errorf("get failed"),
+ wantErr: fmt.Errorf("get failed"),
+ wantRet: &data.SpecCodeResponse{
+ StatusCode: http.StatusInternalServerError,
+ },
+ },
}
- assert.NotNil(t, handler)
- //create
- ctx := droplet.NewContext()
- service := &entity.Service{}
- reqBody := `{
- "id": "1",
- "plugins": {
- "limit-count": {
- "count": 2,
- "time_window": 60,
- "rejected_code": 503,
- "key": "remote_addr"
- }
- },
- "upstream": {
- "type": "roundrobin",
- "nodes": [{
- "host": "39.97.63.215",
- "port": 80,
- "weight": 1
- }]
- }
- }`
- err = json.Unmarshal([]byte(reqBody), service)
- assert.Nil(t, err)
- ctx.SetInput(service)
- ret, err := handler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Service)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ mStore := &store.MockInterface{}
+ mStore.On("Get", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ assert.Equal(t, tc.wantGetKey, args.Get(0))
+ }).Return(tc.giveRet, tc.giveErr)
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = handler.Get(ctx)
- stored := ret.(*entity.Service)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, service.ID)
+ h := Handler{serviceStore: mStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Get(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
+}
- //update
- service2 := &UpdateInput{}
- service2.ID = "1"
- reqBody = `{
- "name": "test-service",
- "plugins": {
- "limit-count": {
- "count": 2,
- "time_window": 60,
- "rejected_code": 503,
- "key": "remote_addr"
- }
+func TestService_List(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Service
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all service",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ giveData: []*entity.Service{
+ {Name: "s1"},
+ {Name: "s2"},
+ {Name: "test_service"},
+ {Name: "service_test"},
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ &entity.Service{Name: "s1"},
+ &entity.Service{Name: "s2"},
+ &entity.Service{Name: "test_service"},
+ &entity.Service{Name: "service_test"},
+ },
+ TotalSize: 4,
+ },
},
- "enable_websocket": true,
- "upstream": {
- "type": "roundrobin",
- "nodes": [{
- "host": "39.97.63.215",
- "port": 80,
- "weight": 1
- }]
- }
- }`
- err = json.Unmarshal([]byte(reqBody), service2)
- assert.Nil(t, err)
- ctx.SetInput(service2)
- ret, err = handler.Update(ctx)
- assert.Nil(t, err)
- // Check the returned value
- objRet, ok = ret.(*entity.Service)
- assert.True(t, ok)
- assert.Equal(t, service2.ID, objRet.ID)
- assert.Equal(t, service2.Name, objRet.Name)
+ {
+ caseDesc: "list service with 'service'",
+ giveInput: &ListInput{
+ Name: "service",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ giveData: []*entity.Service{
+ {BaseInfo: entity.BaseInfo{CreateTime: 1609376661}, Name: "s1"},
+ {BaseInfo: entity.BaseInfo{CreateTime: 1609376662}, Name: "s2"},
+ {BaseInfo: entity.BaseInfo{CreateTime: 1609376663}, Name: "test_service"},
+ {BaseInfo: entity.BaseInfo{CreateTime: 1609376664}, Name: "service_test"},
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ &entity.Service{BaseInfo: entity.BaseInfo{CreateTime: 1609376663}, Name: "test_service"},
+ &entity.Service{BaseInfo: entity.BaseInfo{CreateTime: 1609376664}, Name: "service_test"},
+ },
+ TotalSize: 2,
+ },
+ },
+ {
+ caseDesc: "list service with key s1",
+ giveInput: &ListInput{
+ Name: "s1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ giveData: []*entity.Service{
+ {Name: "s1"},
+ {Name: "s2"},
+ {Name: "test_service"},
+ {Name: "service_test"},
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ &entity.Service{Name: "s1"},
+ },
+ TotalSize: 1,
+ },
+ },
+ {
+ caseDesc: "list service and format",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ giveData: []*entity.Service{
+ {
+ Name: "s1",
+ Upstream: &entity.UpstreamDef{
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {Name: "s2"},
+ {Name: "test_service"},
+ {Name: "service_test"},
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ &entity.Service{Name: "s1", Upstream: &entity.UpstreamDef{
+ Nodes: []*entity.Node{
+ {
+ Host: "39.97.63.215",
+ Port: 80,
+ Weight: 1,
+ },
+ },
+ }},
+ &entity.Service{Name: "s2"},
+ &entity.Service{Name: "test_service"},
+ &entity.Service{Name: "service_test"},
+ },
+ TotalSize: 4,
+ },
+ },
+ }
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ mStore := &store.MockInterface{}
+ mStore.On("List", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(0).(store.ListInput)
+ assert.Equal(t, tc.wantInput.PageSize, input.PageSize)
+ assert.Equal(t, tc.wantInput.PageNumber, input.PageNumber)
+ }).Return(func(input store.ListInput) *store.ListOutput {
+ var returnData []interface{}
+ for _, c := range tc.giveData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := handler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ returnData = append(returnData, input.Format(c))
+ }
+ }
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
+ }
+ }, tc.giveErr)
- //list search match
- listInput2 := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1, "name": "test"}`
- err = json.Unmarshal([]byte(reqBody), listInput2)
- assert.Nil(t, err)
- ctx.SetInput(listInput2)
- retPage, err = handler.List(ctx)
- assert.Nil(t, err)
- dataPage = retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ h := Handler{serviceStore: mStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.List(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
+}
- //list search not match
- listInput3 := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1, "name": "not-exists"}`
- err = json.Unmarshal([]byte(reqBody), listInput3)
- assert.Nil(t, err)
- ctx.SetInput(listInput3)
- retPage, err = handler.List(ctx)
- assert.Nil(t, err)
- dataPage = retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 0)
+func TestService_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Service
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ upstreamInput string
+ upstreamRet interface{}
+ upstreamErr interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, upstream not found",
+ getCalled: false,
+ giveInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("upstream id: u1 not found"),
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ upstreamInput: "u1",
+ upstreamErr: data.ErrNotFound,
+ },
+ {
+ caseDesc: "create failed, upstream return error",
+ getCalled: false,
+ giveInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("unknown error"),
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ upstreamInput: "u1",
+ upstreamErr: fmt.Errorf("unknown error"),
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
- //create without upstream
- service11 := &entity.Service{}
- reqBody = `{
- "id": "11",
- "plugins": {
- "limit-count": {
- "count": 2,
- "time_window": 60,
- "rejected_code": 503,
- "key": "remote_addr"
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), service11)
- assert.Nil(t, err)
- ctx.SetInput(service11)
- ret, err = handler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok = ret.(*entity.Service)
- assert.True(t, ok)
- assert.Equal(t, "11", objRet.ID)
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Get", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ id := args.Get(0).(string)
+ assert.Equal(t, tc.upstreamInput, id)
+ }).Return(tc.upstreamRet, tc.upstreamErr)
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
+}
- //get
- input11 := &GetInput{}
- input11.ID = "11"
- ctx.SetInput(input11)
- ret, err = handler.Get(ctx)
- stored = ret.(*entity.Service)
- assert.Nil(t, err)
- assert.Equal(t, "11", stored.ID)
+func TestService_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ upstreamInput string
+ upstreamRet interface{}
+ upstreamErr interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ },
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s2",
+ },
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (s1) doesn't match ID on body (s2)"),
+ },
+ {
+ caseDesc: "update failed, upstream not found",
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ wantInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("upstream id: u1 not found"),
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ upstreamInput: "u1",
+ upstreamErr: data.ErrNotFound,
+ },
+ {
+ caseDesc: "update failed, upstream return error",
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ wantInput: &entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("unknown error"),
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ upstreamInput: "u1",
+ upstreamErr: fmt.Errorf("unknown error"),
+ },
+ {
Review comment:
It's hard to test this.
Because we don't call `upstream.Get` in the `Update` function.
What we can test is that the third argument in the `Update` function is true.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org