github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/rollout-service/pkg/notifier/subscriber_test.go (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 package notifier 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 "github.com/cenkalti/backoff/v4" 25 "github.com/freiheit-com/kuberpult/pkg/setup" 26 "github.com/freiheit-com/kuberpult/services/rollout-service/pkg/service" 27 "github.com/freiheit-com/kuberpult/services/rollout-service/pkg/versions" 28 "github.com/google/go-cmp/cmp" 29 ) 30 31 type expectedNotification struct { 32 Application string 33 Environment string 34 } 35 36 type mockArgocdNotifier struct { 37 ch chan<- expectedNotification 38 } 39 40 func (m *mockArgocdNotifier) NotifyArgoCd(ctx context.Context, environment, application string) { 41 m.ch <- expectedNotification{application, environment} 42 } 43 44 func TestSubscribe(t *testing.T) { 45 t.Parallel() 46 type step struct { 47 ArgoEvent *service.ArgoEvent 48 VersionEvent *versions.KuberpultEvent 49 50 ExpectedNotification *expectedNotification 51 } 52 tcs := []struct { 53 Name string 54 Steps []step 55 }{ 56 { 57 Name: "shuts down correctly", 58 }, 59 { 60 Name: "notifies when the kuberpult version differs", 61 Steps: []step{ 62 { 63 ArgoEvent: &service.ArgoEvent{ 64 Application: "foo", 65 Environment: "bar", 66 Version: &versions.VersionInfo{Version: 1}, 67 }, 68 }, 69 { 70 VersionEvent: &versions.KuberpultEvent{ 71 Application: "foo", 72 Environment: "bar", 73 Version: &versions.VersionInfo{Version: 2}, 74 }, 75 ExpectedNotification: &expectedNotification{ 76 Application: "foo", 77 Environment: "bar", 78 }, 79 }, 80 }, 81 }, 82 { 83 Name: "doesnt notify for the same version again", 84 Steps: []step{ 85 { 86 ArgoEvent: &service.ArgoEvent{ 87 Application: "foo", 88 Environment: "bar", 89 Version: &versions.VersionInfo{Version: 1}, 90 }, 91 }, 92 { 93 VersionEvent: &versions.KuberpultEvent{ 94 Application: "foo", 95 Environment: "bar", 96 Version: &versions.VersionInfo{Version: 2}, 97 }, 98 ExpectedNotification: &expectedNotification{ 99 Application: "foo", 100 Environment: "bar", 101 }, 102 }, 103 { 104 VersionEvent: &versions.KuberpultEvent{ 105 Application: "foo", 106 Environment: "bar", 107 Version: &versions.VersionInfo{Version: 2}, 108 }, 109 }, 110 }, 111 }, 112 { 113 Name: "does notify for each version", 114 Steps: []step{ 115 { 116 ArgoEvent: &service.ArgoEvent{ 117 Application: "foo", 118 Environment: "bar", 119 Version: &versions.VersionInfo{Version: 1}, 120 }, 121 }, 122 { 123 VersionEvent: &versions.KuberpultEvent{ 124 Application: "foo", 125 Environment: "bar", 126 Version: &versions.VersionInfo{Version: 2}, 127 }, 128 ExpectedNotification: &expectedNotification{ 129 Application: "foo", 130 Environment: "bar", 131 }, 132 }, 133 { 134 VersionEvent: &versions.KuberpultEvent{ 135 Application: "foo", 136 Environment: "bar", 137 Version: &versions.VersionInfo{Version: 3}, 138 }, 139 ExpectedNotification: &expectedNotification{ 140 Application: "foo", 141 Environment: "bar", 142 }, 143 }, 144 }, 145 }, 146 } 147 for _, tc := range tcs { 148 tc := tc 149 t.Run(tc.Name, func(t *testing.T) { 150 ctx := context.Background() 151 notifications := make(chan expectedNotification, len(tc.Steps)) 152 mn := &mockArgocdNotifier{notifications} 153 bc := service.New() 154 ctx, cancel := context.WithCancel(ctx) 155 eCh := make(chan error, 1) 156 hs := &setup.HealthServer{} 157 go func() { 158 eCh <- Subscribe(ctx, mn, bc, hs.Reporter("notify")) 159 }() 160 161 for _, s := range tc.Steps { 162 if s.ArgoEvent != nil { 163 bc.ProcessArgoEvent(ctx, *s.ArgoEvent) 164 } else { 165 bc.ProcessKuberpultEvent(ctx, *s.VersionEvent) 166 } 167 if s.ExpectedNotification != nil { 168 notification := <-notifications 169 if !cmp.Equal(notification, *s.ExpectedNotification) { 170 t.Errorf("expected notification %v, but got %v", s.ExpectedNotification, notification) 171 } 172 } else { 173 select { 174 case notification := <-notifications: 175 t.Errorf("exptected no notification, but got %v", notification) 176 default: 177 } 178 } 179 } 180 cancel() 181 err := <-eCh 182 if err != nil { 183 t.Errorf("expected no error, but got %q", err) 184 } 185 }) 186 } 187 } 188 189 func TestSubscriberHandlesReconnects(t *testing.T) { 190 tcs := []struct { 191 Name string 192 Disconnects uint 193 }{ 194 { 195 Name: "reconnects are handled", 196 Disconnects: 3, 197 }, 198 } 199 for _, tc := range tcs { 200 tc := tc 201 t.Run(tc.Name, func(t *testing.T) { 202 ctx := context.Background() 203 notifications := make(chan expectedNotification, tc.Disconnects) 204 mn := &mockArgocdNotifier{notifications} 205 bc := service.New() 206 ctx, cancel := context.WithCancel(ctx) 207 eCh := make(chan error, 1) 208 hs := &setup.HealthServer{} 209 hs.BackOffFactory = func() backoff.BackOff { return backoff.NewConstantBackOff(1 * time.Nanosecond) } 210 go func() { 211 eCh <- Subscribe(ctx, mn, bc, hs.Reporter("notify")) 212 }() 213 for i := uint64(1); i < uint64(tc.Disconnects); i += 1 { 214 bc.ProcessKuberpultEvent(ctx, versions.KuberpultEvent{ 215 Application: "app", 216 Environment: "env", 217 Version: &versions.VersionInfo{Version: i}, 218 }) 219 <-notifications 220 bc.DisconnectAll() 221 } 222 cancel() 223 err := <-eCh 224 if err != nil { 225 t.Errorf("expected no error, but got %q", err) 226 } 227 }) 228 } 229 }