github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/rollout-service/pkg/revolution/revolution_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  This file is part of kuberpult.
    18  
    19  Kuberpult is free software: you can redistribute it and/or modify
    20  it under the terms of the Expat(MIT) License as published by
    21  the Free Software Foundation.
    22  
    23  Kuberpult is distributed in the hope that it will be useful,
    24  but WITHOUT ANY WARRANTY; without even the implied warranty of
    25  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    26  MIT License for more details.
    27  
    28  You should have received a copy of the MIT License
    29  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    30  
    31  Copyright 2023 freiheit.com
    32  */
    33  package revolution
    34  
    35  import (
    36  	"context"
    37  	"io"
    38  	"net/http"
    39  	"net/http/httptest"
    40  	"testing"
    41  	"time"
    42  
    43  	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
    44  	"github.com/argoproj/gitops-engine/pkg/health"
    45  	"github.com/freiheit-com/kuberpult/services/rollout-service/pkg/service"
    46  	"github.com/freiheit-com/kuberpult/services/rollout-service/pkg/versions"
    47  	"github.com/google/go-cmp/cmp"
    48  )
    49  
    50  func TestRevolution(t *testing.T) {
    51  	type request struct {
    52  		Url    string
    53  		Header http.Header
    54  		Body   string
    55  	}
    56  	type step struct {
    57  		ArgoEvent       *service.ArgoEvent
    58  		KuberpultEvent  *versions.KuberpultEvent
    59  		ExpectedRequest *request
    60  	}
    61  	tcs := []struct {
    62  		Name  string
    63  		Steps []step
    64  		Now   time.Time
    65  	}{
    66  		{
    67  			Name: "send out deployment events with timestamp once",
    68  			Now:  time.Unix(123456789, 0).UTC(),
    69  			Steps: []step{
    70  				{
    71  					ArgoEvent: &service.ArgoEvent{
    72  						Environment:      "foo",
    73  						Application:      "bar",
    74  						SyncStatusCode:   v1alpha1.SyncStatusCodeSynced,
    75  						HealthStatusCode: health.HealthStatusHealthy,
    76  						Version: &versions.VersionInfo{
    77  							Version:        1,
    78  							SourceCommitId: "123456",
    79  							DeployedAt:     time.Unix(123456789, 0).UTC(),
    80  						},
    81  					},
    82  					KuberpultEvent: &versions.KuberpultEvent{
    83  						Environment:  "foo",
    84  						Application:  "bar",
    85  						IsProduction: true,
    86  						Version: &versions.VersionInfo{
    87  							Version:        1,
    88  							SourceCommitId: "123456",
    89  							DeployedAt:     time.Unix(123456789, 0).UTC(),
    90  						},
    91  					},
    92  
    93  					ExpectedRequest: &request{
    94  						Url: "/",
    95  						Header: http.Header{
    96  							"Content-Type":        []string{"application/json"},
    97  							"User-Agent":          []string{"kuberpult"},
    98  							"X-Hub-Signature-256": []string{"sha256=c227a4f702ce00368a15bd00c3678dd20c76ed7275d82c5a2d48009beb78b5ee"},
    99  						},
   100  						Body: `{"id":"0ee3e568-0f9d-5be9-b75c-caa9025599c2","commitHash":"123456","eventTime":"1973-11-29T21:33:09Z","serviceName":"bar"}`,
   101  					},
   102  				},
   103  				{
   104  					ArgoEvent: &service.ArgoEvent{
   105  						Environment:      "foo",
   106  						Application:      "bar",
   107  						SyncStatusCode:   v1alpha1.SyncStatusCodeSynced,
   108  						HealthStatusCode: health.HealthStatusDegraded,
   109  						Version: &versions.VersionInfo{
   110  							Version:        1,
   111  							SourceCommitId: "123456",
   112  							DeployedAt:     time.Unix(123456789, 0).UTC(),
   113  						},
   114  					},
   115  
   116  					ExpectedRequest: nil,
   117  				},
   118  			},
   119  		},
   120  		{
   121  			Name: "ignore old events",
   122  			Now:  time.Date(2024, 2, 15, 12, 15, 0, 0, time.UTC),
   123  			Steps: []step{
   124  				{
   125  					ArgoEvent: &service.ArgoEvent{
   126  						Environment:      "foo",
   127  						Application:      "bar",
   128  						SyncStatusCode:   v1alpha1.SyncStatusCodeSynced,
   129  						HealthStatusCode: health.HealthStatusDegraded,
   130  						Version: &versions.VersionInfo{
   131  							Version:        1,
   132  							SourceCommitId: "123456",
   133  							DeployedAt:     time.Date(2024, 2, 14, 12, 15, 0, 0, time.UTC),
   134  						},
   135  					},
   136  					ExpectedRequest: nil,
   137  				},
   138  			},
   139  		},
   140  	}
   141  	for _, tc := range tcs {
   142  		tc := tc
   143  		t.Run(tc.Name, func(t *testing.T) {
   144  			reqCh := make(chan *request)
   145  			revolution := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   146  				body, _ := io.ReadAll(r.Body)
   147  				hdr := r.Header.Clone()
   148  				hdr.Del("Accept-Encoding")
   149  				hdr.Del("Content-Length")
   150  				reqCh <- &request{
   151  					Url:    r.URL.String(),
   152  					Header: hdr,
   153  					Body:   string(body),
   154  				}
   155  				w.WriteHeader(http.StatusOK)
   156  			}))
   157  			readyCh := make(chan struct{}, 1)
   158  			bc := service.New()
   159  			errCh := make(chan error, 1)
   160  			cs := New(Config{
   161  				URL:         revolution.URL,
   162  				Token:       []byte("revolution"),
   163  				Concurrency: 100,
   164  				MaxEventAge: time.Second,
   165  			})
   166  			cs.ready = func() { readyCh <- struct{}{} }
   167  			cs.now = func() time.Time { return tc.Now }
   168  			ctx, cancel := context.WithCancel(context.Background())
   169  			go func() {
   170  				errCh <- cs.Subscribe(ctx, bc)
   171  			}()
   172  			<-readyCh
   173  			for i, s := range tc.Steps {
   174  				if s.ArgoEvent != nil {
   175  					bc.ProcessArgoEvent(context.Background(), *s.ArgoEvent)
   176  				}
   177  				if s.KuberpultEvent != nil {
   178  					bc.ProcessKuberpultEvent(context.Background(), *s.KuberpultEvent)
   179  				}
   180  				if s.ExpectedRequest != nil {
   181  					select {
   182  					case <-time.After(5 * time.Second):
   183  						t.Fatalf("expexted request in step %d, but didn't receive any", i)
   184  					case req := <-reqCh:
   185  						d := cmp.Diff(req, s.ExpectedRequest)
   186  						if d != "" {
   187  							t.Errorf("unexpected requests diff in step %d: %s", i, d)
   188  						}
   189  					}
   190  
   191  				} else {
   192  					select {
   193  					case req := <-reqCh:
   194  						t.Errorf("unexpected requests in step %d: %#v", i, req)
   195  					default:
   196  					}
   197  				}
   198  			}
   199  			cancel()
   200  			err := <-errCh
   201  			if err != nil {
   202  				t.Errorf("expected no error but got %q", err)
   203  			}
   204  		})
   205  	}
   206  }