go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/deploy/service/model/logic_test.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package model 16 17 import ( 18 "testing" 19 20 "go.chromium.org/luci/deploy/api/modelpb" 21 22 . "github.com/smartystreets/goconvey/convey" 23 ) 24 25 // service name => version name => traffic portion it gets 26 type serviceMap map[string]map[string]int32 27 28 func TestIntendedMatchesReported(t *testing.T) { 29 t.Parallel() 30 31 Convey("GAE", t, func() { 32 type testCase struct { 33 intended serviceMap 34 reported serviceMap 35 } 36 check := func(tc testCase) bool { 37 return appengineIntendedMatchesReported(stateProto(true, tc.intended), stateProto(false, tc.reported)) 38 } 39 40 So(check(testCase{}), ShouldBeTrue) 41 42 // Ignored extra services and versions. 43 So(check(testCase{ 44 intended: serviceMap{ 45 "svc1": {"ver1": 200, "ver2": 800}, 46 "svc2": {"ver1": 1000}, 47 }, 48 reported: serviceMap{ 49 "svc1": {"ver1": 200, "ver2": 800, "ignored": 0}, 50 "svc2": {"ver1": 1000, "ignored": 0}, 51 "ignored": {"ver1": 1000}, 52 }, 53 }), ShouldBeTrue) 54 55 // Moving traffic. 56 So(check(testCase{ 57 intended: serviceMap{ 58 "svc1": {"ver1": 800, "ver2": 200}, 59 "svc2": {"ver1": 1000}, 60 }, 61 reported: serviceMap{ 62 "svc1": {"ver1": 200, "ver2": 800}, 63 "svc2": {"ver1": 1000}, 64 }, 65 }), ShouldBeFalse) 66 67 // Deploying new version. 68 So(check(testCase{ 69 intended: serviceMap{ 70 "svc1": {"ver1": 200, "ver2": 800}, 71 "svc2": {"ver2": 1000}, 72 }, 73 reported: serviceMap{ 74 "svc1": {"ver1": 200, "ver2": 800}, 75 "svc2": {"ver1": 1000}, 76 }, 77 }), ShouldBeFalse) 78 79 // Deploying new version, but not shifting any traffic to it. 80 So(check(testCase{ 81 intended: serviceMap{ 82 "svc1": {"ver1": 200, "ver2": 800, "ver3": 0}, 83 "svc2": {"ver1": 1000}, 84 }, 85 reported: serviceMap{ 86 "svc1": {"ver1": 200, "ver2": 800}, 87 "svc2": {"ver1": 1000}, 88 }, 89 }), ShouldBeFalse) 90 }) 91 } 92 93 func serviceProto(name string, intended bool, versions map[string]int32) *modelpb.AppengineState_Service { 94 var vers []*modelpb.AppengineState_Service_Version 95 for ver := range versions { 96 verpb := &modelpb.AppengineState_Service_Version{ 97 Name: ver, 98 } 99 if intended { 100 verpb.IntendedState = &modelpb.AppengineState_Service_Version_IntendedState{} 101 } else { 102 verpb.CapturedState = &modelpb.AppengineState_Service_Version_CapturedState{} 103 } 104 vers = append(vers, verpb) 105 } 106 return &modelpb.AppengineState_Service{ 107 Name: name, 108 Versions: vers, 109 TrafficSplitting: modelpb.AppengineState_Service_COOKIE, 110 TrafficAllocation: versions, 111 } 112 } 113 114 func stateProto(intended bool, services serviceMap) *modelpb.AppengineState { 115 var svc []*modelpb.AppengineState_Service 116 for name, vers := range services { 117 svc = append(svc, serviceProto(name, intended, vers)) 118 } 119 out := &modelpb.AppengineState{Services: svc} 120 if intended { 121 out.IntendedState = &modelpb.AppengineState_IntendedState{} 122 } else { 123 out.CapturedState = &modelpb.AppengineState_CapturedState{} 124 } 125 return out 126 }