github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/frontend-service/src/ui/utils/store.test.tsx (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  import { act, renderHook } from '@testing-library/react';
    17  import {
    18      addAction,
    19      AllLocks,
    20      appendAction,
    21      DisplayLock,
    22      FlushRolloutStatus,
    23      UpdateAction,
    24      updateActions,
    25      UpdateOverview,
    26      UpdateRolloutStatus,
    27      UpdateSnackbar,
    28      useLocksConflictingWithActions,
    29      useLocksSimilarTo,
    30      useNavigateWithSearchParams,
    31      useRolloutStatus,
    32  } from './store';
    33  import {
    34      BatchAction,
    35      Environment,
    36      EnvironmentGroup,
    37      LockBehavior,
    38      Priority,
    39      RolloutStatus,
    40      StreamStatusResponse,
    41  } from '../../api/api';
    42  import { makeDisplayLock, makeLock } from '../../setupTests';
    43  import { BrowserRouter } from 'react-router-dom';
    44  
    45  describe('Test useLocksSimilarTo', () => {
    46      type TestDataStore = {
    47          name: string;
    48          inputEnvGroups: EnvironmentGroup[]; // this just defines what locks generally exist
    49          inputAction: BatchAction; // the action we are rendering currently in the sidebar
    50          expectedLocks: AllLocks;
    51      };
    52  
    53      const testdata: TestDataStore[] = [
    54          {
    55              name: 'empty data',
    56              inputAction: {
    57                  action: {
    58                      $case: 'deleteEnvironmentLock',
    59                      deleteEnvironmentLock: {
    60                          environment: 'dev',
    61                          lockId: 'l1',
    62                      },
    63                  },
    64              },
    65              inputEnvGroups: [],
    66              expectedLocks: {
    67                  appLocks: [],
    68                  environmentLocks: [],
    69              },
    70          },
    71          {
    72              name: 'one env lock: should not find another lock',
    73              inputAction: {
    74                  action: {
    75                      $case: 'deleteEnvironmentLock',
    76                      deleteEnvironmentLock: {
    77                          environment: 'dev',
    78                          lockId: 'l1',
    79                      },
    80                  },
    81              },
    82              inputEnvGroups: [
    83                  {
    84                      environments: [
    85                          {
    86                              name: 'dev',
    87                              distanceToUpstream: 0,
    88                              priority: Priority.UPSTREAM,
    89                              locks: {
    90                                  l1: makeLock({ lockId: 'l1' }),
    91                              },
    92                              applications: {},
    93                          },
    94                      ],
    95                      environmentGroupName: 'group1',
    96                      distanceToUpstream: 0,
    97                      priority: Priority.UNRECOGNIZED,
    98                  },
    99              ],
   100              expectedLocks: {
   101                  appLocks: [],
   102                  environmentLocks: [],
   103              },
   104          },
   105          {
   106              name: 'two env locks with same ID on different envs: should find the other lock',
   107              inputAction: {
   108                  action: {
   109                      $case: 'deleteEnvironmentLock',
   110                      deleteEnvironmentLock: {
   111                          environment: 'dev',
   112                          lockId: 'l1',
   113                      },
   114                  },
   115              },
   116              inputEnvGroups: [
   117                  {
   118                      environments: [
   119                          {
   120                              name: 'dev',
   121                              distanceToUpstream: 0,
   122                              priority: Priority.UPSTREAM,
   123                              locks: {
   124                                  l1: makeLock({ lockId: 'l1' }),
   125                              },
   126                              applications: {},
   127                          },
   128                          {
   129                              name: 'staging',
   130                              distanceToUpstream: 0,
   131                              priority: Priority.UPSTREAM,
   132                              locks: {
   133                                  l1: makeLock({ lockId: 'l1' }),
   134                              },
   135                              applications: {},
   136                          },
   137                      ],
   138                      environmentGroupName: 'group1',
   139                      distanceToUpstream: 0,
   140                      priority: Priority.UNRECOGNIZED,
   141                  },
   142              ],
   143              expectedLocks: {
   144                  appLocks: [],
   145                  environmentLocks: [
   146                      makeDisplayLock({
   147                          lockId: 'l1',
   148                          environment: 'staging',
   149                      }),
   150                  ],
   151              },
   152          },
   153          {
   154              name: 'env lock and app lock with same ID: deleting the env lock should find the other lock',
   155              inputAction: {
   156                  action: {
   157                      $case: 'deleteEnvironmentLock',
   158                      deleteEnvironmentLock: {
   159                          environment: 'dev',
   160                          lockId: 'l1',
   161                      },
   162                  },
   163              },
   164              inputEnvGroups: [
   165                  {
   166                      environments: [
   167                          {
   168                              name: 'dev',
   169                              distanceToUpstream: 0,
   170                              priority: Priority.UPSTREAM,
   171                              locks: {
   172                                  l1: makeLock({ lockId: 'l1' }),
   173                              },
   174                              applications: {
   175                                  app1: {
   176                                      name: 'betty',
   177                                      locks: {
   178                                          l1: makeLock({ lockId: 'l1' }),
   179                                      },
   180                                      version: 666,
   181                                      undeployVersion: false,
   182                                      queuedVersion: 0,
   183                                      argoCd: undefined,
   184                                  },
   185                              },
   186                          },
   187                      ],
   188                      environmentGroupName: 'group1',
   189                      distanceToUpstream: 0,
   190                      priority: Priority.UNRECOGNIZED,
   191                  },
   192              ],
   193              expectedLocks: {
   194                  appLocks: [
   195                      makeDisplayLock({
   196                          environment: 'dev',
   197                          lockId: 'l1',
   198                          application: 'betty',
   199                          message: 'lock msg 1',
   200                      }),
   201                  ],
   202                  environmentLocks: [],
   203              },
   204          },
   205          {
   206              name: 'bug: delete-all button must appear for each entry. 2 Env Locks, 1 App lock exist. 1 Env lock, 1 app lock in cart',
   207              inputAction: {
   208                  action: {
   209                      $case: 'deleteEnvironmentApplicationLock',
   210                      deleteEnvironmentApplicationLock: {
   211                          environment: 'dev',
   212                          lockId: 'l1',
   213                          application: 'app1',
   214                      },
   215                  },
   216              },
   217              inputEnvGroups: [
   218                  {
   219                      environments: [
   220                          {
   221                              name: 'dev',
   222                              distanceToUpstream: 0,
   223                              priority: Priority.UPSTREAM,
   224                              locks: {
   225                                  l1: makeLock({ lockId: 'l1' }),
   226                              },
   227                              applications: {
   228                                  app1: {
   229                                      name: 'betty',
   230                                      locks: {
   231                                          l1: makeLock({ lockId: 'l1' }),
   232                                      },
   233                                      version: 666,
   234                                      undeployVersion: false,
   235                                      queuedVersion: 0,
   236                                      argoCd: undefined,
   237                                  },
   238                              },
   239                          },
   240                          {
   241                              name: 'dev2',
   242                              distanceToUpstream: 0,
   243                              priority: Priority.UPSTREAM,
   244                              locks: {
   245                                  l1: makeLock({ lockId: 'l1' }),
   246                              },
   247                              applications: {},
   248                          },
   249                      ],
   250                      environmentGroupName: 'group1',
   251                      distanceToUpstream: 0,
   252                      priority: Priority.UNRECOGNIZED,
   253                  },
   254              ],
   255              expectedLocks: {
   256                  appLocks: [
   257                      makeDisplayLock({
   258                          environment: 'dev',
   259                          lockId: 'l1',
   260                          application: 'betty',
   261                          message: 'lock msg 1',
   262                      }),
   263                  ],
   264                  environmentLocks: [
   265                      makeDisplayLock({
   266                          environment: 'dev',
   267                          lockId: 'l1',
   268                          message: 'lock msg 1',
   269                      }),
   270                      makeDisplayLock({
   271                          environment: 'dev2',
   272                          lockId: 'l1',
   273                          message: 'lock msg 1',
   274                      }),
   275                  ],
   276              },
   277          },
   278      ];
   279  
   280      describe.each(testdata)('with', (testcase) => {
   281          it(testcase.name, () => {
   282              // given
   283              updateActions([]);
   284              UpdateOverview.set({
   285                  applications: {},
   286                  environmentGroups: testcase.inputEnvGroups,
   287              });
   288              // when
   289              const actions = renderHook(() => useLocksSimilarTo(testcase.inputAction)).result.current;
   290              // then
   291              expect(actions.appLocks).toStrictEqual(testcase.expectedLocks.appLocks);
   292              expect(actions.environmentLocks).toStrictEqual(testcase.expectedLocks.environmentLocks);
   293          });
   294      });
   295  });
   296  
   297  describe('Test useNavigateWithSearchParams', () => {
   298      type TestDataStore = {
   299          name: string;
   300          currentURL: string;
   301          navigationTo: string;
   302          expectedURL: string;
   303      };
   304  
   305      const testdata: TestDataStore[] = [
   306          {
   307              name: 'url with no search parameters',
   308              currentURL: '',
   309              navigationTo: 'test-random-page',
   310              expectedURL: 'test-random-page',
   311          },
   312          {
   313              name: 'url with some search parameters',
   314              currentURL: '/ui/home/test/whaat?query1=boo&query2=bar',
   315              navigationTo: 'test-random-page',
   316              expectedURL: 'test-random-page?query1=boo&query2=bar',
   317          },
   318      ];
   319  
   320      describe.each(testdata)('with', (testcase) => {
   321          it(testcase.name, () => {
   322              // given
   323              const wrapper = ({ children }: { children: JSX.Element }) => <BrowserRouter>{children}</BrowserRouter>;
   324              window.history.pushState({}, 'Test page', testcase.currentURL);
   325              // when
   326              const result = renderHook(() => useNavigateWithSearchParams(testcase.navigationTo), { wrapper }).result
   327                  .current;
   328              // then
   329              expect(result.navURL).toBe(testcase.expectedURL);
   330              // when
   331              act(() => {
   332                  result.navCallback();
   333              });
   334              // then
   335              expect(window.location.href).toContain(testcase.expectedURL);
   336          });
   337      });
   338  });
   339  
   340  describe('Rollout Status', () => {
   341      type Testcase = {
   342          name: string;
   343          events: Array<StreamStatusResponse | { error: true }>;
   344          expectedApps: Array<{
   345              application: string;
   346              environment: string;
   347              version: number;
   348              rolloutStatus: RolloutStatus | undefined;
   349          }>;
   350      };
   351  
   352      const testdata: Array<Testcase> = [
   353          {
   354              name: 'not enabled if empty',
   355              events: [],
   356  
   357              expectedApps: [
   358                  {
   359                      application: 'app1',
   360                      environment: 'env1',
   361                      version: 0,
   362                      rolloutStatus: undefined,
   363                  },
   364              ],
   365          },
   366          {
   367              name: 'updates one app',
   368              events: [
   369                  {
   370                      environment: 'env1',
   371                      application: 'app1',
   372                      version: 1,
   373                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL,
   374                  },
   375              ],
   376  
   377              expectedApps: [
   378                  {
   379                      application: 'app1',
   380                      environment: 'env1',
   381                      version: 1,
   382                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL,
   383                  },
   384              ],
   385          },
   386          {
   387              name: 'keep the latest entry per app and environment',
   388              events: [
   389                  {
   390                      environment: 'env1',
   391                      application: 'app1',
   392                      version: 1,
   393                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL,
   394                  },
   395                  {
   396                      environment: 'env1',
   397                      application: 'app1',
   398                      version: 2,
   399                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL,
   400                  },
   401              ],
   402  
   403              expectedApps: [
   404                  {
   405                      application: 'app1',
   406                      environment: 'env1',
   407                      version: 2,
   408                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL,
   409                  },
   410              ],
   411          },
   412          {
   413              name: 'flushes the state',
   414              events: [
   415                  {
   416                      environment: 'env1',
   417                      application: 'app1',
   418                      version: 1,
   419                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL,
   420                  },
   421                  { error: true },
   422              ],
   423  
   424              expectedApps: [
   425                  {
   426                      application: 'app1',
   427                      environment: 'env1',
   428                      version: 0,
   429                      rolloutStatus: undefined,
   430                  },
   431              ],
   432          },
   433      ];
   434  
   435      describe.each(testdata)('with', (testcase) => {
   436          it(testcase.name, () => {
   437              FlushRolloutStatus();
   438              testcase.events.forEach((ev) => {
   439                  if ('error' in ev) {
   440                      FlushRolloutStatus();
   441                  } else {
   442                      UpdateRolloutStatus(ev);
   443                  }
   444              });
   445              testcase.expectedApps.forEach((app) => {
   446                  const rollout = renderHook(() =>
   447                      useRolloutStatus((getter) => getter.getAppStatus(app.application, app.version, app.environment))
   448                  );
   449                  expect(rollout.result.current).toEqual(app.rolloutStatus);
   450              });
   451          });
   452      });
   453  });
   454  
   455  describe('Test addAction duplicate detection', () => {
   456      type TestDataStore = {
   457          name: string;
   458          firstAction: BatchAction;
   459          differentAction: BatchAction;
   460      };
   461  
   462      const testdata: TestDataStore[] = [
   463          {
   464              name: 'create environment lock',
   465              firstAction: {
   466                  action: {
   467                      $case: 'createEnvironmentLock',
   468                      createEnvironmentLock: {
   469                          environment: 'dev',
   470                          lockId: 'foo',
   471                          message: 'do it',
   472                      },
   473                  },
   474              },
   475              differentAction: {
   476                  action: {
   477                      $case: 'createEnvironmentLock',
   478                      createEnvironmentLock: {
   479                          environment: 'staging',
   480                          lockId: 'foo',
   481                          message: 'do it',
   482                      },
   483                  },
   484              },
   485          },
   486          {
   487              name: 'delete environment lock',
   488              firstAction: {
   489                  action: {
   490                      $case: 'deleteEnvironmentLock',
   491                      deleteEnvironmentLock: {
   492                          environment: 'dev',
   493                          lockId: 'foo',
   494                      },
   495                  },
   496              },
   497              differentAction: {
   498                  action: {
   499                      $case: 'deleteEnvironmentLock',
   500                      deleteEnvironmentLock: {
   501                          environment: 'staging',
   502                          lockId: 'foo',
   503                      },
   504                  },
   505              },
   506          },
   507          {
   508              name: 'create app lock',
   509              firstAction: {
   510                  action: {
   511                      $case: 'createEnvironmentApplicationLock',
   512                      createEnvironmentApplicationLock: {
   513                          environment: 'dev',
   514                          application: 'app1',
   515                          lockId: 'foo',
   516                          message: 'do it',
   517                      },
   518                  },
   519              },
   520              differentAction: {
   521                  action: {
   522                      $case: 'createEnvironmentApplicationLock',
   523                      createEnvironmentApplicationLock: {
   524                          environment: 'dev',
   525                          application: 'app2',
   526                          lockId: 'foo',
   527                          message: 'do it',
   528                      },
   529                  },
   530              },
   531          },
   532          {
   533              name: 'delete app lock',
   534              firstAction: {
   535                  action: {
   536                      $case: 'deleteEnvironmentApplicationLock',
   537                      deleteEnvironmentApplicationLock: {
   538                          environment: 'dev',
   539                          application: 'app1',
   540                          lockId: 'foo',
   541                      },
   542                  },
   543              },
   544              differentAction: {
   545                  action: {
   546                      $case: 'deleteEnvironmentApplicationLock',
   547                      deleteEnvironmentApplicationLock: {
   548                          environment: 'dev',
   549                          application: 'app2',
   550                          lockId: 'foo',
   551                      },
   552                  },
   553              },
   554          },
   555          {
   556              name: 'deploy',
   557              firstAction: {
   558                  action: {
   559                      $case: 'deploy',
   560                      deploy: {
   561                          environment: 'dev',
   562                          application: 'app1',
   563                          version: 1,
   564                          ignoreAllLocks: false,
   565                          lockBehavior: LockBehavior.IGNORE,
   566                      },
   567                  },
   568              },
   569              differentAction: {
   570                  action: {
   571                      $case: 'deploy',
   572                      deploy: {
   573                          environment: 'dev',
   574                          application: 'app2',
   575                          version: 1,
   576                          ignoreAllLocks: false,
   577                          lockBehavior: LockBehavior.IGNORE,
   578                      },
   579                  },
   580              },
   581          },
   582          {
   583              name: 'undeploy',
   584              firstAction: {
   585                  action: {
   586                      $case: 'undeploy',
   587                      undeploy: {
   588                          application: 'app1',
   589                      },
   590                  },
   591              },
   592              differentAction: {
   593                  action: {
   594                      $case: 'undeploy',
   595                      undeploy: {
   596                          application: 'app2',
   597                      },
   598                  },
   599              },
   600          },
   601          {
   602              name: 'prepare undeploy',
   603              firstAction: {
   604                  action: {
   605                      $case: 'prepareUndeploy',
   606                      prepareUndeploy: {
   607                          application: 'app1',
   608                      },
   609                  },
   610              },
   611              differentAction: {
   612                  action: {
   613                      $case: 'prepareUndeploy',
   614                      prepareUndeploy: {
   615                          application: 'app2',
   616                      },
   617                  },
   618              },
   619          },
   620      ];
   621  
   622      describe.each(testdata)('with', (testcase) => {
   623          it(testcase.name, () => {
   624              // given
   625              updateActions([]);
   626  
   627              // when
   628              addAction(testcase.firstAction);
   629              // then
   630              expect(UpdateAction.get().actions.length).toStrictEqual(1);
   631  
   632              // when
   633              addAction(testcase.firstAction);
   634              // then
   635              expect(UpdateAction.get().actions.length).toStrictEqual(1);
   636  
   637              // when
   638              addAction(testcase.differentAction);
   639              // then
   640              expect(UpdateAction.get().actions.length).toStrictEqual(2);
   641  
   642              // when
   643              addAction(testcase.differentAction);
   644              // then
   645              expect(UpdateAction.get().actions.length).toStrictEqual(2);
   646          });
   647      });
   648  });
   649  
   650  describe('Test maxActions', () => {
   651      type TestDataStore = {
   652          name: string;
   653          inputActionsLen: number;
   654          expectedLen: number;
   655          expectedShowError: boolean;
   656      };
   657  
   658      const testdata: TestDataStore[] = [
   659          {
   660              name: 'below limit',
   661              inputActionsLen: 99,
   662              expectedLen: 99,
   663              expectedShowError: false,
   664          },
   665          {
   666              name: 'at limit',
   667              inputActionsLen: 100,
   668              expectedLen: 100,
   669              expectedShowError: false,
   670          },
   671          {
   672              name: 'over limit',
   673              inputActionsLen: 101,
   674              expectedLen: 100,
   675              expectedShowError: true,
   676          },
   677      ];
   678  
   679      describe.each(testdata)('with', (testcase) => {
   680          it(testcase.name, () => {
   681              // given
   682              updateActions([]);
   683  
   684              // when
   685              for (let i = 0; i < testcase.inputActionsLen; i++) {
   686                  appendAction([
   687                      {
   688                          action: {
   689                              $case: 'deploy',
   690                              deploy: {
   691                                  environment: 'foo',
   692                                  application: 'bread' + i,
   693                                  version: i,
   694                                  ignoreAllLocks: false,
   695                                  lockBehavior: LockBehavior.IGNORE,
   696                              },
   697                          },
   698                      },
   699                  ]);
   700              }
   701              // then
   702              expect(UpdateSnackbar.get().show).toStrictEqual(testcase.expectedShowError);
   703              expect(UpdateAction.get().actions.length).toStrictEqual(testcase.expectedLen);
   704          });
   705      });
   706  });
   707  
   708  describe('Test useLocksConflictingWithActions', () => {
   709      type TestDataStore = {
   710          name: string;
   711          actions: BatchAction[];
   712          expectedAppLocks: DisplayLock[];
   713          expectedEnvLocks: DisplayLock[];
   714          environments: Environment[];
   715      };
   716  
   717      const testdata: TestDataStore[] = [
   718          {
   719              name: 'empty actions empty locks',
   720              actions: [],
   721              expectedAppLocks: [],
   722              expectedEnvLocks: [],
   723              environments: [],
   724          },
   725          {
   726              name: 'deploy action and related app lock and env lock',
   727              actions: [
   728                  {
   729                      action: {
   730                          $case: 'deploy',
   731                          deploy: {
   732                              environment: 'dev',
   733                              application: 'app1',
   734                              version: 1,
   735                              ignoreAllLocks: false,
   736                              lockBehavior: LockBehavior.IGNORE,
   737                          },
   738                      },
   739                  },
   740              ],
   741              environments: [
   742                  {
   743                      name: 'dev',
   744                      locks: {
   745                          'lock-env-dev': makeLock({
   746                              message: 'locked because christmas',
   747                              lockId: 'my-env-lock1',
   748                          }),
   749                      },
   750                      applications: {
   751                          echo: {
   752                              name: 'app1',
   753                              version: 0,
   754                              locks: {
   755                                  applock: makeLock({
   756                                      lockId: 'app-lock-id',
   757                                      message: 'i do not like this app',
   758                                  }),
   759                              },
   760                              queuedVersion: 0,
   761                              undeployVersion: false,
   762                          },
   763                      },
   764                      distanceToUpstream: 0,
   765                      priority: 0,
   766                  },
   767              ],
   768              expectedAppLocks: [
   769                  makeDisplayLock({
   770                      lockId: 'app-lock-id',
   771                      application: 'app1',
   772                      message: 'i do not like this app',
   773                      environment: 'dev',
   774                  }),
   775              ],
   776              expectedEnvLocks: [
   777                  makeDisplayLock({
   778                      lockId: 'my-env-lock1',
   779                      environment: 'dev',
   780                      message: 'locked because christmas',
   781                  }),
   782              ],
   783          },
   784          {
   785              name: 'deploy action and unrelated locks',
   786              actions: [
   787                  {
   788                      action: {
   789                          $case: 'deploy',
   790                          deploy: {
   791                              environment: 'dev',
   792                              application: 'app1',
   793                              version: 1,
   794                              ignoreAllLocks: false,
   795                              lockBehavior: LockBehavior.IGNORE,
   796                          },
   797                      },
   798                  },
   799              ],
   800              environments: [
   801                  {
   802                      name: 'staging', // this lock differs by stage
   803                      locks: {
   804                          'lock-env-dev': makeLock({
   805                              message: 'locked because christmas',
   806                              lockId: 'my-env-lock1',
   807                          }),
   808                      },
   809                      applications: {
   810                          echo: {
   811                              name: 'anotherapp', // this lock differs by app
   812                              version: 0,
   813                              locks: {
   814                                  applock: makeLock({
   815                                      lockId: 'app-lock-id',
   816                                      message: 'i do not like this app',
   817                                  }),
   818                              },
   819                              queuedVersion: 0,
   820                              undeployVersion: false,
   821                          },
   822                      },
   823                      distanceToUpstream: 0,
   824                      priority: 0,
   825                  },
   826              ],
   827              expectedAppLocks: [],
   828              expectedEnvLocks: [],
   829          },
   830      ];
   831  
   832      describe.each(testdata)('with', (testcase) => {
   833          it(testcase.name, () => {
   834              // given
   835              updateActions(testcase.actions);
   836              UpdateOverview.set({
   837                  applications: {},
   838                  environmentGroups: [
   839                      {
   840                          environmentGroupName: 'g1',
   841                          environments: testcase.environments,
   842                          distanceToUpstream: 0,
   843                          priority: Priority.UNRECOGNIZED,
   844                      },
   845                  ],
   846              });
   847  
   848              // when
   849              const actualLocks = renderHook(() => useLocksConflictingWithActions()).result.current;
   850              // then
   851              expect(actualLocks.environmentLocks).toStrictEqual(testcase.expectedEnvLocks);
   852              expect(actualLocks.appLocks).toStrictEqual(testcase.expectedAppLocks);
   853          });
   854      });
   855  });
   856  
   857  describe('Test addAction blocking release train additions', () => {
   858      type TestDataStore = {
   859          name: string;
   860          firstAction: BatchAction;
   861          differentAction: BatchAction;
   862          expectedActions: number;
   863      };
   864  
   865      const testdata: TestDataStore[] = [
   866          {
   867              name: 'deploy 2 in a row',
   868              expectedActions: 2,
   869              firstAction: {
   870                  action: {
   871                      $case: 'deploy',
   872                      deploy: {
   873                          environment: 'dev',
   874                          application: 'app1',
   875                          version: 1,
   876                          ignoreAllLocks: false,
   877                          lockBehavior: LockBehavior.IGNORE,
   878                      },
   879                  },
   880              },
   881              differentAction: {
   882                  action: {
   883                      $case: 'deploy',
   884                      deploy: {
   885                          environment: 'dev',
   886                          application: 'app2',
   887                          version: 1,
   888                          ignoreAllLocks: false,
   889                          lockBehavior: LockBehavior.IGNORE,
   890                      },
   891                  },
   892              },
   893          },
   894          {
   895              name: 'can not add release train after deploy action',
   896              expectedActions: 1,
   897              firstAction: {
   898                  action: {
   899                      $case: 'deploy',
   900                      deploy: {
   901                          environment: 'dev',
   902                          application: 'app1',
   903                          version: 1,
   904                          ignoreAllLocks: false,
   905                          lockBehavior: LockBehavior.IGNORE,
   906                      },
   907                  },
   908              },
   909              differentAction: {
   910                  action: {
   911                      $case: 'releaseTrain',
   912                      releaseTrain: {
   913                          target: 'dev',
   914                          team: '',
   915                          commitHash: '',
   916                      },
   917                  },
   918              },
   919          },
   920          {
   921              name: 'can not add release train after release train',
   922              expectedActions: 1,
   923              firstAction: {
   924                  action: {
   925                      $case: 'releaseTrain',
   926                      releaseTrain: {
   927                          target: 'dev',
   928                          team: '',
   929                          commitHash: '',
   930                      },
   931                  },
   932              },
   933              differentAction: {
   934                  action: {
   935                      $case: 'releaseTrain',
   936                      releaseTrain: {
   937                          target: 'stagin',
   938                          team: '',
   939                          commitHash: '',
   940                      },
   941                  },
   942              },
   943          },
   944          {
   945              name: 'can not add deploy action after release train',
   946              expectedActions: 1,
   947              firstAction: {
   948                  action: {
   949                      $case: 'releaseTrain',
   950                      releaseTrain: {
   951                          target: 'dev',
   952                          team: '',
   953                          commitHash: '',
   954                      },
   955                  },
   956              },
   957              differentAction: {
   958                  action: {
   959                      $case: 'deploy',
   960                      deploy: {
   961                          environment: 'dev',
   962                          application: 'app1',
   963                          version: 1,
   964                          ignoreAllLocks: false,
   965                          lockBehavior: LockBehavior.IGNORE,
   966                      },
   967                  },
   968              },
   969          },
   970      ];
   971  
   972      describe.each(testdata)('with', (testcase) => {
   973          it(testcase.name, () => {
   974              // given
   975              updateActions([]);
   976  
   977              // when
   978              addAction(testcase.firstAction);
   979              // then
   980              expect(UpdateAction.get().actions.length).toStrictEqual(1);
   981  
   982              // when
   983              addAction(testcase.differentAction);
   984              // then
   985              expect(UpdateAction.get().actions.length).toStrictEqual(testcase.expectedActions);
   986          });
   987      });
   988  });