package cloudmap import ( "context" "github.com/aws/aws-sdk-go/service/servicediscovery" "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/util/cache" "testing" "time" ) func Test_defaultInstancesCache_cloneInstanceAttributesByID(t *testing.T) { type args struct { instanceAttrsByID map[string]instanceAttributes } tests := []struct { name string args args want map[string]instanceAttributes }{ { name: "when it's non-empty", args: args{ instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { "AWS_INIT_HEALTH_STATUS": "HEALTHY", "AWS_INSTANCE_IPV4": "192.168.1.1", "k8s.io/pod": "pod1", "k8s.io/namespace": "pod-ns", }, }, }, want: map[string]instanceAttributes{ "192.168.1.1": { "AWS_INIT_HEALTH_STATUS": "HEALTHY", "AWS_INSTANCE_IPV4": "192.168.1.1", "k8s.io/pod": "pod1", "k8s.io/namespace": "pod-ns", }, }, }, { name: "when it's nil", args: args{ instanceAttrsByID: nil, }, want: map[string]instanceAttributes{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &defaultInstancesCache{} got := c.cloneInstanceAttributesByID(tt.args.instanceAttrsByID) assert.Equal(t, tt.want, got) }) } } func Test_defaultInstancesCache_recordSuccessfulRegisterInstanceOperation(t *testing.T) { now := time.Now() oneSecAfterNow := now.Add(1 * time.Second) oneSecBeforeNow := now.Add(-1 * time.Second) type fields struct { instancesAttrsCacheItemByService map[string]*instancesAttrsCacheItem } type args struct { serviceID string instanceID string attrs instanceAttributes operation *servicediscovery.Operation } tests := []struct { name string fields fields args args wantInstanceAttrsByService map[string]map[string]instanceAttributes }{ { name: "when cache for service presents and updateTime pasts lastUpdateTime, should update cache", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, }, lastUpdatedTimeByID: map[string]time.Time{ "192.168.1.1": now, }, }, }, }, args: args{ serviceID: "service-A", instanceID: "192.168.1.1", attrs: instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", "k": "v", }, operation: &servicediscovery.Operation{ UpdateDate: &oneSecAfterNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", "k": "v", }, }, }, }, { name: "when cache for service presents and lastUpdateTime empty, should update cache", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, }, lastUpdatedTimeByID: make(map[string]time.Time), }, }, }, args: args{ serviceID: "service-A", instanceID: "192.168.1.1", attrs: instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", "k": "v", }, operation: &servicediscovery.Operation{ UpdateDate: &oneSecAfterNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", "k": "v", }, }, }, }, { name: "when cache for service presents and updateTime before lastUpdateTime, should be no-op", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, }, lastUpdatedTimeByID: map[string]time.Time{ "192.168.1.1": now, }, }, }, }, args: args{ serviceID: "service-A", instanceID: "192.168.1.1", attrs: instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", "k": "v", }, operation: &servicediscovery.Operation{ UpdateDate: &oneSecBeforeNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", }, }, }, }, { name: "when cache for service presents and updateTime pasts lastUpdateTime, should add new items into cache", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, }, lastUpdatedTimeByID: map[string]time.Time{ "192.168.1.1": now, }, }, }, }, args: args{ serviceID: "service-A", instanceID: "192.168.1.2", attrs: instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.2", }, operation: &servicediscovery.Operation{ UpdateDate: &oneSecAfterNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", }, "192.168.1.2": instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.2", }, }, }, }, { name: "when cache for service absents, should be no-op", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, }, }, }, }, args: args{ serviceID: "service-B", instanceID: "192.168.1.1", attrs: instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", "k": "v", }, operation: nil, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": instanceAttributes{ AttrAWSInstanceIPV4: "192.168.1.1", }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instancesAttrsCache := cache.NewLRUExpireCache(defaultInstanceAttrsCacheSize) c := &defaultInstancesCache{ instancesAttrsCache: instancesAttrsCache, } for serviceID, cacheItem := range tt.fields.instancesAttrsCacheItemByService { instancesAttrsCache.Add(serviceID, cacheItem, defaultInstanceAttrsCacheTTL) } c.recordSuccessfulRegisterInstanceOperation(tt.args.serviceID, tt.args.instanceID, tt.args.attrs, tt.args.operation) for serviceID, wantInstanceAttrsByID := range tt.wantInstanceAttrsByService { gotInstanceAttrsByID, err := c.ListInstances(context.Background(), serviceID) assert.NoError(t, err) assert.Equal(t, wantInstanceAttrsByID, gotInstanceAttrsByID) } }) } } func Test_defaultInstancesCache_recordSuccessfulDeregisterInstanceOperation(t *testing.T) { now := time.Now() oneSecAfterNow := now.Add(1 * time.Second) oneSecBeforeNow := now.Add(-1 * time.Second) type fields struct { instancesAttrsCacheItemByService map[string]*instancesAttrsCacheItem } type args struct { serviceID string instanceID string operation *servicediscovery.Operation } tests := []struct { name string fields fields args args wantInstanceAttrsByService map[string]map[string]instanceAttributes }{ { name: "when cache for service presents and updateTime pasts lastUpdateTime, should remove from cache", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, "192.168.1.2": { AttrAWSInstanceIPV4: "192.168.1.2", }, }, lastUpdatedTimeByID: map[string]time.Time{ "192.168.1.1": now, "192.168.1.2": now, }, }, }, }, args: args{ serviceID: "service-A", instanceID: "192.168.1.1", operation: &servicediscovery.Operation{ UpdateDate: &oneSecAfterNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.2": { AttrAWSInstanceIPV4: "192.168.1.2", }, }, }, }, { name: "when cache for service presents and updateTime before lastUpdateTime, should be no-op", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, "192.168.1.2": { AttrAWSInstanceIPV4: "192.168.1.2", }, }, lastUpdatedTimeByID: map[string]time.Time{ "192.168.1.1": now, "192.168.1.2": now, }, }, }, }, args: args{ serviceID: "service-A", instanceID: "192.168.1.1", operation: &servicediscovery.Operation{ UpdateDate: &oneSecBeforeNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, "192.168.1.2": { AttrAWSInstanceIPV4: "192.168.1.2", }, }, }, }, { name: "when cache for service absents, should be no-op", fields: fields{ instancesAttrsCacheItemByService: map[string]*instancesAttrsCacheItem{ "service-A": { instanceAttrsByID: map[string]instanceAttributes{ "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, "192.168.1.2": { AttrAWSInstanceIPV4: "192.168.1.2", }, }, lastUpdatedTimeByID: map[string]time.Time{ "192.168.1.1": now, "192.168.1.2": now, }, }, }, }, args: args{ serviceID: "service-B", instanceID: "192.168.1.1", operation: &servicediscovery.Operation{ UpdateDate: &oneSecAfterNow, }, }, wantInstanceAttrsByService: map[string]map[string]instanceAttributes{ "service-A": { "192.168.1.1": { AttrAWSInstanceIPV4: "192.168.1.1", }, "192.168.1.2": { AttrAWSInstanceIPV4: "192.168.1.2", }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { instancesAttrsCache := cache.NewLRUExpireCache(defaultInstanceAttrsCacheSize) c := &defaultInstancesCache{ instancesAttrsCache: instancesAttrsCache, } for serviceID, cacheItem := range tt.fields.instancesAttrsCacheItemByService { instancesAttrsCache.Add(serviceID, cacheItem, defaultInstanceAttrsCacheTTL) } c.recordSuccessfulDeregisterInstanceOperation(tt.args.serviceID, tt.args.instanceID, tt.args.operation) for serviceID, wantInstanceAttrsByID := range tt.wantInstanceAttrsByService { gotInstanceAttrsByID, err := c.ListInstances(context.Background(), serviceID) assert.NoError(t, err) assert.Equal(t, wantInstanceAttrsByID, gotInstanceAttrsByID) } }) } }