You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by GitBox <gi...@apache.org> on 2021/02/07 10:16:37 UTC
[GitHub] [apisix-dashboard] Jaycean opened a new pull request #1452: feat(be): refactor upstream unit test
Jaycean opened a new pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452
Please answer these questions before submitting a pull request
- Why submit this pull request?
- [ ] Bugfix
- [x] New feature provided
- [ ] Improve performance
- [ ] Backport patches
- Related issues
___
### Bugfix
- Description
Refactor upstream unit test.
Remove etcd dependency of 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] Jaycean edited a comment on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean edited a comment on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775716643
>@starsz starsz 3 hours ago Contributor
Yes.The same question I had meet.
It's strange why I can't reply directly
Yes, in fact, I learned from your writing here
----------------------------------------------------------------
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] Jaycean commented on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775716643
https://github.com/apache/apisix-dashboard/pull/1452#discussion_r572544034
It's strange why I can't reply directly
Yes, in fact, I learned from your writing here
----------------------------------------------------------------
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] Jaycean commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r572632419
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
Review comment:
done.
I modified the nodes data type in mockdata to test the format.
----------------------------------------------------------------
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] Jaycean commented on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-774810574
cc @starsz @nic-chen @imjoey
PTAL. Thks.
----------------------------------------------------------------
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 #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
nic-chen commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571956356
##########
File path: api/internal/handler/upstream/upstream.go
##########
@@ -270,27 +267,15 @@ func (h *Handler) Exist(c droplet.Context) (interface{}, error) {
return nil, err
}
- sort := store.NewSort(nil)
- filter := store.NewFilter([]string{"name", name})
- pagination := store.NewPagination(0, 0)
- query := store.NewQuery(sort, filter, pagination)
- rows := store.NewFilterSelector(toRows(ret), query)
Review comment:
@Jaycean that's good, thanks.
----------------------------------------------------------------
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 #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
nic-chen commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571882686
##########
File path: api/internal/handler/upstream/upstream.go
##########
@@ -270,27 +267,15 @@ func (h *Handler) Exist(c droplet.Context) (interface{}, error) {
return nil, err
}
- sort := store.NewSort(nil)
- filter := store.NewFilter([]string{"name", name})
- pagination := store.NewPagination(0, 0)
- query := store.NewQuery(sort, filter, pagination)
- rows := store.NewFilterSelector(toRows(ret), query)
Review comment:
maybe we could remove these functions now, please confirm whether these functions are invoked elsewhere.
----------------------------------------------------------------
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 merged pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
juzhiyuan merged pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452
----------------------------------------------------------------
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 #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
starsz commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r572542126
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
Review comment:
Maybe we should test the format in `List` function.
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Key: "server_addr2",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u3",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream3",
+ Key: "server_addr3",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ }
- upstreamHandler = &Handler{
- upstreamStore: store.GetStore(store.HubKeyUpstream),
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Upstream
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all upstream",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ mockData[1],
+ mockData[2],
+ },
+ TotalSize: 3,
+ },
+ },
+ {
+ caseDesc: "list upstream with 'upstream1'",
+ giveInput: &ListInput{
+ Name: "upstream1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ },
+ TotalSize: 1,
+ },
+ },
}
- assert.NotNil(t, upstreamHandler)
-
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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 mockData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
+ returnData = append(returnData, input.Format(c))
+ }
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
-
- //update
- upstream2 := &UpdateInput{}
- upstream2.ID = "1"
- reqBody = `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "enable_websocket": true,
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream2)
- assert.Nil(t, err)
- ctx.SetInput(upstream2)
- ret, err = upstreamHandler.Update(ctx)
- assert.Nil(t, err)
- // check the returned value
- objRet, ok = ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, upstream2.ID, objRet.ID)
-
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := upstreamHandler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ }, tc.giveErr)
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstream_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Upstream
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: nil,
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestUpstream_Pass_Host(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "2",
- "nodes": [{
- "host": "httpbin.org",
- "port": 80,
- "weight": 1
- }],
- "type": "roundrobin",
- "pass_host": "node"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "2", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "2"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
+func TestUpstream_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "update success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (u1) doesn't match ID on body (u2)"),
+ },
Review comment:
Maybe we need to test `Update` failed.
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Key: "server_addr2",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u3",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream3",
+ Key: "server_addr3",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ }
- upstreamHandler = &Handler{
- upstreamStore: store.GetStore(store.HubKeyUpstream),
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Upstream
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all upstream",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ mockData[1],
+ mockData[2],
+ },
+ TotalSize: 3,
+ },
+ },
+ {
+ caseDesc: "list upstream with 'upstream1'",
+ giveInput: &ListInput{
+ Name: "upstream1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ },
+ TotalSize: 1,
+ },
+ },
}
- assert.NotNil(t, upstreamHandler)
-
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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 mockData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
+ returnData = append(returnData, input.Format(c))
+ }
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
-
- //update
- upstream2 := &UpdateInput{}
- upstream2.ID = "1"
- reqBody = `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "enable_websocket": true,
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream2)
- assert.Nil(t, err)
- ctx.SetInput(upstream2)
- ret, err = upstreamHandler.Update(ctx)
- assert.Nil(t, err)
- // check the returned value
- objRet, ok = ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, upstream2.ID, objRet.ID)
-
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := upstreamHandler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ }, tc.giveErr)
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstream_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Upstream
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: nil,
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestUpstream_Pass_Host(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "2",
- "nodes": [{
- "host": "httpbin.org",
- "port": 80,
- "weight": 1
- }],
- "type": "roundrobin",
- "pass_host": "node"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "2", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "2"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
+func TestUpstream_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "update success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (u1) doesn't match ID on body (u2)"),
+ },
+ }
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "2"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+ h := Handler{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 TestUpstream_Patch_Update(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "3",
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1980,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "3", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- reqBody1 := `{
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1981,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- responesBody := `"nodes":[{"host":"172.16.238.20","port":1981,"weight":1}],"type":"roundrobin"}`
-
- input2 := &PatchInput{}
- input2.ID = "3"
- input2.SubPath = ""
- input2.Body = []byte(reqBody1)
- ctx.SetInput(input2)
-
- ret2, err := upstreamHandler.Patch(ctx)
- assert.Nil(t, err)
- _ret2, err := json.Marshal(ret2)
- assert.Nil(t, err)
- isContains := strings.Contains(string(_ret2), responesBody)
- assert.True(t, isContains)
+func TestUpstream_Patch(t *testing.T) {
+ existUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
- //delete test data
- inputDel2 := &BatchDelete{}
- reqBody = `{"ids": "3"}`
- err = json.Unmarshal([]byte(reqBody), inputDel2)
- assert.Nil(t, err)
- ctx.SetInput(inputDel2)
- _, err = upstreamHandler.BatchDelete(ctx)
+ patchUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
+ patchUpstreamBytes, err := json.Marshal(patchUpstream)
assert.Nil(t, err)
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "patch success",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "",
+ Body: patchUpstreamBytes,
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch success by path",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "/nodes",
+ Body: []byte(`[{"host": "172.16.238.20","port": 1981,"weight": 1}]`),
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(15),
+ "send": float64(15),
+ "read": float64(15),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch failed, path error",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "error",
+ Body: []byte("0"),
+ },
+ wantRet: handler.SpecCodeResponse(
+ errors.New("add operation does not apply: doc is missing path: \"error\": missing value")),
+ wantErr: errors.New("add operation does not apply: doc is missing path: \"error\": missing value"),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Get", mock.Anything, mock.Anything).Return(existUpstream, nil)
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.False(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Patch(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ if tc.wantErr != nil && err != nil {
+ assert.Error(t, tc.wantErr.(error), err.Error())
+ } else {
+ assert.Equal(t, tc.wantErr, err)
+ }
Review comment:
Yes.The same question I had meet.
----------------------------------------------------------------
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] Jaycean commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571795912
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Key: "server_addr2",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u3",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream3",
+ Key: "server_addr3",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ }
- upstreamHandler = &Handler{
- upstreamStore: store.GetStore(store.HubKeyUpstream),
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Upstream
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all upstream",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ mockData[1],
+ mockData[2],
+ },
+ TotalSize: 3,
+ },
+ },
+ {
+ caseDesc: "list upstream with 'upstream1'",
+ giveInput: &ListInput{
+ Name: "upstream1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ },
+ TotalSize: 1,
+ },
+ },
}
- assert.NotNil(t, upstreamHandler)
-
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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 mockData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
+ returnData = append(returnData, input.Format(c))
+ }
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
-
- //update
- upstream2 := &UpdateInput{}
- upstream2.ID = "1"
- reqBody = `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "enable_websocket": true,
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream2)
- assert.Nil(t, err)
- ctx.SetInput(upstream2)
- ret, err = upstreamHandler.Update(ctx)
- assert.Nil(t, err)
- // check the returned value
- objRet, ok = ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, upstream2.ID, objRet.ID)
-
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := upstreamHandler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ }, tc.giveErr)
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstream_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Upstream
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: nil,
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestUpstream_Pass_Host(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "2",
- "nodes": [{
- "host": "httpbin.org",
- "port": 80,
- "weight": 1
- }],
- "type": "roundrobin",
- "pass_host": "node"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "2", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "2"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
+func TestUpstream_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "update success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (u1) doesn't match ID on body (u2)"),
+ },
+ }
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "2"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+ h := Handler{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 TestUpstream_Patch_Update(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "3",
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1980,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "3", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- reqBody1 := `{
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1981,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- responesBody := `"nodes":[{"host":"172.16.238.20","port":1981,"weight":1}],"type":"roundrobin"}`
-
- input2 := &PatchInput{}
- input2.ID = "3"
- input2.SubPath = ""
- input2.Body = []byte(reqBody1)
- ctx.SetInput(input2)
-
- ret2, err := upstreamHandler.Patch(ctx)
- assert.Nil(t, err)
- _ret2, err := json.Marshal(ret2)
- assert.Nil(t, err)
- isContains := strings.Contains(string(_ret2), responesBody)
- assert.True(t, isContains)
+func TestUpstream_Patch(t *testing.T) {
+ existUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
- //delete test data
- inputDel2 := &BatchDelete{}
- reqBody = `{"ids": "3"}`
- err = json.Unmarshal([]byte(reqBody), inputDel2)
- assert.Nil(t, err)
- ctx.SetInput(inputDel2)
- _, err = upstreamHandler.BatchDelete(ctx)
+ patchUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
+ patchUpstreamBytes, err := json.Marshal(patchUpstream)
assert.Nil(t, err)
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "patch success",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "",
+ Body: patchUpstreamBytes,
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch success by path",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "/nodes",
+ Body: []byte(`[{"host": "172.16.238.20","port": 1981,"weight": 1}]`),
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(15),
+ "send": float64(15),
+ "read": float64(15),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch failed, path error",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "error",
+ Body: []byte("0"),
+ },
+ wantRet: handler.SpecCodeResponse(
+ errors.New("add operation does not apply: doc is missing path: \"error\": missing value")),
+ wantErr: errors.New("add operation does not apply: doc is missing path: \"error\": missing value"),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Get", mock.Anything, mock.Anything).Return(existUpstream, nil)
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.False(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Patch(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ if tc.wantErr != nil && err != nil {
+ assert.Error(t, tc.wantErr.(error), err.Error())
+ } else {
+ assert.Equal(t, tc.wantErr, err)
+ }
Review comment:
The patch method uses the package:`github.com/evanphx/json -Patch/V5`, when the error message is returned, the error is converted to match the error type, otherwise it cannot be matched.
Using wantret errors.New Create, geterr returns error.WithStack , error type with stack.
```
expected: *errors.errorString(&errors.errorString{s:"add operation does not apply: doc is missing path: \"error\": missing value"})
actual : *errors.withStack(add operation does not apply: doc is missing path: "error": missing value)
```
----------------------------------------------------------------
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] Jaycean commented on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775079236
> @Jaycean What is the current code coverage of `upstream.go`? It's a little strange that this pr doesn't show coverage
yeah,My local run test coverage is 79.1%
```
--- PASS: TestUpstream_ListUpstreamNames/get_upstream_list_names (0.00s)
--- PASS: TestUpstream_ListUpstreamNames/get_upstream_list_names_nil (0.00s)
PASS
coverage: 79.1% of statements
ok github.com/apisix/manager-api/internal/handler/upstream 1.091s coverage: 79.1% of statements
```
----------------------------------------------------------------
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] Jaycean edited a comment on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean edited a comment on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775079236
> @Jaycean What is the current code coverage of `upstream.go`? It's a little strange that this pr doesn't show coverage
yeah,My local run test coverage is 79.1%
```
--- PASS: TestUpstream_ListUpstreamNames/get_upstream_list_names (0.00s)
--- PASS: TestUpstream_ListUpstreamNames/get_upstream_list_names_nil (0.00s)
PASS
coverage: 79.1% of statements
ok github.com/apisix/manager-api/internal/handler/upstream 1.091s coverage: 79.1% of statements
```
It's really a bit strange. I'll see if there's a similar situation in other PR, no codecov report.
----------------------------------------------------------------
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] imjoey commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
imjoey commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r573030977
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Key: "server_addr2",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u3",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream3",
+ Key: "server_addr3",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ }
- upstreamHandler = &Handler{
- upstreamStore: store.GetStore(store.HubKeyUpstream),
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Upstream
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all upstream",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ mockData[1],
+ mockData[2],
+ },
+ TotalSize: 3,
+ },
+ },
+ {
+ caseDesc: "list upstream with 'upstream1'",
+ giveInput: &ListInput{
+ Name: "upstream1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ },
+ TotalSize: 1,
+ },
+ },
}
- assert.NotNil(t, upstreamHandler)
-
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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 mockData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
+ returnData = append(returnData, input.Format(c))
+ }
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
-
- //update
- upstream2 := &UpdateInput{}
- upstream2.ID = "1"
- reqBody = `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "enable_websocket": true,
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream2)
- assert.Nil(t, err)
- ctx.SetInput(upstream2)
- ret, err = upstreamHandler.Update(ctx)
- assert.Nil(t, err)
- // check the returned value
- objRet, ok = ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, upstream2.ID, objRet.ID)
-
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := upstreamHandler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ }, tc.giveErr)
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstream_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Upstream
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: nil,
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestUpstream_Pass_Host(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "2",
- "nodes": [{
- "host": "httpbin.org",
- "port": 80,
- "weight": 1
- }],
- "type": "roundrobin",
- "pass_host": "node"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "2", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "2"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
+func TestUpstream_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "update success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (u1) doesn't match ID on body (u2)"),
+ },
+ }
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "2"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+ h := Handler{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 TestUpstream_Patch_Update(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "3",
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1980,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "3", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- reqBody1 := `{
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1981,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- responesBody := `"nodes":[{"host":"172.16.238.20","port":1981,"weight":1}],"type":"roundrobin"}`
-
- input2 := &PatchInput{}
- input2.ID = "3"
- input2.SubPath = ""
- input2.Body = []byte(reqBody1)
- ctx.SetInput(input2)
-
- ret2, err := upstreamHandler.Patch(ctx)
- assert.Nil(t, err)
- _ret2, err := json.Marshal(ret2)
- assert.Nil(t, err)
- isContains := strings.Contains(string(_ret2), responesBody)
- assert.True(t, isContains)
+func TestUpstream_Patch(t *testing.T) {
+ existUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
- //delete test data
- inputDel2 := &BatchDelete{}
- reqBody = `{"ids": "3"}`
- err = json.Unmarshal([]byte(reqBody), inputDel2)
- assert.Nil(t, err)
- ctx.SetInput(inputDel2)
- _, err = upstreamHandler.BatchDelete(ctx)
+ patchUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
+ patchUpstreamBytes, err := json.Marshal(patchUpstream)
assert.Nil(t, err)
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "patch success",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "",
+ Body: patchUpstreamBytes,
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch success by path",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "/nodes",
+ Body: []byte(`[{"host": "172.16.238.20","port": 1981,"weight": 1}]`),
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(15),
+ "send": float64(15),
+ "read": float64(15),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch failed, path error",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "error",
+ Body: []byte("0"),
+ },
+ wantRet: handler.SpecCodeResponse(
+ errors.New("add operation does not apply: doc is missing path: \"error\": missing value")),
+ wantErr: errors.New("add operation does not apply: doc is missing path: \"error\": missing value"),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Get", mock.Anything, mock.Anything).Return(existUpstream, nil)
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.False(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Patch(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ if tc.wantErr != nil && err != nil {
+ assert.Error(t, tc.wantErr.(error), err.Error())
+ } else {
+ assert.Equal(t, tc.wantErr, err)
+ }
Review comment:
@Jaycean OK, gotcha. Thanks for the explanations.
----------------------------------------------------------------
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 #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
codecov-io edited a comment on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775714401
# [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=h1) Report
> Merging [#1452](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=desc) (28ed890) into [master](https://codecov.io/gh/apache/apisix-dashboard/commit/8796fa1b00617f83b495798cc7b86418d1456533?el=desc) (8796fa1) will **increase** coverage by `1.10%`.
> The diff coverage is `100.00%`.
[![Impacted file tree graph](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/graphs/tree.svg?width=650&height=150&src=pr&token=Q1HERXN96P)](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=tree)
```diff
@@ Coverage Diff @@
## master #1452 +/- ##
==========================================
+ Coverage 67.61% 68.72% +1.10%
==========================================
Files 48 48
Lines 3042 3038 -4
==========================================
+ Hits 2057 2088 +31
+ Misses 741 712 -29
+ Partials 244 238 -6
```
| [Impacted Files](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [api/internal/handler/upstream/upstream.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvdXBzdHJlYW0vdXBzdHJlYW0uZ28=) | `88.78% <100.00%> (+5.73%)` | :arrow_up: |
| [api/internal/core/store/selector.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc2VsZWN0b3IuZ28=) | `75.92% <0.00%> (-11.12%)` | :arrow_down: |
| [api/internal/log/log.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2xvZy9sb2cuZ28=) | `50.00% <0.00%> (-10.00%)` | :arrow_down: |
| [api/internal/core/entity/query.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvZW50aXR5L3F1ZXJ5Lmdv) | `0.00% <0.00%> (-9.62%)` | :arrow_down: |
| [api/internal/core/store/query.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvcXVlcnkuZ28=) | `88.09% <0.00%> (-9.53%)` | :arrow_down: |
| [api/internal/core/storage/etcd.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmFnZS9ldGNkLmdv) | `50.00% <0.00%> (+6.36%)` | :arrow_up: |
| [api/internal/handler/ssl/ssl.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvc3NsL3NzbC5nbw==) | `68.87% <0.00%> (+20.72%)` | :arrow_up: |
------
[Continue to review full report at Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?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/1452?src=pr&el=footer). Last update [8796fa1...28ed890](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?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] Jaycean edited a comment on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean edited a comment on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775716643
```
@starsz starsz 3 hours ago Contributor
Yes.The same question I had meet.
```
It's strange why I can't reply directly
Yes, in fact, I learned from your writing here
----------------------------------------------------------------
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] liuxiran commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
liuxiran commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571816080
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
Review comment:
I found that `LIst` support name as filter[1], it would be better add a case to test it ^_^
reference:
[1]: https://github.com/apache/apisix-dashboard/blob/a87028a4098afd2fb09cf1724028cff4da568b42/api/internal/handler/upstream/upstream.go#L91
----------------------------------------------------------------
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] liuxiran commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
liuxiran commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571816080
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
Review comment:
I found that `List` support name as filter[1], it would be better add a case to test it ^_^
reference:
[1]: https://github.com/apache/apisix-dashboard/blob/a87028a4098afd2fb09cf1724028cff4da568b42/api/internal/handler/upstream/upstream.go#L91
----------------------------------------------------------------
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 #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
codecov-io edited a comment on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775714401
# [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=h1) Report
> Merging [#1452](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=desc) (28ed890) into [master](https://codecov.io/gh/apache/apisix-dashboard/commit/8796fa1b00617f83b495798cc7b86418d1456533?el=desc) (8796fa1) will **decrease** coverage by `7.54%`.
> The diff coverage is `100.00%`.
[![Impacted file tree graph](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/graphs/tree.svg?width=650&height=150&src=pr&token=Q1HERXN96P)](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=tree)
```diff
@@ Coverage Diff @@
## master #1452 +/- ##
==========================================
- Coverage 67.61% 60.07% -7.55%
==========================================
Files 48 48
Lines 3042 3038 -4
==========================================
- Hits 2057 1825 -232
- Misses 741 973 +232
+ Partials 244 240 -4
```
| [Impacted Files](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [api/internal/handler/upstream/upstream.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvdXBzdHJlYW0vdXBzdHJlYW0uZ28=) | `86.91% <100.00%> (+3.86%)` | :arrow_up: |
| [api/internal/core/entity/entity.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvZW50aXR5L2VudGl0eS5nbw==) | `18.75% <0.00%> (-81.25%)` | :arrow_down: |
| [...l/handler/route\_online\_debug/route\_online\_debug.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvcm91dGVfb25saW5lX2RlYnVnL3JvdXRlX29ubGluZV9kZWJ1Zy5nbw==) | `7.14% <0.00%> (-66.67%)` | :arrow_down: |
| [api/internal/handler/data\_loader/route\_import.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvZGF0YV9sb2FkZXIvcm91dGVfaW1wb3J0Lmdv) | `30.24% <0.00%> (-34.68%)` | :arrow_down: |
| [api/internal/log/log.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2xvZy9sb2cuZ28=) | `30.00% <0.00%> (-30.00%)` | :arrow_down: |
| [api/internal/utils/consts/api\_error.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL3V0aWxzL2NvbnN0cy9hcGlfZXJyb3IuZ28=) | `25.00% <0.00%> (-25.00%)` | :arrow_down: |
| [api/internal/core/store/storehub.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc3RvcmVodWIuZ28=) | `45.91% <0.00%> (-24.49%)` | :arrow_down: |
| [api/internal/utils/json\_patch.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL3V0aWxzL2pzb25fcGF0Y2guZ28=) | `34.48% <0.00%> (-24.14%)` | :arrow_down: |
| [api/internal/filter/schema.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2ZpbHRlci9zY2hlbWEuZ28=) | `31.93% <0.00%> (-23.53%)` | :arrow_down: |
| [api/internal/handler/global\_rule/global\_rule.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvZ2xvYmFsX3J1bGUvZ2xvYmFsX3J1bGUuZ28=) | `66.12% <0.00%> (-17.75%)` | :arrow_down: |
| ... and [13 more](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree-more) | |
------
[Continue to review full report at Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?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/1452?src=pr&el=footer). Last update [8796fa1...28ed890](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?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] codecov-io commented on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
codecov-io commented on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775714401
# [Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=h1) Report
> Merging [#1452](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=desc) (28ed890) into [master](https://codecov.io/gh/apache/apisix-dashboard/commit/8796fa1b00617f83b495798cc7b86418d1456533?el=desc) (8796fa1) will **decrease** coverage by `15.98%`.
> The diff coverage is `100.00%`.
[![Impacted file tree graph](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/graphs/tree.svg?width=650&height=150&src=pr&token=Q1HERXN96P)](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=tree)
```diff
@@ Coverage Diff @@
## master #1452 +/- ##
===========================================
- Coverage 67.61% 51.63% -15.99%
===========================================
Files 48 39 -9
Lines 3042 2597 -445
===========================================
- Hits 2057 1341 -716
- Misses 741 1082 +341
+ Partials 244 174 -70
```
| [Impacted Files](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [api/internal/handler/upstream/upstream.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvdXBzdHJlYW0vdXBzdHJlYW0uZ28=) | `66.35% <100.00%> (-16.70%)` | :arrow_down: |
| [api/internal/utils/version.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL3V0aWxzL3ZlcnNpb24uZ28=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [api/internal/filter/request\_id.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2ZpbHRlci9yZXF1ZXN0X2lkLmdv) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [api/internal/core/entity/entity.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvZW50aXR5L2VudGl0eS5nbw==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [api/internal/core/store/storehub.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2NvcmUvc3RvcmUvc3RvcmVodWIuZ28=) | `0.00% <0.00%> (-70.41%)` | :arrow_down: |
| [api/internal/filter/cors.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2ZpbHRlci9jb3JzLmdv) | `0.00% <0.00%> (-66.67%)` | :arrow_down: |
| [api/internal/filter/schema.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2ZpbHRlci9zY2hlbWEuZ28=) | `0.00% <0.00%> (-55.47%)` | :arrow_down: |
| [api/internal/utils/consts/api\_error.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL3V0aWxzL2NvbnN0cy9hcGlfZXJyb3IuZ28=) | `0.00% <0.00%> (-50.00%)` | :arrow_down: |
| [api/internal/handler/data\_loader/route\_import.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvZGF0YV9sb2FkZXIvcm91dGVfaW1wb3J0Lmdv) | `27.41% <0.00%> (-37.50%)` | :arrow_down: |
| [api/internal/handler/server\_info/server\_info.go](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree#diff-YXBpL2ludGVybmFsL2hhbmRsZXIvc2VydmVyX2luZm8vc2VydmVyX2luZm8uZ28=) | `57.14% <0.00%> (-33.34%)` | :arrow_down: |
| ... and [33 more](https://codecov.io/gh/apache/apisix-dashboard/pull/1452/diff?src=pr&el=tree-more) | |
------
[Continue to review full report at Codecov](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?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/1452?src=pr&el=footer). Last update [8796fa1...28ed890](https://codecov.io/gh/apache/apisix-dashboard/pull/1452?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] Jaycean commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571888603
##########
File path: api/internal/handler/upstream/upstream.go
##########
@@ -270,27 +267,15 @@ func (h *Handler) Exist(c droplet.Context) (interface{}, error) {
return nil, err
}
- sort := store.NewSort(nil)
- filter := store.NewFilter([]string{"name", name})
- pagination := store.NewPagination(0, 0)
- query := store.NewQuery(sort, filter, pagination)
- rows := store.NewFilterSelector(toRows(ret), query)
Review comment:
I suggest using a new issue to record this problem. Later, I will check whether these methods are used in other places, and submit a new PR to delete them. If my idea is wrong, please correct me.
----------------------------------------------------------------
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] imjoey commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
imjoey commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r571786346
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Key: "server_addr2",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u3",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream3",
+ Key: "server_addr3",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ }
- upstreamHandler = &Handler{
- upstreamStore: store.GetStore(store.HubKeyUpstream),
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Upstream
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all upstream",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ mockData[1],
+ mockData[2],
+ },
+ TotalSize: 3,
+ },
+ },
+ {
+ caseDesc: "list upstream with 'upstream1'",
+ giveInput: &ListInput{
+ Name: "upstream1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ },
+ TotalSize: 1,
+ },
+ },
}
- assert.NotNil(t, upstreamHandler)
-
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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 mockData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
+ returnData = append(returnData, input.Format(c))
+ }
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
-
- //update
- upstream2 := &UpdateInput{}
- upstream2.ID = "1"
- reqBody = `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "enable_websocket": true,
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream2)
- assert.Nil(t, err)
- ctx.SetInput(upstream2)
- ret, err = upstreamHandler.Update(ctx)
- assert.Nil(t, err)
- // check the returned value
- objRet, ok = ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, upstream2.ID, objRet.ID)
-
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := upstreamHandler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ }, tc.giveErr)
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstream_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Upstream
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: nil,
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestUpstream_Pass_Host(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "2",
- "nodes": [{
- "host": "httpbin.org",
- "port": 80,
- "weight": 1
- }],
- "type": "roundrobin",
- "pass_host": "node"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "2", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "2"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
+func TestUpstream_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "update success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (u1) doesn't match ID on body (u2)"),
+ },
+ }
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "2"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.True(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+ h := Handler{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 TestUpstream_Patch_Update(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "3",
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1980,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "3", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- reqBody1 := `{
- "nodes": [{
- "host": "172.16.238.20",
- "port": 1981,
- "weight": 1
- }],
- "type": "roundrobin"
- }`
- responesBody := `"nodes":[{"host":"172.16.238.20","port":1981,"weight":1}],"type":"roundrobin"}`
-
- input2 := &PatchInput{}
- input2.ID = "3"
- input2.SubPath = ""
- input2.Body = []byte(reqBody1)
- ctx.SetInput(input2)
-
- ret2, err := upstreamHandler.Patch(ctx)
- assert.Nil(t, err)
- _ret2, err := json.Marshal(ret2)
- assert.Nil(t, err)
- isContains := strings.Contains(string(_ret2), responesBody)
- assert.True(t, isContains)
+func TestUpstream_Patch(t *testing.T) {
+ existUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
- //delete test data
- inputDel2 := &BatchDelete{}
- reqBody = `{"ids": "3"}`
- err = json.Unmarshal([]byte(reqBody), inputDel2)
- assert.Nil(t, err)
- ctx.SetInput(inputDel2)
- _, err = upstreamHandler.BatchDelete(ctx)
+ patchUpstream := &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ }
+ patchUpstreamBytes, err := json.Marshal(patchUpstream)
assert.Nil(t, err)
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *PatchInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "patch success",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "",
+ Body: patchUpstreamBytes,
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr2",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch success by path",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "/nodes",
+ Body: []byte(`[{"host": "172.16.238.20","port": 1981,"weight": 1}]`),
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(15),
+ "send": float64(15),
+ "read": float64(15),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": float64(2),
+ "successes": float64(1),
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": float64(1),
+ "http_failures": float64(2),
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": float64(20),
+ "send": float64(20),
+ "read": float64(20),
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr_patch",
+ Nodes: []interface{}{
+ map[string]interface{}{
+ "host": "172.16.238.20",
+ "port": float64(1981),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ getCalled: true,
+ },
+ {
+ caseDesc: "patch failed, path error",
+ giveInput: &PatchInput{
+ ID: "u1",
+ SubPath: "error",
+ Body: []byte("0"),
+ },
+ wantRet: handler.SpecCodeResponse(
+ errors.New("add operation does not apply: doc is missing path: \"error\": missing value")),
+ wantErr: errors.New("add operation does not apply: doc is missing path: \"error\": missing value"),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Get", mock.Anything, mock.Anything).Return(existUpstream, nil)
+ upstreamStore.On("Update", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ createIfNotExist := args.Get(2).(bool)
+ assert.Equal(t, tc.wantInput, input)
+ assert.False(t, createIfNotExist)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Patch(ctx)
+ assert.Equal(t, tc.getCalled, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ if tc.wantErr != nil && err != nil {
+ assert.Error(t, tc.wantErr.(error), err.Error())
+ } else {
+ assert.Equal(t, tc.wantErr, err)
+ }
Review comment:
@Jaycean I'm a little confused. Could we use `assert.Equal(t, tc.wantErr, err)` here? Is this only for pretty logs for developers? Thanks.
----------------------------------------------------------------
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] Jaycean commented on a change in pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
Jaycean commented on a change in pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#discussion_r572632768
##########
File path: api/internal/handler/upstream/upstream_test.go
##########
@@ -19,285 +19,1719 @@ package upstream
import (
"encoding/json"
- "strings"
+ "errors"
+ "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"
+ "github.com/apisix/manager-api/internal/utils/consts"
)
-var upstreamHandler *Handler
+func TestUpstream_Get(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ giveInput *GetInput
+ giveRet *entity.Upstream
+ giveErr error
+ wantErr error
+ wantGetKey string
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "upstream: get success",
+ giveInput: &GetInput{ID: "u1"},
+ wantGetKey: "u1",
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ 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,
+ },
+ },
+ }
-func TestUpstream(t *testing.T) {
- // init
- err := storage.InitETCDClient(conf.ETCDConfig)
- assert.Nil(t, err)
- err = store.InitStores()
- assert.Nil(t, err)
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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)
+
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstreams_List(t *testing.T) {
+ mockData := []*entity.Upstream{
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream2",
+ Key: "server_addr2",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ {
+ BaseInfo: entity.BaseInfo{
+ ID: "u3",
+ CreateTime: 1609340491,
+ UpdateTime: 1609340491,
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream3",
+ Key: "server_addr3",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ }
- upstreamHandler = &Handler{
- upstreamStore: store.GetStore(store.HubKeyUpstream),
+ tests := []struct {
+ caseDesc string
+ giveInput *ListInput
+ giveData []*entity.Upstream
+ giveErr error
+ wantErr error
+ wantInput store.ListInput
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "list all upstream",
+ giveInput: &ListInput{
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ mockData[1],
+ mockData[2],
+ },
+ TotalSize: 3,
+ },
+ },
+ {
+ caseDesc: "list upstream with 'upstream1'",
+ giveInput: &ListInput{
+ Name: "upstream1",
+ Pagination: store.Pagination{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ },
+ wantInput: store.ListInput{
+ PageSize: 10,
+ PageNumber: 10,
+ },
+ wantRet: &store.ListOutput{
+ Rows: []interface{}{
+ mockData[0],
+ },
+ TotalSize: 1,
+ },
+ },
}
- assert.NotNil(t, upstreamHandler)
-
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := true
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.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 mockData {
+ if input.Predicate(c) {
+ if input.Format == nil {
+ returnData = append(returnData, c)
+ continue
+ }
+ returnData = append(returnData, input.Format(c))
+ }
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "1", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(100) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "1"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
-
- //update
- upstream2 := &UpdateInput{}
- upstream2.ID = "1"
- reqBody = `{
- "id": "1",
- "name": "upstream3",
- "description": "upstream upstream",
- "type": "roundrobin",
- "nodes": [{
- "host": "a.a.com",
- "port": 80,
- "weight": 1
- }],
- "timeout":{
- "connect":15,
- "send":15,
- "read":15
- },
- "enable_websocket": true,
- "hash_on": "header",
- "key": "server_addr",
- "checks": {
- "active": {
- "timeout": 5,
- "http_path": "/status",
- "host": "foo.com",
- "healthy": {
- "interval": 2,
- "successes": 1
- },
- "unhealthy": {
- "interval": 1,
- "http_failures": 2
- },
- "req_headers": ["User-Agent: curl/7.29.0"]
- },
- "passive": {
- "healthy": {
- "http_statuses": [200, 201],
- "successes": 3
- },
- "unhealthy": {
- "http_statuses": [500],
- "http_failures": 3,
- "tcp_failures": 3
+ return &store.ListOutput{
+ Rows: returnData,
+ TotalSize: len(returnData),
}
- }
- }
- }`
- err = json.Unmarshal([]byte(reqBody), upstream2)
- assert.Nil(t, err)
- ctx.SetInput(upstream2)
- ret, err = upstreamHandler.Update(ctx)
- assert.Nil(t, err)
- // check the returned value
- objRet, ok = ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, upstream2.ID, objRet.ID)
-
- //list
- listInput := &ListInput{}
- reqBody = `{"page_size": 1, "page": 1}`
- err = json.Unmarshal([]byte(reqBody), listInput)
- assert.Nil(t, err)
- ctx.SetInput(listInput)
- retPage, err := upstreamHandler.List(ctx)
- assert.Nil(t, err)
- dataPage := retPage.(*store.ListOutput)
- assert.Equal(t, len(dataPage.Rows), 1)
+ }, tc.giveErr)
- //delete test data
- inputDel := &BatchDelete{}
- reqBody = `{"ids": "1"}`
- err = json.Unmarshal([]byte(reqBody), inputDel)
- assert.Nil(t, err)
- ctx.SetInput(inputDel)
- _, err = upstreamHandler.BatchDelete(ctx)
- assert.Nil(t, err)
+ h := Handler{upstreamStore: upstreamStore}
+ 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)
+ })
+ }
+}
+
+func TestUpstream_Create(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *entity.Upstream
+ giveRet interface{}
+ giveErr error
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "create success",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: nil,
+ },
+ {
+ caseDesc: "create failed, create return error",
+ getCalled: true,
+ giveInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ giveErr: fmt.Errorf("create failed"),
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": float64(3),
+ "tcp_failures": float64(3),
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantErr: fmt.Errorf("create failed"),
+ wantRet: handler.SpecCodeResponse(fmt.Errorf("create failed")),
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.caseDesc, func(t *testing.T) {
+ getCalled := false
+
+ upstreamStore := &store.MockInterface{}
+ upstreamStore.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+ getCalled = true
+ input := args.Get(1).(*entity.Upstream)
+ assert.Equal(t, tc.wantInput, input)
+ }).Return(tc.giveRet, tc.giveErr)
+
+ h := Handler{upstreamStore: upstreamStore}
+
+ ctx := droplet.NewContext()
+ ctx.SetInput(tc.giveInput)
+ ret, err := h.Create(ctx)
+ assert.True(t, getCalled)
+ assert.Equal(t, tc.wantRet, ret)
+ assert.Equal(t, tc.wantErr, err)
+ })
+ }
}
-func TestUpstream_Pass_Host(t *testing.T) {
- //create
- ctx := droplet.NewContext()
- upstream := &entity.Upstream{}
- reqBody := `{
- "id": "2",
- "nodes": [{
- "host": "httpbin.org",
- "port": 80,
- "weight": 1
- }],
- "type": "roundrobin",
- "pass_host": "node"
- }`
- err := json.Unmarshal([]byte(reqBody), upstream)
- assert.Nil(t, err)
- ctx.SetInput(upstream)
- ret, err := upstreamHandler.Create(ctx)
- assert.Nil(t, err)
- objRet, ok := ret.(*entity.Upstream)
- assert.True(t, ok)
- assert.Equal(t, "2", objRet.ID)
-
- //sleep
- time.Sleep(time.Duration(20) * time.Millisecond)
-
- //get
- input := &GetInput{}
- input.ID = "2"
- ctx.SetInput(input)
- ret, err = upstreamHandler.Get(ctx)
- stored := ret.(*entity.Upstream)
- assert.Nil(t, err)
- assert.Equal(t, stored.ID, upstream.ID)
+func TestUpstream_Update(t *testing.T) {
+ tests := []struct {
+ caseDesc string
+ getCalled bool
+ giveInput *UpdateInput
+ giveErr error
+ giveRet interface{}
+ wantInput *entity.Upstream
+ wantErr error
+ wantRet interface{}
+ }{
+ {
+ caseDesc: "update success",
+ getCalled: true,
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ giveRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantInput: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ wantRet: &entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u1",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ {
+ caseDesc: "create failed, different id",
+ giveInput: &UpdateInput{
+ ID: "u1",
+ Upstream: entity.Upstream{
+ BaseInfo: entity.BaseInfo{
+ ID: "u2",
+ },
+ UpstreamDef: entity.UpstreamDef{
+ Name: "upstream1",
+ Timeout: map[string]interface{}{
+ "connect": 15,
+ "send": 15,
+ "read": 15,
+ },
+ Checks: map[string]interface{}{
+ "active": map[string]interface{}{
+ "timeout": float64(5),
+ "http_path": "/status",
+ "host": "foo.com",
+ "healthy": map[string]interface{}{
+ "interval": 2,
+ "successes": 1,
+ },
+ "unhealthy": map[string]interface{}{
+ "interval": 1,
+ "http_failures": 2,
+ },
+ "req_headers": []interface{}{"User-Agent: curl/7.29.0"},
+ },
+ "passive": map[string]interface{}{
+ "healthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(200), float64(201)},
+ "successes": float64(3),
+ },
+ "unhealthy": map[string]interface{}{
+ "http_statuses": []interface{}{float64(500)},
+ "http_failures": 3,
+ "tcp_failures": 3,
+ },
+ },
+ },
+ Key: "server_addr",
+ Nodes: []map[string]interface{}{
+ {
+ "host": "39.97.63.215",
+ "port": float64(80),
+ "weight": float64(1),
+ },
+ },
+ },
+ },
+ },
+ wantRet: &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+ wantErr: fmt.Errorf("ID on path (u1) doesn't match ID on body (u2)"),
+ },
Review comment:
Update I added a test case that could not be updated due to ID matching failure, but my description is wrong and has been 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
[GitHub] [apisix-dashboard] nic-chen commented on pull request #1452: feat(be): refactor upstream unit test
Posted by GitBox <gi...@apache.org>.
nic-chen commented on pull request #1452:
URL: https://github.com/apache/apisix-dashboard/pull/1452#issuecomment-775062816
@Jaycean What is the current code coverage of `upstream.go`? It's a little strange that this pr doesn't show coverage
----------------------------------------------------------------
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