github.com/polarismesh/polaris@v1.17.8/cache/service/service_test.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package service
    19  
    20  import (
    21  	"fmt"
    22  	"math/rand"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/golang/mock/gomock"
    28  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    29  	"github.com/stretchr/testify/assert"
    30  
    31  	types "github.com/polarismesh/polaris/cache/api"
    32  	cachemock "github.com/polarismesh/polaris/cache/mock"
    33  	"github.com/polarismesh/polaris/common/model"
    34  	"github.com/polarismesh/polaris/common/utils"
    35  	"github.com/polarismesh/polaris/store"
    36  	"github.com/polarismesh/polaris/store/mock"
    37  )
    38  
    39  // 生成一个测试的serviceCache和对应的mock对象
    40  func newTestServiceCache(t *testing.T) (*gomock.Controller, *mock.MockStore, *serviceCache, *instanceCache) {
    41  	ctl := gomock.NewController(t)
    42  
    43  	storage := mock.NewMockStore(ctl)
    44  	mockCacheMgr := cachemock.NewMockCacheManager(ctl)
    45  
    46  	mockSvcCache := NewServiceCache(storage, mockCacheMgr)
    47  	mockInstCache := NewInstanceCache(storage, mockCacheMgr)
    48  
    49  	mockCacheMgr.EXPECT().GetCacher(types.CacheService).Return(mockSvcCache).AnyTimes()
    50  	mockCacheMgr.EXPECT().GetCacher(types.CacheInstance).Return(mockInstCache).AnyTimes()
    51  
    52  	mockTx := mock.NewMockTx(ctl)
    53  	mockTx.EXPECT().Commit().Return(nil).AnyTimes()
    54  	mockTx.EXPECT().Rollback().Return(nil).AnyTimes()
    55  	mockTx.EXPECT().CreateReadView().Return(nil).AnyTimes()
    56  	storage.EXPECT().StartReadTx().Return(mockTx, nil).AnyTimes()
    57  	storage.EXPECT().GetUnixSecond(gomock.Any()).AnyTimes().Return(time.Now().Unix(), nil)
    58  
    59  	opt := map[string]interface{}{
    60  		"disableBusiness": false,
    61  		"needMeta":        true,
    62  	}
    63  	_ = mockInstCache.Initialize(opt)
    64  	_ = mockSvcCache.Initialize(opt)
    65  
    66  	return ctl, storage, mockSvcCache.(*serviceCache), mockInstCache.(*instanceCache)
    67  }
    68  
    69  // 获取当前缓存中的services总数
    70  func getServiceCacheCount(sc *serviceCache) int {
    71  	sum := 0
    72  	_ = sc.IteratorServices(func(key string, value *model.Service) (bool, error) {
    73  		sum++
    74  		return true, nil
    75  	})
    76  	return sum
    77  }
    78  
    79  // 生成一些测试的services
    80  func genModelService(total int) map[string]*model.Service {
    81  	out := make(map[string]*model.Service)
    82  	for i := 0; i < total; i++ {
    83  		item := &model.Service{
    84  			ID:         fmt.Sprintf("ID-%d", i),
    85  			Namespace:  fmt.Sprintf("Namespace-%d", i),
    86  			Name:       fmt.Sprintf("Name-%d", i),
    87  			Revision:   utils.NewUUID(),
    88  			Valid:      true,
    89  			ModifyTime: time.Unix(int64(i), 0),
    90  		}
    91  		out[item.ID] = item
    92  	}
    93  
    94  	return out
    95  }
    96  
    97  // 生成一些测试的services
    98  func genModelServiceByNamespace(total int, namespace string) map[string]*model.Service {
    99  	out := make(map[string]*model.Service)
   100  	for i := 0; i < total; i++ {
   101  		item := &model.Service{
   102  			ID:         fmt.Sprintf("%s-ID-%d", namespace, i),
   103  			Namespace:  namespace,
   104  			Name:       fmt.Sprintf("Name-%d", i),
   105  			Valid:      true,
   106  			Revision:   utils.NewUUID(),
   107  			ModifyTime: time.Unix(int64(i), 0),
   108  		}
   109  		out[item.ID] = item
   110  	}
   111  
   112  	return out
   113  }
   114  
   115  func genModelInstancesByServicesWithInsId(
   116  	services map[string]*model.Service, instCount int, insIdPrefix string) (map[string][]*model.Instance, map[string]*model.Instance) {
   117  	var svcToInstances = make(map[string][]*model.Instance, len(services))
   118  	var allInstances = make(map[string]*model.Instance, len(services)*instCount)
   119  	var idx int
   120  	for id, svc := range services {
   121  		label := svc.Name
   122  		instancesSvc := make([]*model.Instance, 0, instCount)
   123  		for i := 0; i < instCount; i++ {
   124  			entry := &model.Instance{
   125  				Proto: &apiservice.Instance{
   126  					Id:   utils.NewStringValue(fmt.Sprintf("%s-instanceID-%s-%d", insIdPrefix, label, idx)),
   127  					Host: utils.NewStringValue(fmt.Sprintf("host-%s-%d", label, idx)),
   128  					Port: utils.NewUInt32Value(uint32(idx + 10)),
   129  				},
   130  				ServiceID: svc.ID,
   131  				Valid:     true,
   132  			}
   133  			idx++
   134  			instancesSvc = append(instancesSvc, entry)
   135  			allInstances[entry.ID()] = entry
   136  		}
   137  		svcToInstances[id] = instancesSvc
   138  	}
   139  	return svcToInstances, allInstances
   140  }
   141  
   142  // 生成一些测试的services
   143  func genModelServiceByNamespaces(total int, namespace []string) map[string]*model.Service {
   144  	out := make(map[string]*model.Service)
   145  	for i := 0; i < total; i++ {
   146  		item := &model.Service{
   147  			ID:         fmt.Sprintf("ID-%d", i),
   148  			Namespace:  namespace[rand.Intn(len(namespace))],
   149  			Name:       fmt.Sprintf("Name-%d", i),
   150  			Valid:      true,
   151  			ModifyTime: time.Unix(int64(i), 0),
   152  		}
   153  		out[item.ID] = item
   154  	}
   155  
   156  	return out
   157  }
   158  
   159  // TestServiceUpdate 测试缓存更新函数
   160  func TestServiceUpdate(t *testing.T) {
   161  	ctl, storage, sc, _ := newTestServiceCache(t)
   162  	defer ctl.Finish()
   163  
   164  	t.Run("所有数据为空, 可以正常获取数据", func(t *testing.T) {
   165  		gomock.InOrder(
   166  			storage.EXPECT().
   167  				GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).
   168  				Return(nil, nil).Times(1),
   169  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(0), nil),
   170  		)
   171  
   172  		if err := sc.Update(); err != nil {
   173  			t.Fatalf("error: %s", err.Error())
   174  		}
   175  
   176  		if sum := getServiceCacheCount(sc); sum != 0 {
   177  			t.Fatalf("error: %d", sum)
   178  		}
   179  	})
   180  	t.Run("有数据更新, 数据正常", func(t *testing.T) {
   181  		_ = sc.Clear()
   182  		services := genModelService(100)
   183  		gomock.InOrder(
   184  			storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).
   185  				Return(services, nil),
   186  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services)), nil),
   187  		)
   188  
   189  		if err := sc.Update(); err != nil {
   190  			t.Fatalf("error: %s", err.Error())
   191  		}
   192  
   193  		if sum := getServiceCacheCount(sc); sum != 100 {
   194  			t.Fatalf("error: %d", sum)
   195  		}
   196  	})
   197  	t.Run("有数据更新, 重复更新, 数据更新正常", func(t *testing.T) {
   198  		_ = sc.Clear()
   199  		services1 := genModelService(100)
   200  		services2 := genModelService(300)
   201  		gomock.InOrder(
   202  			storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).
   203  				Return(services1, nil),
   204  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services1)), nil),
   205  		)
   206  
   207  		if err := sc.Update(); err != nil {
   208  			t.Fatalf("error: %s", err.Error())
   209  		}
   210  
   211  		gomock.InOrder(
   212  			storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).
   213  				Return(services2, nil),
   214  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services2)), nil),
   215  		)
   216  		_ = sc.Update()
   217  		if sum := getServiceCacheCount(sc); sum != 300 {
   218  			t.Fatalf("error: %d", sum)
   219  		}
   220  	})
   221  }
   222  
   223  // TestServiceUpdate1 测试缓存更新函数1
   224  func TestServiceUpdate1(t *testing.T) {
   225  	ctl, storage, sc, _ := newTestServiceCache(t)
   226  	defer ctl.Finish()
   227  
   228  	t.Run("服务全部被删除, 会被清除掉", func(t *testing.T) {
   229  		_ = sc.Clear()
   230  		services := genModelService(100)
   231  		gomock.InOrder(storage.EXPECT().
   232  			GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil),
   233  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(100), nil),
   234  		)
   235  		_ = sc.Update()
   236  
   237  		// 把所有的都置为false
   238  		for _, service := range services {
   239  			service.Valid = false
   240  		}
   241  
   242  		gomock.InOrder(storage.EXPECT().
   243  			GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil),
   244  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(0), nil),
   245  		)
   246  		_ = sc.Update()
   247  
   248  		if sum := getServiceCacheCount(sc); sum != 0 {
   249  			t.Fatalf("error: %d", sum)
   250  		}
   251  	})
   252  
   253  	t.Run("服务部分被删除, 缓存内容正常", func(t *testing.T) {
   254  		_ = sc.Clear()
   255  		services := genModelService(100)
   256  		gomock.InOrder(storage.EXPECT().
   257  			GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil),
   258  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services)), nil),
   259  		)
   260  		_ = sc.Update()
   261  
   262  		// 把所有的都置为false
   263  		count := len(services)
   264  		idx := 0
   265  		for _, service := range services {
   266  			if idx%2 == 0 {
   267  				service.Valid = false
   268  				count--
   269  			}
   270  			idx++
   271  		}
   272  
   273  		gomock.InOrder(storage.EXPECT().
   274  			GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil),
   275  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(count), nil),
   276  		)
   277  		_ = sc.Update()
   278  
   279  		if sum := getServiceCacheCount(sc); sum != 50 { // remain half
   280  			t.Fatalf("error: %d", sum)
   281  		}
   282  	})
   283  }
   284  
   285  // TestServiceUpdate2 测试缓存更新
   286  func TestServiceUpdate2(t *testing.T) {
   287  	ctl, storage, sc, _ := newTestServiceCache(t)
   288  	defer ctl.Finish()
   289  
   290  	t.Run("store返回失败, update会返回失败", func(t *testing.T) {
   291  		_ = sc.Clear()
   292  		gomock.InOrder(
   293  			storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).
   294  				Return(nil, fmt.Errorf("store error")),
   295  			storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(0), nil),
   296  		)
   297  
   298  		if err := sc.Update(); err != nil {
   299  			t.Logf("pass: %s", err.Error())
   300  		} else {
   301  			t.Fatalf("error")
   302  		}
   303  	})
   304  }
   305  
   306  // TestGetServiceByName 根据服务名获取服务缓存信息
   307  func TestGetServiceByName(t *testing.T) {
   308  	ctl, _, sc, _ := newTestServiceCache(t)
   309  	defer ctl.Finish()
   310  	t.Run("可以根据服务名和命名空间, 正常获取缓存服务信息", func(t *testing.T) {
   311  		_ = sc.Clear()
   312  		services := genModelService(20)
   313  		sc.setServices(services)
   314  
   315  		for _, entry := range services {
   316  			service := sc.GetServiceByName(entry.Name, entry.Namespace)
   317  			if service == nil {
   318  				t.Fatalf("error")
   319  			}
   320  		}
   321  	})
   322  	t.Run("服务不存在, 返回为空", func(t *testing.T) {
   323  		_ = sc.Clear()
   324  		services := genModelService(20)
   325  		sc.setServices(services)
   326  		if service := sc.GetServiceByName("aaa", "bbb"); service != nil {
   327  			t.Fatalf("error")
   328  		}
   329  	})
   330  }
   331  
   332  // TestServiceCache_GetServiceByID 根据服务ID获取服务缓存信息
   333  func TestServiceCache_GetServiceByID(t *testing.T) {
   334  	ctl, _, sc, _ := newTestServiceCache(t)
   335  	defer ctl.Finish()
   336  
   337  	t.Run("可以根据服务ID, 正常获取缓存的服务信息", func(t *testing.T) {
   338  		_ = sc.Clear()
   339  		services := genModelService(30)
   340  		sc.setServices(services)
   341  
   342  		for _, entry := range services {
   343  			service := sc.GetServiceByID(entry.ID)
   344  			if service == nil {
   345  				t.Fatalf("error")
   346  			}
   347  		}
   348  	})
   349  
   350  	t.Run("缓存内容为空, 根据ID获取数据, 会返回为空", func(t *testing.T) {
   351  		_ = sc.Clear()
   352  		services := genModelService(30)
   353  		sc.setServices(services)
   354  
   355  		if service := sc.GetServiceByID("123456789"); service != nil {
   356  			t.Fatalf("error")
   357  		}
   358  	})
   359  }
   360  
   361  func genModelInstancesByServices(
   362  	services map[string]*model.Service, instCount int) (map[string][]*model.Instance, map[string]*model.Instance) {
   363  	var svcToInstances = make(map[string][]*model.Instance, len(services))
   364  	var allInstances = make(map[string]*model.Instance, len(services)*instCount)
   365  	var idx int
   366  	for id, svc := range services {
   367  		label := svc.Name
   368  		instancesSvc := make([]*model.Instance, 0, instCount)
   369  		for i := 0; i < instCount; i++ {
   370  			entry := &model.Instance{
   371  				Proto: &apiservice.Instance{
   372  					Id:   utils.NewStringValue(fmt.Sprintf("instanceID-%s-%d", label, idx)),
   373  					Host: utils.NewStringValue(fmt.Sprintf("host-%s-%d", label, idx)),
   374  					Port: utils.NewUInt32Value(uint32(idx + 10)),
   375  				},
   376  				ServiceID: svc.ID,
   377  				Valid:     true,
   378  			}
   379  			idx++
   380  			instancesSvc = append(instancesSvc, entry)
   381  			allInstances[entry.ID()] = entry
   382  		}
   383  		svcToInstances[id] = instancesSvc
   384  	}
   385  	return svcToInstances, allInstances
   386  }
   387  
   388  // TestServiceCache_GetServicesByFilter 根据实例的host查询对应的服务列表
   389  func TestServiceCache_GetServicesByFilter(t *testing.T) {
   390  	ctl, mockStore, sc, _ := newTestServiceCache(t)
   391  	defer ctl.Finish()
   392  
   393  	t.Run("可以根据服务host-正常获取缓存的服务信息", func(t *testing.T) {
   394  		_ = sc.Clear()
   395  		services := genModelServiceByNamespace(100, "default")
   396  		sc.setServices(services)
   397  
   398  		svcInstances, instances := genModelInstancesByServices(services, 2)
   399  		ic := sc.instCache.(*instanceCache)
   400  
   401  		mockStore.EXPECT().GetServicesCount().Return(uint32(len(services)), nil).AnyTimes()
   402  		mockStore.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(len(instances)), nil).AnyTimes()
   403  		mockStore.EXPECT().GetMoreServices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(services, nil).AnyTimes()
   404  		mockStore.EXPECT().GetMoreInstances(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(instances, nil).AnyTimes()
   405  		ic.setInstances(instances)
   406  
   407  		hostToService := make(map[string]string)
   408  		for svc, instances := range svcInstances {
   409  			hostToService[instances[0].Host()] = svc
   410  		}
   411  		// 先不带命名空间进行查询
   412  		for host, svcId := range hostToService {
   413  			instArgs := &store.InstanceArgs{
   414  				Hosts: []string{host},
   415  			}
   416  			svcArgs := &types.ServiceArgs{
   417  				EmptyCondition: true,
   418  			}
   419  			amount, services, err := sc.GetServicesByFilter(svcArgs, instArgs, 0, 10)
   420  			if err != nil {
   421  				t.Fatal(err)
   422  			}
   423  			if amount != 1 {
   424  				t.Fatalf("service count is %d, expect 1", amount)
   425  			}
   426  			if len(services) != 1 {
   427  				t.Fatalf("service count is %d, expect 1", len(services))
   428  			}
   429  			if services[0].ID != svcId {
   430  				t.Fatalf("service id not match, actual %s, expect %s", services[0].ID, svcId)
   431  			}
   432  		}
   433  
   434  	})
   435  }
   436  
   437  func TestServiceCache_NamespaceCount(t *testing.T) {
   438  	ctl, _, sc, ic := newTestServiceCache(t)
   439  	defer ctl.Finish()
   440  
   441  	t.Run("先刷新instancesCache, 在刷新serviceCache, 计数等待一段时间之后正常", func(t *testing.T) {
   442  		_ = sc.Clear()
   443  		_ = ic.Clear()
   444  
   445  		ic := sc.instCache.(*instanceCache)
   446  
   447  		nsList := []string{"default", "test-1", "test-2", "test-3"}
   448  
   449  		services := genModelServiceByNamespaces(50, nsList)
   450  		svcInstances, instances := genModelInstancesByServices(services, 2)
   451  		expectNsCount := make(map[string]int)
   452  
   453  		for svcId, instances := range svcInstances {
   454  			svc := services[svcId]
   455  			if _, ok := expectNsCount[svc.Namespace]; !ok {
   456  				expectNsCount[svc.Namespace] = 0
   457  			}
   458  			expectNsCount[svc.Namespace] += len(instances)
   459  		}
   460  
   461  		ic.setInstances(instances)
   462  		time.Sleep(time.Duration(5 * time.Second))
   463  		sc.setServices(services)
   464  		time.Sleep(time.Duration(5 * time.Second))
   465  
   466  		acutalNsCount := make(map[string]int)
   467  		for i := range nsList {
   468  			ns := nsList[i]
   469  			acutalNsCount[ns] = int(sc.GetNamespaceCntInfo(ns).InstanceCnt.TotalInstanceCount)
   470  		}
   471  
   472  		fmt.Printf("expect ns count : %#v\n", expectNsCount)
   473  		fmt.Printf("acutal ns count : %#v\n", acutalNsCount)
   474  
   475  		if !reflect.DeepEqual(expectNsCount, acutalNsCount) {
   476  			t.Fatal("namespace count is no currect")
   477  		}
   478  	})
   479  }
   480  
   481  // TestRevisionWorker 测试revision的管道是否正常
   482  func TestRevisionWorker(t *testing.T) {
   483  	ctl := gomock.NewController(t)
   484  	storage := mock.NewMockStore(ctl)
   485  	mockCacheMgr := cachemock.NewMockCacheManager(ctl)
   486  	storage.EXPECT().GetUnixSecond(gomock.Any()).AnyTimes().Return(time.Now().Unix(), nil)
   487  	defer ctl.Finish()
   488  
   489  	t.Run("revision计算, chan可以正常收发", func(t *testing.T) {
   490  		svcCache := NewServiceCache(storage, mockCacheMgr)
   491  		mockInstCache := NewInstanceCache(storage, mockCacheMgr)
   492  		mockCacheMgr.EXPECT().GetCacher(types.CacheInstance).Return(mockInstCache).AnyTimes()
   493  		mockCacheMgr.EXPECT().GetCacher(types.CacheService).Return(svcCache).AnyTimes()
   494  		_ = svcCache.Initialize(map[string]interface{}{})
   495  		_ = mockInstCache.Initialize(map[string]interface{}{})
   496  
   497  		t.Cleanup(func() {
   498  			_ = mockInstCache.Clear()
   499  			_ = svcCache.Clear()
   500  		})
   501  
   502  		// mock一下cache中服务的数据
   503  		maxTotal := 20480
   504  		services := make(map[string]*model.Service)
   505  		for i := 0; i < maxTotal; i++ {
   506  			item := &model.Service{
   507  				ID:       fmt.Sprintf("service-id-%d", i),
   508  				Revision: fmt.Sprintf("revision-%d", i),
   509  				Valid:    true,
   510  			}
   511  			services[item.ID] = item
   512  		}
   513  		storage.EXPECT().GetServicesCount().Return(uint32(maxTotal), nil).AnyTimes()
   514  		storage.EXPECT().GetMoreServices(gomock.Any(), true, false, false).Return(services, nil)
   515  		// 触发计算
   516  		_ = svcCache.Update()
   517  		time.Sleep(time.Second * 10)
   518  		assert.Equal(t, maxTotal, svcCache.GetRevisionWorker().GetServiceRevisionCount())
   519  
   520  		services = make(map[string]*model.Service)
   521  		for i := 0; i < maxTotal; i++ {
   522  			if i%2 == 0 {
   523  				item := &model.Service{
   524  					ID:       fmt.Sprintf("service-id-%d", i),
   525  					Revision: fmt.Sprintf("revision-%d", i),
   526  					Valid:    false,
   527  				}
   528  				services[item.ID] = item
   529  			}
   530  		}
   531  		storage.EXPECT().GetServicesCount().Return(uint32(maxTotal), nil).AnyTimes()
   532  		storage.EXPECT().GetMoreServices(gomock.Any(), false, false, false).Return(services, nil)
   533  		// 触发计算
   534  		_ = svcCache.Update()
   535  		time.Sleep(time.Second * 20)
   536  		// 检查是否有正常计算
   537  		assert.Equal(t, maxTotal/2, svcCache.GetRevisionWorker().GetServiceRevisionCount())
   538  	})
   539  }
   540  
   541  // TestComputeRevision 测试计算revision的函数
   542  func TestComputeRevision(t *testing.T) {
   543  	t.Run("instances为空, 可以正常计算", func(t *testing.T) {
   544  		out, err := ComputeRevision("123", nil)
   545  		assert.NoError(t, err)
   546  		assert.NotEmpty(t, out)
   547  	})
   548  
   549  	t.Run("instances内容一样, 不同顺序, 计算出的revision一样", func(t *testing.T) {
   550  		instances := make([]*model.Instance, 0, 6)
   551  		for i := 0; i < 6; i++ {
   552  			instances = append(instances, &model.Instance{
   553  				Proto: &apiservice.Instance{
   554  					Revision: utils.NewStringValue(fmt.Sprintf("revision-%d", i)),
   555  				},
   556  			})
   557  		}
   558  
   559  		lhs, err := ComputeRevision("123", nil)
   560  		assert.NoError(t, err)
   561  		assert.NotEmpty(t, lhs)
   562  
   563  		// 交换一下数据, 数据内容不变, revision应该保证不变
   564  		tmp := instances[0]
   565  		instances[0] = instances[1]
   566  		instances[1] = instances[3]
   567  		instances[3] = tmp
   568  
   569  		rhs, err := ComputeRevision("123", nil)
   570  		assert.NoError(t, err)
   571  		assert.Equal(t, lhs, rhs)
   572  	})
   573  
   574  	t.Run("serviceRevision发生改变, 返回改变", func(t *testing.T) {
   575  		lhs, err := ComputeRevision("123", nil)
   576  		assert.NoError(t, err)
   577  		assert.NotEmpty(t, lhs)
   578  
   579  		rhs, err := ComputeRevision("456", nil)
   580  		assert.NoError(t, err)
   581  		assert.NotEqual(t, lhs, rhs)
   582  	})
   583  
   584  	t.Run("instances内容改变, 返回改变", func(t *testing.T) {
   585  		instance := &model.Instance{Proto: &apiservice.Instance{Revision: utils.NewStringValue("123456")}}
   586  		lhs, err := ComputeRevision("123", []*model.Instance{instance})
   587  		assert.NoError(t, err)
   588  		assert.NotEmpty(t, lhs)
   589  
   590  		instance.Proto.Revision.Value = "654321"
   591  		rhs, err := ComputeRevision("456", []*model.Instance{instance})
   592  		assert.NoError(t, err)
   593  		assert.NotEqual(t, lhs, rhs)
   594  	})
   595  }