github.com/kubeshop/testkube@v1.17.23/pkg/triggers/service_test.go (about)

     1  package triggers
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/golang/mock/gomock"
     9  	"github.com/stretchr/testify/assert"
    10  	corev1 "k8s.io/api/core/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/client-go/kubernetes/fake"
    13  
    14  	executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1"
    15  	testsv3 "github.com/kubeshop/testkube-operator/api/tests/v3"
    16  	testtriggersv1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1"
    17  	executorsclientv1 "github.com/kubeshop/testkube-operator/pkg/client/executors/v1"
    18  	testsclientv3 "github.com/kubeshop/testkube-operator/pkg/client/tests/v3"
    19  	testsourcesv1 "github.com/kubeshop/testkube-operator/pkg/client/testsources/v1"
    20  	testsuiteexecutionsv1 "github.com/kubeshop/testkube-operator/pkg/client/testsuiteexecutions/v1"
    21  	testsuitesv3 "github.com/kubeshop/testkube-operator/pkg/client/testsuites/v3"
    22  	faketestkube "github.com/kubeshop/testkube-operator/pkg/clientset/versioned/fake"
    23  	"github.com/kubeshop/testkube/internal/app/api/metrics"
    24  	"github.com/kubeshop/testkube/pkg/api/v1/testkube"
    25  	"github.com/kubeshop/testkube/pkg/configmap"
    26  	"github.com/kubeshop/testkube/pkg/event"
    27  	"github.com/kubeshop/testkube/pkg/event/bus"
    28  	"github.com/kubeshop/testkube/pkg/executor/client"
    29  	"github.com/kubeshop/testkube/pkg/featureflags"
    30  	"github.com/kubeshop/testkube/pkg/log"
    31  	logsclient "github.com/kubeshop/testkube/pkg/logs/client"
    32  	"github.com/kubeshop/testkube/pkg/repository/config"
    33  	"github.com/kubeshop/testkube/pkg/repository/result"
    34  	"github.com/kubeshop/testkube/pkg/repository/testresult"
    35  	"github.com/kubeshop/testkube/pkg/scheduler"
    36  	"github.com/kubeshop/testkube/pkg/secret"
    37  )
    38  
    39  func TestService_Run(t *testing.T) {
    40  	t.Parallel()
    41  
    42  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    43  	defer cancel()
    44  	testMetrics := metrics.NewMetrics()
    45  
    46  	mockCtrl := gomock.NewController(t)
    47  	defer mockCtrl.Finish()
    48  
    49  	mockBus := bus.NewEventBusMock()
    50  	mockResultRepository := result.NewMockRepository(mockCtrl)
    51  	mockTestResultRepository := testresult.NewMockRepository(mockCtrl)
    52  
    53  	mockExecutorsClient := executorsclientv1.NewMockInterface(mockCtrl)
    54  	mockTestsClient := testsclientv3.NewMockInterface(mockCtrl)
    55  	mockTestSuitesClient := testsuitesv3.NewMockInterface(mockCtrl)
    56  	mockTestSourcesClient := testsourcesv1.NewMockInterface(mockCtrl)
    57  	mockSecretClient := secret.NewMockInterface(mockCtrl)
    58  	configMapConfig := config.NewMockRepository(mockCtrl)
    59  	mockConfigMapClient := configmap.NewMockInterface(mockCtrl)
    60  	mockTestSuiteExecutionsClient := testsuiteexecutionsv1.NewMockInterface(mockCtrl)
    61  
    62  	mockExecutor := client.NewMockExecutor(mockCtrl)
    63  
    64  	mockEventEmitter := event.NewEmitter(bus.NewEventBusMock(), "", nil)
    65  
    66  	mockTest := testsv3.Test{
    67  		ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "some-test"},
    68  		Spec: testsv3.TestSpec{
    69  			Type_: "cypress",
    70  			ExecutionRequest: &testsv3.ExecutionRequest{
    71  				Name:   "some-custom-execution",
    72  				Number: 1,
    73  				Image:  "test-image",
    74  			},
    75  		},
    76  	}
    77  	mockTestsClient.EXPECT().Get("some-test").Return(&mockTest, nil).AnyTimes()
    78  	var mockNextExecutionNumber int32 = 1
    79  	mockResultRepository.EXPECT().GetNextExecutionNumber(gomock.Any(), "some-test").Return(mockNextExecutionNumber, nil)
    80  	mockExecutionResult := testkube.ExecutionResult{Status: testkube.ExecutionStatusRunning}
    81  	mockExecution := testkube.Execution{Name: "test-execution-1"}
    82  	mockExecution.ExecutionResult = &mockExecutionResult
    83  	mockResultRepository.EXPECT().GetByNameAndTest(gomock.Any(), "some-custom-execution", "some-test").Return(mockExecution, nil)
    84  	mockSecretUUID := "b524c2f6-6bcf-4178-87c1-1aa2b2abb5dc"
    85  	mockTestsClient.EXPECT().GetCurrentSecretUUID("some-test").Return(mockSecretUUID, nil)
    86  	mockExecutorTypes := "cypress"
    87  	mockExecutorV1 := executorv1.Executor{
    88  		TypeMeta:   metav1.TypeMeta{},
    89  		ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "cypress"},
    90  		Spec: executorv1.ExecutorSpec{
    91  			Types:                  []string{mockExecutorTypes},
    92  			ExecutorType:           "job",
    93  			URI:                    "",
    94  			Image:                  "cypress",
    95  			Args:                   nil,
    96  			Command:                []string{"run"},
    97  			ImagePullSecrets:       nil,
    98  			Features:               nil,
    99  			ContentTypes:           nil,
   100  			JobTemplate:            "",
   101  			JobTemplateReference:   "",
   102  			Meta:                   nil,
   103  			UseDataDirAsWorkingDir: false,
   104  		},
   105  	}
   106  	mockExecutorsClient.EXPECT().GetByType(mockExecutorTypes).Return(&mockExecutorV1, nil).AnyTimes()
   107  	mockResultRepository.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil)
   108  	mockResultRepository.EXPECT().StartExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
   109  	mockExecutor.EXPECT().Execute(gomock.Any(), gomock.Any(), gomock.Any()).Return(&mockExecutionResult, nil)
   110  	mockResultRepository.EXPECT().UpdateResult(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
   111  
   112  	mockTestExecution := testkube.Execution{
   113  		Id:              "test-suite-execution-1",
   114  		ExecutionResult: &testkube.ExecutionResult{Status: testkube.ExecutionStatusPassed},
   115  	}
   116  	mockResultRepository.EXPECT().Get(gomock.Any(), gomock.Any()).Return(mockTestExecution, nil)
   117  
   118  	testLogger := log.DefaultLogger
   119  
   120  	mockLogsStream := logsclient.NewMockStream(mockCtrl)
   121  
   122  	sched := scheduler.NewScheduler(
   123  		testMetrics,
   124  		mockExecutor,
   125  		mockExecutor,
   126  		mockResultRepository,
   127  		mockTestResultRepository,
   128  		mockExecutorsClient,
   129  		mockTestsClient,
   130  		mockTestSuitesClient,
   131  		mockTestSourcesClient,
   132  		mockSecretClient,
   133  		mockEventEmitter,
   134  		testLogger,
   135  		configMapConfig,
   136  		mockConfigMapClient,
   137  		mockTestSuiteExecutionsClient,
   138  		mockBus,
   139  		"",
   140  		featureflags.FeatureFlags{},
   141  		mockLogsStream,
   142  		"",
   143  		"",
   144  		"",
   145  	)
   146  
   147  	mockLeaseBackend := NewMockLeaseBackend(mockCtrl)
   148  	testClusterID := "testkube-api"
   149  	testIdentifier := "test-host-1"
   150  	mockLeaseBackend.EXPECT().TryAcquire(gomock.Any(), testIdentifier, testClusterID).Return(true, nil).AnyTimes()
   151  
   152  	fakeTestkubeClientset := faketestkube.NewSimpleClientset()
   153  	fakeClientset := fake.NewSimpleClientset()
   154  	eventBus := bus.NewEventBusMock()
   155  	metrics := metrics.NewMetrics()
   156  	s := NewService(
   157  		sched,
   158  		fakeClientset,
   159  		fakeTestkubeClientset,
   160  		mockTestSuitesClient,
   161  		mockTestsClient,
   162  		mockResultRepository,
   163  		mockTestResultRepository,
   164  		mockLeaseBackend,
   165  		testLogger,
   166  		configMapConfig,
   167  		mockExecutorsClient,
   168  		mockExecutor,
   169  		eventBus,
   170  		metrics,
   171  		WithClusterID(testClusterID),
   172  		WithIdentifier(testIdentifier),
   173  		WithScraperInterval(50*time.Millisecond),
   174  		WithLeaseCheckerInterval(50*time.Millisecond),
   175  	)
   176  
   177  	s.Run(ctx)
   178  
   179  	time.Sleep(100 * time.Millisecond)
   180  
   181  	testNamespace := "testkube"
   182  	testTrigger := testtriggersv1.TestTrigger{
   183  		ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-trigger-1"},
   184  		Spec: testtriggersv1.TestTriggerSpec{
   185  			Resource:          "pod",
   186  			ResourceSelector:  testtriggersv1.TestTriggerSelector{Name: "test-pod"},
   187  			Event:             "created",
   188  			Action:            "run",
   189  			Execution:         "test",
   190  			ConcurrencyPolicy: "allow",
   191  			TestSelector:      testtriggersv1.TestTriggerSelector{Name: "some-test"},
   192  		},
   193  	}
   194  	createdTestTrigger, err := fakeTestkubeClientset.TestsV1().TestTriggers(testNamespace).Create(ctx, &testTrigger, metav1.CreateOptions{})
   195  	assert.NotNil(t, createdTestTrigger)
   196  	assert.NoError(t, err)
   197  
   198  	time.Sleep(10 * time.Millisecond)
   199  
   200  	assert.Len(t, s.triggerStatus, 1)
   201  	key := newStatusKey(testNamespace, "test-trigger-1")
   202  	assert.Contains(t, s.triggerStatus, key)
   203  
   204  	testPod := corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-pod", CreationTimestamp: metav1.Now()}}
   205  	_, err = fakeClientset.CoreV1().Pods(testNamespace).Create(ctx, &testPod, metav1.CreateOptions{})
   206  	assert.NoError(t, err)
   207  
   208  	<-ctx.Done()
   209  }
   210  
   211  func TestService_addTrigger(t *testing.T) {
   212  	t.Parallel()
   213  
   214  	s := Service{triggerStatus: make(map[statusKey]*triggerStatus)}
   215  
   216  	testTrigger := testtriggersv1.TestTrigger{
   217  		ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-1", Namespace: "testkube"},
   218  	}
   219  	s.addTrigger(&testTrigger)
   220  
   221  	assert.Len(t, s.triggerStatus, 1)
   222  	key := newStatusKey("testkube", "test-trigger-1")
   223  	assert.NotNil(t, s.triggerStatus[key])
   224  }
   225  
   226  func TestService_removeTrigger(t *testing.T) {
   227  	t.Parallel()
   228  
   229  	s := Service{triggerStatus: make(map[statusKey]*triggerStatus)}
   230  
   231  	testTrigger1 := testtriggersv1.TestTrigger{
   232  		ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-1", Namespace: "testkube"},
   233  	}
   234  	testTrigger2 := testtriggersv1.TestTrigger{
   235  		ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-2", Namespace: "testkube"},
   236  	}
   237  	s.addTrigger(&testTrigger1)
   238  	s.addTrigger(&testTrigger2)
   239  
   240  	assert.Len(t, s.triggerStatus, 2)
   241  
   242  	s.removeTrigger(&testTrigger1)
   243  
   244  	assert.Len(t, s.triggerStatus, 1)
   245  	key := newStatusKey("testkube", "test-trigger-2")
   246  	assert.NotNil(t, s.triggerStatus[key])
   247  	deletedKey := newStatusKey("testkube", "test-trigger-1")
   248  	assert.Nil(t, s.triggerStatus[deletedKey])
   249  }
   250  
   251  func TestService_updateTrigger(t *testing.T) {
   252  	t.Parallel()
   253  
   254  	s := Service{triggerStatus: make(map[statusKey]*triggerStatus)}
   255  
   256  	oldTestTrigger := testtriggersv1.TestTrigger{
   257  		ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"},
   258  		Spec:       testtriggersv1.TestTriggerSpec{Event: "created"},
   259  	}
   260  	s.addTrigger(&oldTestTrigger)
   261  
   262  	newTestTrigger := testtriggersv1.TestTrigger{
   263  		ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"},
   264  		Spec:       testtriggersv1.TestTriggerSpec{Event: "modified"},
   265  	}
   266  
   267  	s.updateTrigger(&newTestTrigger)
   268  
   269  	assert.Len(t, s.triggerStatus, 1)
   270  	key := newStatusKey("testkube", "test-trigger-1")
   271  	assert.NotNil(t, s.triggerStatus[key])
   272  }