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  }