github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/service/version_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 service
    18  
    19  import (
    20  	"context"
    21  	"crypto/md5"
    22  	"fmt"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/repository/testutil"
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/google/go-cmp/cmp/cmpopts"
    29  	"google.golang.org/grpc/codes"
    30  	"google.golang.org/grpc/status"
    31  	"google.golang.org/protobuf/testing/protocmp"
    32  
    33  	api "github.com/freiheit-com/kuberpult/pkg/api/v1"
    34  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/config"
    35  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/repository"
    36  )
    37  
    38  func TestVersion(t *testing.T) {
    39  	type expectedVersion struct {
    40  		Environment            string
    41  		Application            string
    42  		ExpectedVersion        uint64
    43  		ExpectedDeployedAt     time.Time
    44  		ExpectedSourceCommitId string
    45  	}
    46  	tcs := []struct {
    47  		Name             string
    48  		Setup            []repository.Transformer
    49  		ExpectedVersions []expectedVersion
    50  	}{
    51  		{
    52  			Name: "simple-tests",
    53  			Setup: []repository.Transformer{
    54  				&repository.CreateEnvironment{
    55  					Environment: "development",
    56  					Config: config.EnvironmentConfig{
    57  						Upstream: &config.EnvironmentConfigUpstream{
    58  							Latest: true,
    59  						},
    60  					},
    61  				},
    62  				&repository.CreateEnvironment{
    63  					Environment: "staging",
    64  					Config: config.EnvironmentConfig{
    65  						Upstream: &config.EnvironmentConfigUpstream{
    66  							Latest: true,
    67  						},
    68  					},
    69  				},
    70  				&repository.CreateApplicationVersion{
    71  					Application: "test",
    72  					Version:     1,
    73  					Manifests: map[string]string{
    74  						"development": "dev",
    75  					},
    76  				},
    77  			},
    78  			ExpectedVersions: []expectedVersion{
    79  				{
    80  					Environment:        "development",
    81  					Application:        "test",
    82  					ExpectedVersion:    1,
    83  					ExpectedDeployedAt: time.Unix(2, 0),
    84  				},
    85  				{
    86  					Environment:     "staging",
    87  					Application:     "test",
    88  					ExpectedVersion: 0,
    89  				},
    90  			},
    91  		},
    92  		{
    93  			Name: "with source commits",
    94  			Setup: []repository.Transformer{
    95  				&repository.CreateEnvironment{
    96  					Environment: "development",
    97  					Config: config.EnvironmentConfig{
    98  						Upstream: &config.EnvironmentConfigUpstream{
    99  							Latest: true,
   100  						},
   101  					},
   102  				},
   103  				&repository.CreateEnvironment{
   104  					Environment: "staging",
   105  					Config: config.EnvironmentConfig{
   106  						Upstream: &config.EnvironmentConfigUpstream{
   107  							Latest: true,
   108  						},
   109  					},
   110  				},
   111  				&repository.CreateApplicationVersion{
   112  					Application: "test",
   113  					Version:     1,
   114  					Manifests: map[string]string{
   115  						"development": "dev",
   116  					},
   117  					SourceCommitId: "deadbeef",
   118  				},
   119  			},
   120  			ExpectedVersions: []expectedVersion{
   121  				{
   122  					Environment:            "development",
   123  					Application:            "test",
   124  					ExpectedVersion:        1,
   125  					ExpectedDeployedAt:     time.Unix(2, 0),
   126  					ExpectedSourceCommitId: "deadbeef",
   127  				},
   128  			},
   129  		},
   130  	}
   131  	for _, tc := range tcs {
   132  		tc := tc
   133  		t.Run(tc.Name, func(t *testing.T) {
   134  			repo, err := setupRepositoryTest(t)
   135  			if err != nil {
   136  				t.Fatalf("error setting up repository test: %v", err)
   137  			}
   138  			sv := &VersionServiceServer{Repository: repo}
   139  
   140  			for i, transformer := range tc.Setup {
   141  				now := time.Unix(int64(i), 0)
   142  				ctx := repository.WithTimeNow(testutil.MakeTestContext(), now)
   143  				err := repo.Apply(ctx, transformer)
   144  				if err != nil {
   145  					t.Fatal(err)
   146  				}
   147  			}
   148  			cid := repo.State().Commit.Id().String()
   149  			for _, ev := range tc.ExpectedVersions {
   150  				res, err := sv.GetVersion(context.Background(), &api.GetVersionRequest{
   151  					GitRevision: cid,
   152  					Application: ev.Application,
   153  					Environment: ev.Environment,
   154  				})
   155  				if err != nil {
   156  					t.Fatal(err)
   157  				}
   158  				if res.Version != ev.ExpectedVersion {
   159  					t.Errorf("got wrong version for %s/%s: expected %d but got %d", ev.Application, ev.Environment, ev.ExpectedVersion, res.Version)
   160  				}
   161  				if ev.ExpectedDeployedAt.IsZero() {
   162  					if res.DeployedAt != nil {
   163  						t.Errorf("got wrong deployed at for %s/%s: expected <nil> but got %q", ev.Application, ev.Environment, res.DeployedAt)
   164  					}
   165  				} else {
   166  					if !res.DeployedAt.AsTime().Equal(ev.ExpectedDeployedAt) {
   167  						t.Errorf("got wrong deployed at for %s/%s: expected %q but got %q", ev.Application, ev.Environment, ev.ExpectedDeployedAt, res.DeployedAt.AsTime())
   168  					}
   169  				}
   170  				if ev.ExpectedSourceCommitId != res.SourceCommitId {
   171  					t.Errorf("go wrong source commit id, expected %q, got %q", ev.ExpectedSourceCommitId, res.SourceCommitId)
   172  				}
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func TestGetManifests(t *testing.T) {
   179  	type testCase struct {
   180  		name    string
   181  		setup   []repository.Transformer
   182  		req     *api.GetManifestsRequest
   183  		want    *api.GetManifestsResponse
   184  		wantErr error
   185  	}
   186  
   187  	appName := "app-default"
   188  	appNameOther := "app-other"
   189  	fixtureRequest := func(mods ...func(*api.GetManifestsRequest)) *api.GetManifestsRequest {
   190  		req := &api.GetManifestsRequest{
   191  			Application: appName,
   192  			Release:     "latest",
   193  		}
   194  		for _, m := range mods {
   195  			m(req)
   196  		}
   197  		return req
   198  	}
   199  	fixtureSetupEnv := func() []repository.Transformer {
   200  		return []repository.Transformer{
   201  			&repository.CreateEnvironment{
   202  				Environment: "development",
   203  				Config: config.EnvironmentConfig{
   204  					Upstream: &config.EnvironmentConfigUpstream{
   205  						Latest: true,
   206  					},
   207  				},
   208  			},
   209  			&repository.CreateEnvironment{
   210  				Environment: "staging",
   211  				Config: config.EnvironmentConfig{
   212  					Upstream: &config.EnvironmentConfigUpstream{
   213  						Latest: true,
   214  					},
   215  				},
   216  			},
   217  		}
   218  	}
   219  	fixtureRelease := func(application string, release uint64) *repository.CreateApplicationVersion {
   220  		return &repository.CreateApplicationVersion{
   221  			Application: application,
   222  			Version:     release,
   223  			Manifests: map[string]string{
   224  				"development": fmt.Sprintf("dev-manifest for %s in release %d", application, release),
   225  				"staging":     fmt.Sprintf("staging-manifest for %s in release %d", application, release),
   226  			},
   227  			SourceCommitId: fmt.Sprintf("%x",
   228  				md5.Sum(
   229  					[]byte(
   230  						fmt.Sprintf("source commit id for app %s and release %d", application, release),
   231  					),
   232  				),
   233  			),
   234  		}
   235  	}
   236  
   237  	fixtureReleaseToManifests := func(release *repository.CreateApplicationVersion) *api.GetManifestsResponse {
   238  		return &api.GetManifestsResponse{
   239  			Release: &api.Release{
   240  				Version:        release.Version,
   241  				SourceCommitId: release.SourceCommitId,
   242  			},
   243  			Manifests: map[string]*api.Manifest{
   244  				"development": {
   245  					Environment: "development",
   246  					Content:     release.Manifests["development"],
   247  				},
   248  				"staging": {
   249  					Environment: "staging",
   250  					Content:     release.Manifests["staging"],
   251  				},
   252  			},
   253  		}
   254  	}
   255  
   256  	for _, tc := range []*testCase{
   257  		func() *testCase {
   258  			release := fixtureRelease(appName, 3)
   259  
   260  			return &testCase{
   261  				name: "happy path",
   262  				setup: append(fixtureSetupEnv(),
   263  					fixtureRelease(appNameOther, 1),
   264  					fixtureRelease(appNameOther, 2),
   265  					fixtureRelease(appName, 1),
   266  					fixtureRelease(appName, 2),
   267  					release,
   268  				),
   269  				req:  fixtureRequest(),
   270  				want: fixtureReleaseToManifests(release),
   271  			}
   272  		}(),
   273  		func() *testCase {
   274  			release := fixtureRelease(appName, 2)
   275  
   276  			return &testCase{
   277  				name: "request specific release",
   278  				setup: append(fixtureSetupEnv(),
   279  					fixtureRelease(appName, 1),
   280  					fixtureRelease(appNameOther, 1),
   281  					release,
   282  					fixtureRelease(appName, 3),
   283  					fixtureRelease(appNameOther, 2),
   284  				),
   285  				req:  fixtureRequest(func(req *api.GetManifestsRequest) { req.Release = "2" }),
   286  				want: fixtureReleaseToManifests(release),
   287  			}
   288  		}(),
   289  		{
   290  			name: "no release specified",
   291  			setup: append(fixtureSetupEnv(),
   292  				fixtureRelease(appName, 1),
   293  				fixtureRelease(appName, 2),
   294  				fixtureRelease(appName, 3),
   295  			),
   296  			req:     fixtureRequest(func(req *api.GetManifestsRequest) { req.Release = "" }),
   297  			wantErr: status.Error(codes.InvalidArgument, "invalid release number, expected uint or 'latest'"),
   298  		},
   299  		{
   300  			name: "no application specified",
   301  			setup: append(fixtureSetupEnv(),
   302  				fixtureRelease(appName, 1),
   303  				fixtureRelease(appName, 2),
   304  				fixtureRelease(appName, 3),
   305  			),
   306  			req:     fixtureRequest(func(req *api.GetManifestsRequest) { req.Application = "" }),
   307  			wantErr: status.Error(codes.InvalidArgument, "no application specified"),
   308  		},
   309  		{
   310  			name: "no releases for application",
   311  			setup: append(fixtureSetupEnv(),
   312  				fixtureRelease(appNameOther, 1),
   313  				fixtureRelease(appNameOther, 2),
   314  				fixtureRelease(appNameOther, 3),
   315  			),
   316  			req:     fixtureRequest(),
   317  			wantErr: status.Errorf(codes.NotFound, "no releases found for application %s", appName),
   318  		},
   319  	} {
   320  		tc := tc // TODO SRX-SRRONB: Remove after switching to go v1.22
   321  		t.Run(tc.name, func(t *testing.T) {
   322  			repo, err := setupRepositoryTest(t)
   323  			if err != nil {
   324  				t.Fatalf("error setting up repository test: %v", err)
   325  			}
   326  			sv := &VersionServiceServer{Repository: repo}
   327  
   328  			for i, transformer := range tc.setup {
   329  				now := time.Unix(int64(i), 0)
   330  				ctx := repository.WithTimeNow(testutil.MakeTestContext(), now)
   331  				err := repo.Apply(ctx, transformer)
   332  				if err != nil {
   333  					t.Fatal(err)
   334  				}
   335  			}
   336  
   337  			got, err := sv.GetManifests(context.Background(), tc.req)
   338  			if diff := cmp.Diff(tc.wantErr, err, cmpopts.EquateErrors()); diff != "" {
   339  				t.Errorf("error mismatch (-want, +got):\n%s", diff)
   340  			}
   341  			if diff := cmp.Diff(tc.want, got, protocmp.Transform(), protocmp.IgnoreFields(&api.Release{}, "created_at")); diff != "" {
   342  				t.Errorf("response mismatch (-want, +got):\n%s", diff)
   343  			}
   344  		})
   345  	}
   346  }