stackube/pkg/service-controller/service_controller_test.go
mozhulee 604dd5b0c9 Add service-controller unit tests
Change-Id: I3fd5bc4b62d26c7e6d118ba08b6c001c871fcbd3
Implements: blueprint service-controller-test
Signed-off-by: mozhuli <21621232@zju.edu.cn>
2017-08-28 09:43:43 +08:00

830 lines
24 KiB
Go

/*
Copyright (c) 2017 OpenStack Foundation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package service
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"git.openstack.org/openstack/stackube/pkg/openstack"
drivertypes "git.openstack.org/openstack/stackube/pkg/openstack/types"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
restclient "k8s.io/client-go/rest"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
)
const (
LoadBalancerExist = "LoadBalancerExist"
EnsureLoadBalancerDeleted = "EnsureLoadBalancerDeleted"
)
func newService(name string, uid types.UID, serviceType v1.ServiceType) *v1.Service {
return &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "default",
UID: uid,
SelfLink: testapi.Default.SelfLink("services", name),
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{{
Port: 80,
}},
ExternalIPs: []string{
"1.1.1.1",
},
Type: serviceType,
},
}
}
//Wrap newService so that you dont have to call default argumetns again and again.
func defaultExternalService() *v1.Service {
return newService("external-balancer", types.UID("123"), v1.ServiceTypeLoadBalancer)
}
func defaultNetwork() *drivertypes.Network {
subnets := []*drivertypes.Subnet{{Uid: "123"}}
return &drivertypes.Network{
Name: "kube-default-default",
TenantID: "123",
Subnets: subnets,
}
}
func makeTestServer(t *testing.T, namespace string) (*httptest.Server, *utiltesting.FakeHandler) {
fakeEndpointsHandler := utiltesting.FakeHandler{
StatusCode: http.StatusOK,
ResponseBody: runtime.EncodeOrDie(testapi.Default.Codec(), &v1.Endpoints{}),
}
mux := http.NewServeMux()
mux.Handle(testapi.Default.ResourcePath("endpoints/", namespace, ""), &fakeEndpointsHandler)
mux.Handle(testapi.Default.ResourcePath("services/", namespace, ""), &fakeEndpointsHandler)
mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
t.Errorf("unexpected request: %v", req.RequestURI)
res.WriteHeader(http.StatusNotFound)
})
return httptest.NewServer(mux), &fakeEndpointsHandler
}
func newController() (*ServiceController, *openstack.FakeOSClient, *fake.Clientset) {
osClient := openstack.NewFake(nil)
client := fake.NewSimpleClientset()
controller, _ := NewServiceController(client, osClient)
return controller, osClient, client
}
func newControllerFakeHTTPServer(url, svcName, namespace string) (*ServiceController, *openstack.FakeOSClient) {
osClient := openstack.NewFake(nil)
client := kubernetes.NewForConfigOrDie(&restclient.Config{Host: url, ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(v1.GroupName).GroupVersion}})
controller, _ := NewServiceController(client, osClient)
// Sets fake network.
osClient.SetNetwork(defaultNetwork())
// Injects fake endpoint.
controller.factory.Core().V1().Endpoints().Informer().GetStore().Add(&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: svcName,
Namespace: namespace,
},
Subsets: []v1.EndpointSubset{{
Addresses: []v1.EndpointAddress{{IP: "3.3.3.3"}},
Ports: []v1.EndpointPort{{Port: 80}},
}},
})
return controller, osClient
}
func TestServiceTypeNoLoadBalancer(t *testing.T) {
service := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "no-external-balancer",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Type: v1.ServiceTypeClusterIP,
},
}
// test load balencer exist or not.
testCases := map[string]bool{
"case 1": false,
"case 2": true,
}
lb := &openstack.LoadBalancer{
Name: buildLoadBalancerName(service),
}
for k, lbExist := range testCases {
controller, osClient, client := newController()
if lbExist {
osClient.SetLoadbalancer(lb)
}
err, _ := controller.createLoadBalancerIfNeeded("foo/bar", service)
if err != nil {
t.Errorf("%v: unexpected error: %v", k, err)
}
actions := client.Actions()
if osClient.GetCalledNames()[0] != LoadBalancerExist {
t.Errorf("%v: unexpected openstack client calls: %v", k, osClient.GetCalledDetails())
}
if lbExist {
if osClient.GetCalledNames()[1] != EnsureLoadBalancerDeleted {
t.Errorf("%v: unexpected openstack client calls: %v", k, osClient.GetCalledDetails())
}
}
if len(actions) > 0 {
t.Errorf("%v: unexpected client actions: %v", k, actions)
}
}
}
func TestCreateExternalLoadBalancer(t *testing.T) {
table := []struct {
service *v1.Service
expectErr bool
expectCreated bool
}{
{
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Namespace: "default",
SelfLink: testapi.Default.SelfLink("services", "svc1"),
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
Port: 80,
},
{
Port: 8080,
},
},
Type: v1.ServiceTypeLoadBalancer,
},
},
expectErr: true,
expectCreated: false,
},
{
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc2",
Namespace: "default",
SelfLink: testapi.Default.SelfLink("services", "svc2"),
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{{
Port: 80,
}},
ExternalIPs: []string{
"1.1.1.1",
"2.2.2.2",
},
Type: v1.ServiceTypeLoadBalancer,
},
},
expectErr: true,
expectCreated: false,
},
{
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc3",
Namespace: "default",
SelfLink: testapi.Default.SelfLink("services", "svc3"),
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{{
Port: 80,
}},
ExternalIPs: []string{
"1.1.1.1",
},
Type: v1.ServiceTypeLoadBalancer,
},
},
expectErr: false,
expectCreated: true,
},
}
for _, item := range table {
testServer, endpointsHandler := makeTestServer(t, "default")
defer testServer.Close()
// Create a new fake service controller.
controller, osClient := newControllerFakeHTTPServer(testServer.URL, item.service.Name, item.service.Namespace)
err, _ := controller.createLoadBalancerIfNeeded("foo/bar", item.service)
if !item.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
} else if item.expectErr && err == nil {
t.Errorf("expected error creating %v, got nil", item.service)
}
if !item.expectCreated {
if len(osClient.GetCalledNames()) != 0 {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
endpointsHandler.ValidateRequestCount(t, 0)
} else {
var balancer *openstack.LoadBalancer
for k := range osClient.LoadBalancers {
if balancer == nil {
b := osClient.LoadBalancers[k]
balancer = b
} else {
t.Errorf("expected one load balancer to be created, got %v", osClient.LoadBalancers)
break
}
}
if balancer == nil {
t.Errorf("expected one load balancer to be created, got none")
} else if balancer.Name != buildLoadBalancerName(item.service) &&
balancer.ServicePort != int(item.service.Spec.Ports[0].Port) &&
balancer.ExternalIP != item.service.Spec.ExternalIPs[0] {
t.Errorf("created load balancer has incorrect parameters: %v", balancer)
}
endpointsHandler.ValidateRequestCount(t, 2)
}
}
}
func TestProcessServiceUpdate(t *testing.T) {
var controller *ServiceController
var osClient *openstack.FakeOSClient
//A pair of old and new loadbalancer IP address
oldLBIP := "192.168.1.1"
newLBIP := "192.168.1.2"
testServer, _ := makeTestServer(t, "default")
defer testServer.Close()
testCases := []struct {
testName string
key string
updateFn func(*v1.Service) *v1.Service //Manipulate the structure
svc *v1.Service
expectedFn func(*v1.Service, error, time.Duration) error //Error comparision function
}{
{
testName: "If updating a valid service",
key: "validKey",
svc: defaultExternalService(),
updateFn: func(svc *v1.Service) *v1.Service {
// Create a new fake service controller.
controller, osClient = newControllerFakeHTTPServer(testServer.URL, "external-balancer", "default")
controller.cache.getOrCreate("validKey")
return svc
},
expectedFn: func(svc *v1.Service, err error, retryDuration time.Duration) error {
if err != nil {
return err
}
if retryDuration != doNotRetry {
return fmt.Errorf("retryDuration Expected=%v Obtained=%v", doNotRetry, retryDuration)
}
if len(osClient.GetCalledNames()) != 2 {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
{
testName: "If Updating Loadbalancer IP",
key: "default/sync-test-name",
svc: newService("sync-test-name", types.UID("sync-test-uid"), v1.ServiceTypeLoadBalancer),
updateFn: func(svc *v1.Service) *v1.Service {
svc.Spec.LoadBalancerIP = oldLBIP
keyExpected := svc.GetObjectMeta().GetNamespace() + "/" + svc.GetObjectMeta().GetName()
controller.enqueueService(svc)
cachedServiceTest := controller.cache.getOrCreate(keyExpected)
cachedServiceTest.state = svc
controller.cache.set(keyExpected, cachedServiceTest)
keyGot, quit := controller.workingQueue.Get()
if quit {
t.Fatalf("get no workingQueue element")
}
if keyExpected != keyGot.(string) {
t.Fatalf("get service key error, expected: %s, got: %s", keyExpected, keyGot.(string))
}
copy, err := scheme.Scheme.DeepCopy(svc)
if err != nil {
t.Fatalf("copy service error: %v", err)
}
newService := copy.(*v1.Service)
newService.Spec.LoadBalancerIP = newLBIP
return newService
},
expectedFn: func(svc *v1.Service, err error, retryDuration time.Duration) error {
if err != nil {
return err
}
if retryDuration != doNotRetry {
return fmt.Errorf("retryDuration Expected=%v Obtained=%v", doNotRetry, retryDuration)
}
keyExpected := svc.GetObjectMeta().GetNamespace() + "/" + svc.GetObjectMeta().GetName()
cachedServiceGot, exist := controller.cache.get(keyExpected)
if !exist {
return fmt.Errorf("update service error, workingQueue should contain service: %s", keyExpected)
}
if cachedServiceGot.state.Spec.LoadBalancerIP != newLBIP {
return fmt.Errorf("update LoadBalancerIP error, expected: %s, got: %s", newLBIP, cachedServiceGot.state.Spec.LoadBalancerIP)
}
if len(osClient.GetCalledNames()) != 4 {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
}
for _, tc := range testCases {
newSvc := tc.updateFn(tc.svc)
svcCache := controller.cache.getOrCreate(tc.key)
obtErr, retryDuration := controller.processServiceUpdate(svcCache, newSvc, tc.key)
if err := tc.expectedFn(newSvc, obtErr, retryDuration); err != nil {
t.Errorf("%v processServiceUpdate() %v", tc.testName, err)
}
}
}
func TestSyncService(t *testing.T) {
var controller *ServiceController
var osClient *openstack.FakeOSClient
testServer, _ := makeTestServer(t, "default")
defer testServer.Close()
testCases := []struct {
testName string
key string
updateFn func() //Function to manipulate the controller element to simulate error
expectedFn func(error) error //Expected function if returns nil then test passed, failed otherwise
}{
{
testName: "if an invalid service name is synced",
key: "invalid/key/string",
updateFn: func() {
// Create a new fake service controller.
controller, osClient = newControllerFakeHTTPServer(testServer.URL, "", "")
},
expectedFn: func(e error) error {
//TODO: Expected error is of the format fmt.Errorf("unexpected key format: %q", "invalid/key/string"),
//TODO: should find a way to test for dependent package errors in such a way that it wont break
//TODO: our tests, currently we only test if there is an error.
//Error should be non-nil
if e == nil {
return fmt.Errorf("Expected=unexpected key format: %q, Obtained=nil", "invalid/key/string")
}
if len(osClient.GetCalledNames()) != 0 {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
/* We cannot open this test case as syncService(key) currently runtime.HandleError(err) and suppresses frequently occurring errors
{
testName: "if an invalid service is synced",
key: "somethingelse",
updateFn: func() {
// Create a new fake service controller.
controller, osClient = newControllerFakeHTTPServer(testServer.URL, "external-balancer", "default")
srv := controller.cache.getOrCreate("external-balancer")
srv.state = defaultExternalService()
},
expectedErr: fmt.Errorf("Service somethingelse not in cache even though the watcher thought it was. Ignoring the deletion."),
},
*/
//TODO: see if we can add a test for valid but error throwing service, its difficult right now because synCService() currently runtime.HandleError
{
testName: "if valid service",
key: "external-balancer",
updateFn: func() {
testSvc := defaultExternalService()
// Create a new fake service controller.
controller, osClient = newControllerFakeHTTPServer(testServer.URL, "external-balancer", "default")
controller.enqueueService(testSvc)
svc := controller.cache.getOrCreate("external-balancer")
svc.state = testSvc
},
expectedFn: func(e error) error {
//error should be nil
if e != nil {
return fmt.Errorf("Expected=nil, Obtained=%v", e)
}
if osClient.GetCalledDetails()[0].Name != EnsureLoadBalancerDeleted {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
}
for _, tc := range testCases {
tc.updateFn()
obtainedErr := controller.syncService(tc.key)
//expected matches obtained ??.
if exp := tc.expectedFn(obtainedErr); exp != nil {
t.Errorf("%v Error:%v", tc.testName, exp)
}
//Post processing, the element should not be in the sync queue.
_, exist := controller.cache.get(tc.key)
if exist {
t.Fatalf("%v working Queue should be empty, but contains %s", tc.testName, tc.key)
}
}
}
func TestProcessServiceDeletion(t *testing.T) {
var controller *ServiceController
var osClient *openstack.FakeOSClient
testServer, _ := makeTestServer(t, "default")
defer testServer.Close()
//Add a global svcKey name
svcKey := "external-balancer"
testCases := []struct {
testName string
updateFn func(*ServiceController) //Update function used to manupulate srv and controller values
expectedFn func(svcErr error, retryDuration time.Duration) error //Function to check if the returned value is expected
}{
{
testName: "If an non-existant service is deleted",
updateFn: func(controller *ServiceController) {
//Does not do anything
},
expectedFn: func(svcErr error, retryDuration time.Duration) error {
expectedError := "Service external-balancer not in cache even though the watcher thought it was. Ignoring the deletion."
if svcErr == nil || svcErr.Error() != expectedError {
//cannot be nil or Wrong error message
return fmt.Errorf("Expected=%v Obtained=%v", expectedError, svcErr)
}
if retryDuration != doNotRetry {
//Retry duration should match
return fmt.Errorf("RetryDuration Expected=%v Obtained=%v", doNotRetry, retryDuration)
}
if len(osClient.GetCalledNames()) != 0 {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
{
testName: "If openstack failed to delete the LoadBalancer",
updateFn: func(controller *ServiceController) {
svc := controller.cache.getOrCreate(svcKey)
svc.state = defaultExternalService()
osClient.InjectError("EnsureLoadBalancerDeleted", fmt.Errorf("Error Deleting the Loadbalancer"))
},
expectedFn: func(svcErr error, retryDuration time.Duration) error {
expectedError := "Error Deleting the Loadbalancer"
if svcErr == nil || svcErr.Error() != expectedError {
return fmt.Errorf("Expected=%v Obtained=%v", expectedError, svcErr)
}
if retryDuration != minRetryDelay {
return fmt.Errorf("RetryDuration Expected=%v Obtained=%v", minRetryDelay, retryDuration)
}
if len(osClient.GetCalledNames()) != 1 && osClient.GetCalledDetails()[0].Name != EnsureLoadBalancerDeleted {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
{
testName: "If openstack delete loadbalancer successfully",
updateFn: func(controller *ServiceController) {
testSvc := defaultExternalService()
controller.enqueueService(testSvc)
svc := controller.cache.getOrCreate(svcKey)
svc.state = testSvc
controller.cache.set(svcKey, svc)
},
expectedFn: func(svcErr error, retryDuration time.Duration) error {
if svcErr != nil {
return fmt.Errorf("Expected=nil Obtained=%v", svcErr)
}
if retryDuration != doNotRetry {
//Retry duration should match
return fmt.Errorf("RetryDuration Expected=%v Obtained=%v", doNotRetry, retryDuration)
}
//It should no longer be in the workqueue.
_, exist := controller.cache.get(svcKey)
if exist {
return fmt.Errorf("delete service error, workingQueue should not contain service: %s any more", svcKey)
}
if len(osClient.GetCalledNames()) != 0 && osClient.GetCalledDetails()[0].Name != EnsureLoadBalancerDeleted {
t.Errorf("unexpected openstack client calls: %v", osClient.GetCalledDetails())
}
return nil
},
},
}
for _, tc := range testCases {
// Create a new fake service controller.
controller, osClient = newControllerFakeHTTPServer(testServer.URL, "external-balancer", "default")
tc.updateFn(controller)
obtainedErr, retryDuration := controller.processServiceDeletion(svcKey)
if err := tc.expectedFn(obtainedErr, retryDuration); err != nil {
t.Errorf("%v processServiceDeletion() %v", tc.testName, err)
}
}
}
func TestDoesExternalLoadBalancerNeedsUpdate(t *testing.T) {
var oldSvc, newSvc *v1.Service
testCases := []struct {
testName string //Name of the test case
updateFn func() //Function to update the service object
expectedNeedsUpdate bool //needsupdate always returns bool
}{
{
testName: "If the service type is changed from LoadBalancer to ClusterIP",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
newSvc.Spec.Type = v1.ServiceTypeClusterIP
},
expectedNeedsUpdate: true,
},
{
testName: "If the service's LoadBalancerSourceRanges changed",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
oldSvc.Spec.LoadBalancerSourceRanges = []string{"old load balancer source range"}
newSvc.Spec.LoadBalancerSourceRanges = []string{"new load balancer source range"}
},
expectedNeedsUpdate: true,
},
{
testName: "If the service's LoadBalancer Port are different",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
oldSvc.Spec.Ports = []v1.ServicePort{
{
Port: 8000,
},
}
newSvc.Spec.Ports = []v1.ServicePort{
{
Port: 8001,
},
}
},
expectedNeedsUpdate: true,
},
{
testName: "If externel ip counts are different",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
oldSvc.Spec.ExternalIPs = []string{"old.IP.1"}
newSvc.Spec.ExternalIPs = []string{"new.IP.1", "new.IP.2"}
},
expectedNeedsUpdate: true,
},
{
testName: "If externel ips are different",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
oldSvc.Spec.ExternalIPs = []string{"old.IP.1", "old.IP.2"}
newSvc.Spec.ExternalIPs = []string{"new.IP.1", "new.IP.2"}
},
expectedNeedsUpdate: true,
},
{
testName: "If UID is different",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
oldSvc.UID = types.UID("UID old")
newSvc.UID = types.UID("UID new")
},
expectedNeedsUpdate: true,
},
{
testName: "If ExternalTrafficPolicy is different",
updateFn: func() {
oldSvc = defaultExternalService()
newSvc = defaultExternalService()
newSvc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
},
expectedNeedsUpdate: true,
},
}
controller, _, _ := newController()
for _, tc := range testCases {
tc.updateFn()
obtainedResult := controller.needsUpdate(oldSvc, newSvc)
if obtainedResult != tc.expectedNeedsUpdate {
t.Errorf("%v needsUpdate() should have returned %v but returned %v", tc.testName, tc.expectedNeedsUpdate, obtainedResult)
}
}
}
//All the testcases for ServiceCache uses a single cache, these below test cases should be run in order,
//as tc1 (addCache would add elements to the cache)
//and tc2 (delCache would remove element from the cache without it adding automatically)
//Please keep this in mind while adding new test cases.
func TestServiceCache(t *testing.T) {
//ServiceCache a common service cache for all the test cases
sc := &serviceCache{serviceMap: make(map[string]*cachedService)}
testCases := []struct {
testName string
setCacheFn func()
checkCacheFn func() error
}{
{
testName: "Add",
setCacheFn: func() {
cS := sc.getOrCreate("addTest")
cS.state = defaultExternalService()
},
checkCacheFn: func() error {
//There must be exactly one element
if len(sc.serviceMap) != 1 {
return fmt.Errorf("Expected=1 Obtained=%d", len(sc.serviceMap))
}
return nil
},
},
{
testName: "Del",
setCacheFn: func() {
sc.delete("addTest")
},
checkCacheFn: func() error {
//Now it should have no element
if len(sc.serviceMap) != 0 {
return fmt.Errorf("Expected=0 Obtained=%d", len(sc.serviceMap))
}
return nil
},
},
{
testName: "Set and Get",
setCacheFn: func() {
sc.set("addTest", &cachedService{state: defaultExternalService()})
},
checkCacheFn: func() error {
//Now it should have one element
Cs, bool := sc.get("addTest")
if !bool {
return fmt.Errorf("is Available Expected=true Obtained=%v", bool)
}
if Cs == nil {
return fmt.Errorf("CachedService expected:non-nil Obtained=nil")
}
return nil
},
},
{
testName: "ListKeys",
setCacheFn: func() {
//Add one more entry here
sc.set("addTest1", &cachedService{state: defaultExternalService()})
},
checkCacheFn: func() error {
//It should have two elements
keys := sc.ListKeys()
if len(keys) != 2 {
return fmt.Errorf("Elementes Expected=2 Obtained=%v", len(keys))
}
return nil
},
},
{
testName: "GetbyKeys",
setCacheFn: nil, //Nothing to set
checkCacheFn: func() error {
//It should have two elements
svc, isKey, err := sc.GetByKey("addTest")
if svc == nil || isKey == false || err != nil {
return fmt.Errorf("Expected(non-nil, true, nil) Obtained(%v,%v,%v)", svc, isKey, err)
}
return nil
},
},
{
testName: "allServices",
setCacheFn: nil, //Nothing to set
checkCacheFn: func() error {
//It should return two elements
svcArray := sc.allServices()
if len(svcArray) != 2 {
return fmt.Errorf("Expected(2) Obtained(%v)", len(svcArray))
}
return nil
},
},
}
for _, tc := range testCases {
if tc.setCacheFn != nil {
tc.setCacheFn()
}
if err := tc.checkCacheFn(); err != nil {
t.Errorf("%v returned %v", tc.testName, err)
}
}
}