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 2020/12/31 01:54:20 UTC
[GitHub] [apisix-dashboard] starsz opened a new pull request #1169: feat: remove the etcd dependency in the service unit test
starsz opened a new pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169
Please answer these questions before submitting a pull request
- Why submit this pull request?
- [] Bugfix
- [x] New feature provided
- [ ] Improve performance
- Related issues
#1161
### New feature or improvement
- Describe the details and related test reports.
Remove the etcd dependency in the service unit test
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] starsz merged pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
starsz merged pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] codecov-io edited a comment on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
codecov-io edited a comment on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-752815085
# [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=h1) Report
> Merging [#1169](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=desc) (f8239e2) into [master](https://codecov.io/gh/apache/apisix-dashboard/commit/9824b5f60121f7274c41a93aacff0cc80dec0fa2?el=desc) (9824b5f) will **decrease** coverage by `10.14%`.
> The diff coverage is `n/a`.
[](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=tree)
```diff
@@ Coverage Diff @@
## master #1169 +/- ##
===========================================
- Coverage 67.08% 56.93% -10.15%
===========================================
Files 46 46
Lines 2947 2947
===========================================
- Hits 1977 1678 -299
- Misses 731 1004 +273
- Partials 239 265 +26
```
| [Impacted Files](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [api/internal/handler/plugin/plugin.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvcGx1Z2luL3BsdWdpbi5nbw==) | `16.66% <0.00%> (-70.00%)` | :arrow_down: |
| [api/internal/core/store/query.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvcXVlcnkuZ28=) | `45.23% <0.00%> (-52.39%)` | :arrow_down: |
| [api/internal/core/store/selector.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc2VsZWN0b3IuZ28=) | `35.18% <0.00%> (-51.86%)` | :arrow_down: |
| [api/internal/core/store/validate\_mock.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvdmFsaWRhdGVfbW9jay5nbw==) | `0.00% <0.00%> (-50.00%)` | :arrow_down: |
| [api/internal/filter/authentication.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2ZpbHRlci9hdXRoZW50aWNhdGlvbi5nbw==) | `44.11% <0.00%> (-32.36%)` | :arrow_down: |
| [api/internal/core/store/store.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc3RvcmUuZ28=) | `56.96% <0.00%> (-29.70%)` | :arrow_down: |
| [api/internal/handler/global\_rule/global\_rule.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvZ2xvYmFsX3J1bGUvZ2xvYmFsX3J1bGUuZ28=) | `64.51% <0.00%> (-19.36%)` | :arrow_down: |
| [api/internal/handler/consumer/consumer.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvY29uc3VtZXIvY29uc3VtZXIuZ28=) | `74.46% <0.00%> (-17.03%)` | :arrow_down: |
| [api/internal/handler/ssl/ssl.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvc3NsL3NzbC5nbw==) | `33.33% <0.00%> (-14.82%)` | :arrow_down: |
| [api/internal/utils/json\_patch.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL3V0aWxzL2pzb25fcGF0Y2guZ28=) | `44.82% <0.00%> (-13.80%)` | :arrow_down: |
| ... and [14 more](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree-more) | |
------
[Continue to review full report at Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=footer). Last update [9824b5f...f8239e2](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] juzhiyuan commented on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
juzhiyuan commented on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-773114597
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] nic-chen commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
nic-chen commented on a change in pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#discussion_r570015980
##########
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:
OK
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] juzhiyuan commented on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
juzhiyuan commented on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-773114764
@starsz You may need to request reviews from others first.
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] starsz commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
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.
##########
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"),
+ },
+ {
+ caseDesc: "update failed, update return error",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ giveErr: fmt.Errorf("update failed"),
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{ID: "s1"},
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("update failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("update failed")),
+ },
+ }
- //list
- listInput11 := &ListInput{}
- reqBody = `{"page_size": 10, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput11)
- assert.Nil(t, err)
- ctx.SetInput(listInput11)
- _, err = handler.List(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
- //delete test data
- inputDel11 := &BatchDelete{}
- reqBody = `{"ids": "11"}`
- err = json.Unmarshal([]byte(reqBody), inputDel11)
- assert.Nil(t, err)
- ctx.SetInput(inputDel11)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ 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)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Update(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestService_Patch_Update(t *testing.T) {
- //create
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Patch(t *testing.T) {
+ existService := &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "exist_service",
+ UpstreamID: "u1",
+ EnableWebsocket: false,
+ Labels: map[string]string{
+ "version": "v1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ }
+
+ tests := []struct {
+ caseDesc string
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ serviceInput string
+ serviceRet *entity.Service
+ serviceErr error
+ called bool
+ }{
+ {
+ caseDesc: "patch all success",
+ giveInput: &PatchInput{
+ ID: "s1",
+ SubPath: "",
+ Body: []byte(`{
+ "name":"patched",
+ "upstream_id":"u2",
+ "enable_websocket":true,
+ "labels":{
+ "version":"v1",
+ "build":"16"
+ },
+ "plugins":{
+ "limit-count":{
+ "count":2,
+ "time_window":60,
+ "rejected_code": 504,
+ "key":"remote_addr"
+ },
+ "key-auth":{
+ "key":"auth-one"
+ }
+ }
+ }`),
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "patched",
+ UpstreamID: "u2",
+ EnableWebsocket: true,
+ Labels: map[string]string{
+ "version": "v1",
+ "build": "16",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": float64(2),
+ "time_window": float64(60),
+ "rejected_code": float64(504),
+ "key": "remote_addr",
+ },
+ "key-auth": map[string]interface{}{
+ "key": "auth-one",
+ },
+ },
+ },
+ serviceInput: "s1",
+ serviceRet: existService,
+ called: true,
+ },
+ {
+ caseDesc: "patch name success",
Review comment:
Fixed.
##########
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"),
+ },
+ {
+ caseDesc: "update failed, update return error",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ giveErr: fmt.Errorf("update failed"),
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{ID: "s1"},
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("update failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("update failed")),
+ },
+ }
- //list
- listInput11 := &ListInput{}
- reqBody = `{"page_size": 10, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput11)
- assert.Nil(t, err)
- ctx.SetInput(listInput11)
- _, err = handler.List(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
- //delete test data
- inputDel11 := &BatchDelete{}
- reqBody = `{"ids": "11"}`
- err = json.Unmarshal([]byte(reqBody), inputDel11)
- assert.Nil(t, err)
- ctx.SetInput(inputDel11)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ 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)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Update(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestService_Patch_Update(t *testing.T) {
- //create
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Patch(t *testing.T) {
+ existService := &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "exist_service",
+ UpstreamID: "u1",
+ EnableWebsocket: false,
+ Labels: map[string]string{
+ "version": "v1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ }
+
+ tests := []struct {
+ caseDesc string
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ serviceInput string
+ serviceRet *entity.Service
+ serviceErr error
+ called bool
+ }{
+ {
+ caseDesc: "patch all success",
Review comment:
Added.
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] codecov-io commented on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
codecov-io commented on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-752815085
# [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=h1) Report
> Merging [#1169](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=desc) (515edad) into [master](https://codecov.io/gh/apache/apisix-dashboard/commit/45d79b674c2f500d76b76939674b6792201a70bb?el=desc) (45d79b6) will **increase** coverage by `1.26%`.
> The diff coverage is `n/a`.
[](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=tree)
```diff
@@ Coverage Diff @@
## master #1169 +/- ##
==========================================
+ Coverage 42.30% 43.56% +1.26%
==========================================
Files 31 31
Lines 1924 1928 +4
==========================================
+ Hits 814 840 +26
+ Misses 1000 983 -17
+ Partials 110 105 -5
```
| [Impacted Files](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [api/internal/handler/global\_rule/global\_rule.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvZ2xvYmFsX3J1bGUvZ2xvYmFsX3J1bGUuZ28=) | `61.40% <0.00%> (-0.87%)` | :arrow_down: |
| [api/internal/handler/service/service.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvc2VydmljZS9zZXJ2aWNlLmdv) | `52.08% <0.00%> (+24.99%)` | :arrow_up: |
------
[Continue to review full report at Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=footer). Last update [45d79b6...515edad](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] nic-chen commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
nic-chen commented on a change in pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#discussion_r569968494
##########
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"),
+ },
+ {
+ caseDesc: "update failed, update return error",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ giveErr: fmt.Errorf("update failed"),
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{ID: "s1"},
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("update failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("update failed")),
+ },
+ }
- //list
- listInput11 := &ListInput{}
- reqBody = `{"page_size": 10, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput11)
- assert.Nil(t, err)
- ctx.SetInput(listInput11)
- _, err = handler.List(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
- //delete test data
- inputDel11 := &BatchDelete{}
- reqBody = `{"ids": "11"}`
- err = json.Unmarshal([]byte(reqBody), inputDel11)
- assert.Nil(t, err)
- ctx.SetInput(inputDel11)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ 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)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Update(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestService_Patch_Update(t *testing.T) {
- //create
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Patch(t *testing.T) {
+ existService := &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "exist_service",
+ UpstreamID: "u1",
+ EnableWebsocket: false,
+ Labels: map[string]string{
+ "version": "v1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ }
+
+ tests := []struct {
+ caseDesc string
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ serviceInput string
+ serviceRet *entity.Service
+ serviceErr error
+ called bool
+ }{
+ {
+ caseDesc: "patch all success",
+ giveInput: &PatchInput{
+ ID: "s1",
+ SubPath: "",
+ Body: []byte(`{
+ "name":"patched",
+ "upstream_id":"u2",
+ "enable_websocket":true,
+ "labels":{
+ "version":"v1",
+ "build":"16"
+ },
+ "plugins":{
+ "limit-count":{
+ "count":2,
+ "time_window":60,
+ "rejected_code": 504,
+ "key":"remote_addr"
+ },
+ "key-auth":{
+ "key":"auth-one"
+ }
+ }
+ }`),
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "patched",
+ UpstreamID: "u2",
+ EnableWebsocket: true,
+ Labels: map[string]string{
+ "version": "v1",
+ "build": "16",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": float64(2),
+ "time_window": float64(60),
+ "rejected_code": float64(504),
+ "key": "remote_addr",
+ },
+ "key-auth": map[string]interface{}{
+ "key": "auth-one",
+ },
+ },
+ },
+ serviceInput: "s1",
+ serviceRet: existService,
+ called: true,
+ },
+ {
+ caseDesc: "patch name success",
Review comment:
desc is not clear.
##########
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"),
+ },
+ {
+ caseDesc: "update failed, update return error",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ giveErr: fmt.Errorf("update failed"),
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{ID: "s1"},
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("update failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("update failed")),
+ },
+ }
- //list
- listInput11 := &ListInput{}
- reqBody = `{"page_size": 10, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput11)
- assert.Nil(t, err)
- ctx.SetInput(listInput11)
- _, err = handler.List(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
- //delete test data
- inputDel11 := &BatchDelete{}
- reqBody = `{"ids": "11"}`
- err = json.Unmarshal([]byte(reqBody), inputDel11)
- assert.Nil(t, err)
- ctx.SetInput(inputDel11)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ 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)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Update(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestService_Patch_Update(t *testing.T) {
- //create
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Patch(t *testing.T) {
+ existService := &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "exist_service",
+ UpstreamID: "u1",
+ EnableWebsocket: false,
+ Labels: map[string]string{
+ "version": "v1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ }
+
+ tests := []struct {
+ caseDesc string
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ serviceInput string
+ serviceRet *entity.Service
+ serviceErr error
+ called bool
+ }{
+ {
+ caseDesc: "patch all success",
Review comment:
we could add a test case for part data.
##########
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:
we could add a test case for service not found.
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] juzhiyuan commented on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
juzhiyuan commented on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-773166020
may sync the latest codes to fix E2E failed.
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] codecov-io edited a comment on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
codecov-io edited a comment on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-752815085
# [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=h1) Report
> Merging [#1169](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=desc) (f8239e2) into [master](https://codecov.io/gh/apache/apisix-dashboard/commit/9824b5f60121f7274c41a93aacff0cc80dec0fa2?el=desc) (9824b5f) will **decrease** coverage by `10.14%`.
> The diff coverage is `n/a`.
[](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=tree)
```diff
@@ Coverage Diff @@
## master #1169 +/- ##
===========================================
- Coverage 67.08% 56.93% -10.15%
===========================================
Files 46 46
Lines 2947 2947
===========================================
- Hits 1977 1678 -299
- Misses 731 1004 +273
- Partials 239 265 +26
```
| [Impacted Files](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [api/internal/handler/plugin/plugin.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvcGx1Z2luL3BsdWdpbi5nbw==) | `16.66% <0.00%> (-70.00%)` | :arrow_down: |
| [api/internal/core/store/query.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvcXVlcnkuZ28=) | `45.23% <0.00%> (-52.39%)` | :arrow_down: |
| [api/internal/core/store/selector.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc2VsZWN0b3IuZ28=) | `35.18% <0.00%> (-51.86%)` | :arrow_down: |
| [api/internal/core/store/validate\_mock.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvdmFsaWRhdGVfbW9jay5nbw==) | `0.00% <0.00%> (-50.00%)` | :arrow_down: |
| [api/internal/filter/authentication.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2ZpbHRlci9hdXRoZW50aWNhdGlvbi5nbw==) | `44.11% <0.00%> (-32.36%)` | :arrow_down: |
| [api/internal/core/store/store.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc3RvcmUuZ28=) | `56.96% <0.00%> (-29.70%)` | :arrow_down: |
| [api/internal/handler/global\_rule/global\_rule.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvZ2xvYmFsX3J1bGUvZ2xvYmFsX3J1bGUuZ28=) | `64.51% <0.00%> (-19.36%)` | :arrow_down: |
| [api/internal/handler/consumer/consumer.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvY29uc3VtZXIvY29uc3VtZXIuZ28=) | `74.46% <0.00%> (-17.03%)` | :arrow_down: |
| [api/internal/handler/ssl/ssl.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvc3NsL3NzbC5nbw==) | `33.33% <0.00%> (-14.82%)` | :arrow_down: |
| [api/internal/utils/json\_patch.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL3V0aWxzL2pzb25fcGF0Y2guZ28=) | `44.82% <0.00%> (-13.80%)` | :arrow_down: |
| ... and [14 more](https://codecov.io/gh/apache/apisix-dashboard/pull/1169/diff?src=pr&el=tree-more) | |
------
[Continue to review full report at Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=footer). Last update [9824b5f...f8239e2](https://codecov.io/gh/apache/apisix-dashboard/pull/1169?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] nic-chen commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
nic-chen commented on a change in pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#discussion_r570015980
##########
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:
OK
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] juzhiyuan commented on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
juzhiyuan commented on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-773114597
waiting for #1426
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] starsz commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
starsz commented on a change in pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#discussion_r570000654
##########
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"),
+ },
+ {
+ caseDesc: "update failed, update return error",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ giveErr: fmt.Errorf("update failed"),
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{ID: "s1"},
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("update failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("update failed")),
+ },
+ }
- //list
- listInput11 := &ListInput{}
- reqBody = `{"page_size": 10, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput11)
- assert.Nil(t, err)
- ctx.SetInput(listInput11)
- _, err = handler.List(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
- //delete test data
- inputDel11 := &BatchDelete{}
- reqBody = `{"ids": "11"}`
- err = json.Unmarshal([]byte(reqBody), inputDel11)
- assert.Nil(t, err)
- ctx.SetInput(inputDel11)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ 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)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Update(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestService_Patch_Update(t *testing.T) {
- //create
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Patch(t *testing.T) {
+ existService := &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "exist_service",
+ UpstreamID: "u1",
+ EnableWebsocket: false,
+ Labels: map[string]string{
+ "version": "v1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ }
+
+ tests := []struct {
+ caseDesc string
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ serviceInput string
+ serviceRet *entity.Service
+ serviceErr error
+ called bool
+ }{
+ {
+ caseDesc: "patch all success",
Review comment:
Added.
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] juzhiyuan commented on pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
juzhiyuan commented on pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#issuecomment-772419999
@starsz you need to request members to review.
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] starsz commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
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
[GitHub] [apisix-dashboard] starsz merged pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
starsz merged pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169
----------------------------------------------------------------
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
[GitHub] [apisix-dashboard] starsz commented on a change in pull request #1169: feat: remove the etcd dependency in the service unit test
Posted by GitBox <gi...@apache.org>.
starsz commented on a change in pull request #1169:
URL: https://github.com/apache/apisix-dashboard/pull/1169#discussion_r570000435
##########
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"),
+ },
+ {
+ caseDesc: "update failed, update return error",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "s1",
+ Service: entity.Service{
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ },
+ giveErr: fmt.Errorf("update failed"),
+ upstreamInput: "u1",
+ upstreamRet: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{ID: "s1"},
+ Name: "s1",
+ UpstreamID: "u1",
+ Desc: "test service",
+ },
+ wantErr: fmt.Errorf("update failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("update failed")),
+ },
+ }
- //list
- listInput11 := &ListInput{}
- reqBody = `{"page_size": 10, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput11)
- assert.Nil(t, err)
- ctx.SetInput(listInput11)
- _, err = handler.List(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ serviceStore := &store.MockInterface{}
+ serviceStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Service)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
- //delete test data
- inputDel11 := &BatchDelete{}
- reqBody = `{"ids": "11"}`
- err = json.Unmarshal([]byte(reqBody), inputDel11)
- assert.Nil(t, err)
- ctx.SetInput(inputDel11)
- _, err = handler.BatchDelete(ctx)
- assert.Nil(t, err)
+ 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)
+ h := Handler{serviceStore: serviceStore, upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Update(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestService_Patch_Update(t *testing.T) {
- //create
- handler := &Handler{
- serviceStore: store.GetStore(store.HubKeyService),
+func TestService_Patch(t *testing.T) {
+ existService := &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "exist_service",
+ UpstreamID: "u1",
+ EnableWebsocket: false,
+ Labels: map[string]string{
+ "version": "v1",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": 2,
+ "time_window": 60,
+ "rejected_code": 503,
+ "key": "remote_addr",
+ },
+ },
+ }
+
+ tests := []struct {
+ caseDesc string
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Service
+ wantErr error
+ wantRet interface{}
+ serviceInput string
+ serviceRet *entity.Service
+ serviceErr error
+ called bool
+ }{
+ {
+ caseDesc: "patch all success",
+ giveInput: &PatchInput{
+ ID: "s1",
+ SubPath: "",
+ Body: []byte(`{
+ "name":"patched",
+ "upstream_id":"u2",
+ "enable_websocket":true,
+ "labels":{
+ "version":"v1",
+ "build":"16"
+ },
+ "plugins":{
+ "limit-count":{
+ "count":2,
+ "time_window":60,
+ "rejected_code": 504,
+ "key":"remote_addr"
+ },
+ "key-auth":{
+ "key":"auth-one"
+ }
+ }
+ }`),
+ },
+ wantInput: &entity.Service{
+ BaseInfo: entity.BaseInfo{
+ ID: "s1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ Name: "patched",
+ UpstreamID: "u2",
+ EnableWebsocket: true,
+ Labels: map[string]string{
+ "version": "v1",
+ "build": "16",
+ },
+ Plugins: map[string]interface{}{
+ "limit-count": map[string]interface{}{
+ "count": float64(2),
+ "time_window": float64(60),
+ "rejected_code": float64(504),
+ "key": "remote_addr",
+ },
+ "key-auth": map[string]interface{}{
+ "key": "auth-one",
+ },
+ },
+ },
+ serviceInput: "s1",
+ serviceRet: existService,
+ called: true,
+ },
+ {
+ caseDesc: "patch name success",
Review comment:
Fixed.
----------------------------------------------------------------
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