Files
reproxy/app/discovery/provider/consulcatalog/consulcatalog_test.go
Umputun a896f08eec add middleware to optionally allow requests from giving ips/ranges
add new remote param to docker and file providers

lint: http nil body

add support of remote ips to consul provider

local implementation of onlyfrom middleware

lint: missing comment

make proxy tests more readable

preffer public IP if any forwwarded
2023-11-26 16:17:04 -06:00

310 lines
7.3 KiB
Go

package consulcatalog
import (
"context"
"fmt"
"sort"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/umputun/reproxy/app/discovery"
)
func TestNew(t *testing.T) {
cc := New(&ConsulClientMock{GetFunc: func() ([]consulService, error) {
return nil, nil
}}, time.Second)
assert.IsType(t, &ConsulCatalog{}, cc)
assert.Equal(t, time.Second, cc.refreshInterval)
}
func TestConsulCatalog_List_error(t *testing.T) {
clientMock := &ConsulClientMock{GetFunc: func() ([]consulService, error) {
return nil, fmt.Errorf("err1")
}}
cc := &ConsulCatalog{client: clientMock}
_, err := cc.List()
require.Error(t, err)
assert.Equal(t, "error get services list, err1", err.Error())
}
func TestConsulCatalog_List(t *testing.T) {
clientMock := &ConsulClientMock{GetFunc: func() ([]consulService, error) {
return []consulService{
{
ServiceID: "id0",
ServiceName: "name0",
ServiceAddress: "addr0",
ServicePort: 1000,
Labels: map[string]string{"foo.bar": "baz"},
},
{
ServiceID: "id1",
ServiceName: "name1",
ServiceAddress: "addr1",
ServicePort: 1000,
Labels: map[string]string{"reproxy.enabled": "false"},
},
{
ServiceID: "id2",
ServiceName: "name2",
ServiceAddress: "addr2",
ServicePort: 2000,
Labels: map[string]string{"reproxy.enabled": "true"},
},
{
ServiceID: "id3",
ServiceName: "name3",
ServiceAddress: "addr3",
ServicePort: 3000,
Labels: map[string]string{"reproxy.route": "^/api/123/(.*)", "reproxy.dest": "/blah/$1",
"reproxy.server": "example.com,domain.com", "reproxy.ping": "/ping",
"reproxy.enabled": "yes", "reproxy.remote": "127.0.0.1, 192.168.1.0/24"},
},
{
ServiceID: "id4",
ServiceName: "name44",
ServiceAddress: "addr44",
ServicePort: 4000,
Labels: map[string]string{"reproxy.enabled": "1"},
},
}, nil
}}
cc := &ConsulCatalog{
client: clientMock,
}
res, err := cc.List()
require.NoError(t, err)
require.Equal(t, 4, len(res))
// sort slice for exclude random item positions after sorting by SrtMatch in List function
sort.Slice(res, func(i, j int) bool {
return len(res[i].Dst+res[i].Server) > len(res[j].Dst+res[j].Server)
})
assert.Equal(t, "^/api/123/(.*)", res[0].SrcMatch.String())
assert.Equal(t, "http://addr3:3000/blah/$1", res[0].Dst)
assert.Equal(t, "example.com", res[0].Server)
assert.Equal(t, "http://addr3:3000/ping", res[0].PingURL)
assert.Equal(t, []string{"127.0.0.1", "192.168.1.0/24"}, res[0].OnlyFromIPs)
assert.Equal(t, "^/api/123/(.*)", res[1].SrcMatch.String())
assert.Equal(t, "http://addr3:3000/blah/$1", res[1].Dst)
assert.Equal(t, "domain.com", res[1].Server)
assert.Equal(t, "http://addr3:3000/ping", res[1].PingURL)
assert.Equal(t, []string{"127.0.0.1", "192.168.1.0/24"}, res[1].OnlyFromIPs)
assert.Equal(t, "^/(.*)", res[2].SrcMatch.String())
assert.Equal(t, "http://addr44:4000/$1", res[2].Dst)
assert.Equal(t, "http://addr44:4000/ping", res[2].PingURL)
assert.Equal(t, "*", res[2].Server)
assert.Equal(t, []string{}, res[2].OnlyFromIPs)
assert.Equal(t, "^/(.*)", res[3].SrcMatch.String())
assert.Equal(t, "http://addr2:2000/$1", res[3].Dst)
assert.Equal(t, "http://addr2:2000/ping", res[3].PingURL)
assert.Equal(t, "*", res[3].Server)
assert.Equal(t, []string{}, res[3].OnlyFromIPs)
}
func TestConsulCatalog_serviceListWasChanged(t *testing.T) {
type fields struct {
list map[string]struct{}
}
type args struct {
services []consulService
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{
name: "empty, not changed",
fields: fields{
list: map[string]struct{}{},
},
args: args{
services: []consulService{},
},
want: false,
},
{
name: "not changed",
fields: fields{
list: map[string]struct{}{"1": {}, "2": {}, "3": {}},
},
args: args{
services: []consulService{{ServiceID: "1"}, {ServiceID: "3"}, {ServiceID: "2"}},
},
want: false,
},
{
name: "changed",
fields: fields{
list: map[string]struct{}{"1": {}, "2": {}, "3": {}},
},
args: args{
services: []consulService{{ServiceID: "1"}, {ServiceID: "100"}, {ServiceID: "2"}},
},
want: true,
},
{
name: "new service, changed",
fields: fields{
list: map[string]struct{}{"1": {}, "2": {}, "3": {}},
},
args: args{
services: []consulService{{ServiceID: "1"}, {ServiceID: "3"}, {ServiceID: "2"}, {ServiceID: "4"}},
},
want: true,
},
{
name: "remove service, changed",
fields: fields{
list: map[string]struct{}{"1": {}, "2": {}, "3": {}},
},
args: args{
services: []consulService{{ServiceID: "1"}, {ServiceID: "3"}},
},
want: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
cc := &ConsulCatalog{
list: tt.fields.list,
}
if got := cc.serviceListWasChanged(tt.args.services); got != tt.want {
t.Errorf("serviceListWasChanged() = %v, want %v", got, tt.want)
}
})
}
}
func TestConsulCatalog_updateServices(t *testing.T) {
cc := &ConsulCatalog{
list: map[string]struct{}{
"3": {},
"100": {},
},
}
cc.updateServices([]consulService{{ServiceID: "1"}, {ServiceID: "2"}, {ServiceID: "3"}})
require.Equal(t, 3, len(cc.list))
_, ok := cc.list["1"]
assert.True(t, ok)
_, ok = cc.list["2"]
assert.True(t, ok)
_, ok = cc.list["3"]
assert.True(t, ok)
}
func TestConsulCatalog_checkUpdates_http_error(t *testing.T) {
clientMock := &ConsulClientMock{
GetFunc: func() ([]consulService, error) {
return nil, fmt.Errorf("err1")
},
}
cc := &ConsulCatalog{
client: clientMock,
}
err := cc.checkUpdates(nil)
require.Error(t, err)
assert.Equal(t, "unable to get services list, err1", err.Error())
}
func TestConsulCatalog_checkUpdates_not_changed(t *testing.T) {
clientMock := &ConsulClientMock{
GetFunc: func() ([]consulService, error) {
return nil, nil
},
}
cc := &ConsulCatalog{
client: clientMock,
}
err := cc.checkUpdates(nil)
require.NoError(t, err)
assert.Equal(t, 0, len(cc.list))
}
func TestConsulCatalog_checkUpdates_changed(t *testing.T) {
clientMock := &ConsulClientMock{
GetFunc: func() ([]consulService, error) {
return []consulService{{ServiceID: "1"}}, nil
},
}
cc := &ConsulCatalog{
list: map[string]struct{}{
"2": {},
},
client: clientMock,
}
ch := make(chan discovery.ProviderID, 1)
err := cc.checkUpdates(ch)
require.NoError(t, err)
assert.Equal(t, 1, len(cc.list))
_, ok := cc.list["1"]
assert.True(t, ok)
s, ok := <-ch
assert.True(t, ok)
assert.Equal(t, discovery.PIConsulCatalog, s)
}
func TestConsulCatalog_Events(t *testing.T) {
clientMock := &ConsulClientMock{
GetFunc: func() ([]consulService, error) {
return []consulService{
{
ServiceID: "1",
Labels: map[string]string{"reproxy.enabled": "1"},
},
}, nil
},
}
cc := &ConsulCatalog{
list: map[string]struct{}{
"2": {},
},
client: clientMock,
refreshInterval: time.Millisecond,
}
ch := cc.Events(context.Background())
var s discovery.ProviderID
select {
case s = <-ch:
case <-time.After(time.Millisecond * 5):
t.Fatal("not received event")
return
}
assert.Equal(t, discovery.PIConsulCatalog, s)
list, err := cc.List()
require.NoError(t, err)
assert.Equal(t, 1, len(list))
}