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  }