github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/systemfetcher/loader_test.go (about)

     1  package systemfetcher_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io/fs"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/internal/model"
    12  	"github.com/kyma-incubator/compass/components/director/internal/systemfetcher"
    13  	"github.com/kyma-incubator/compass/components/director/internal/systemfetcher/automock"
    14  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
    15  	pAutomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock"
    16  	"github.com/kyma-incubator/compass/components/director/pkg/persistence/txtest"
    17  	"github.com/kyma-incubator/compass/components/director/pkg/str"
    18  	"github.com/stretchr/testify/mock"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  const tempFileName = "tmp.json"
    23  
    24  func TestLoadData(t *testing.T) {
    25  	testErr := errors.New("testErr")
    26  	whID1 := "123456789"
    27  	whID2 := "123456789-new"
    28  	whType := model.WebhookModeAsyncCallback
    29  
    30  	applicationTemplateName := "app-tmpl-name"
    31  	applicationTemplatesJSON := "[{" +
    32  		"\"name\":\"" + applicationTemplateName + "\"," +
    33  		"\"description\":\"app-tmpl-desc\"}]"
    34  	applicationTemplatesWithIntSysJSON := "[{" +
    35  		"\"name\":\"" + applicationTemplateName + "\"," +
    36  		"\"applicationInputJSON\":\"{\\\"name\\\": \\\"name\\\", \\\"labels\\\": {\\\"legacy\\\": \\\"true\\\"}}\"," +
    37  		"\"intSystem\":{" +
    38  		"\"name\":\"int-sys-name\"," +
    39  		"\"description\":\"int-sys-desc\"}," +
    40  		"\"description\":\"app-tmpl-desc\"}]"
    41  
    42  	applicationTemplatesWithIntSysJSONAndPlaceholders := "[{" +
    43  		"\"name\":\"" + applicationTemplateName + "\"," +
    44  		"\"applicationInputJSON\":\"{\\\"name\\\": \\\"name\\\", \\\"labels\\\": {\\\"legacy\\\":\\\"true\\\"}}\"," +
    45  		"\"placeholders\":[{\"name\":\"name\",\"description\": \"description\",\"jsonPath\": \"jsonPath\"}]," +
    46  		"\"labels\":{\"managed_app_provisioning\":false}," +
    47  		"\"intSystem\":{" +
    48  		"\"name\":\"int-sys-name\"," +
    49  		"\"description\":\"int-sys-desc\"}," +
    50  		"\"description\":\"app-tmpl-desc\"}]"
    51  
    52  	applicationTemplatesWithIntSysJSONAndWebhooks := "[{" +
    53  		"\"name\":\"" + applicationTemplateName + "\"," +
    54  		"\"webhooks\":[{\"id\":\"" + whID1 + "\", \"objectID\":\"" + whID1 + "\", \"objectType\": \"ApplicationWebhook\", \"type\": \"CONFIGURATION_CHANGED\", \"mode\": \"ASYNC_CALLBACK\" },{\"id\":\"" + whID2 + "\", \"objectID\":\"" + whID2 + "\", \"objectType\": \"ApplicationWebhook\", \"type\": \"CONFIGURATION_CHANGED\", \"mode\": \"ASYNC_CALLBACK\" }]," +
    55  		"\"applicationInputJSON\":\"{\\\"name\\\": \\\"name\\\", \\\"labels\\\": {\\\"legacy\\\":\\\"true\\\"}}\"," +
    56  		"\"placeholders\":[{\"name\":\"name\",\"description\": \"description\",\"jsonPath\": \"jsonPath\"}]," +
    57  		"\"intSystem\":{" +
    58  		"\"name\":\"int-sys-name\"," +
    59  		"\"description\":\"int-sys-desc\"}," +
    60  		"\"description\":\"app-tmpl-desc\"}]"
    61  
    62  	pageInfo := &pagination.Page{
    63  		StartCursor: "",
    64  		EndCursor:   "",
    65  		HasNextPage: false,
    66  	}
    67  
    68  	intSysPage := model.IntegrationSystemPage{
    69  		Data: []*model.IntegrationSystem{
    70  			{
    71  				ID:          "id",
    72  				Name:        "name",
    73  				Description: str.Ptr("desc"),
    74  			},
    75  		},
    76  		PageInfo:   pageInfo,
    77  		TotalCount: 0,
    78  	}
    79  
    80  	type testCase struct {
    81  		name              string
    82  		mockTransactioner func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner)
    83  		appTmplSvc        func() *automock.AppTmplService
    84  		intSysSvc         func() *automock.IntSysSvc
    85  		readDirFunc       func(path string) ([]os.DirEntry, error)
    86  		readFileFunc      func(path string) ([]byte, error)
    87  		expectedErr       error
    88  	}
    89  	tests := []testCase{
    90  		{
    91  			name: "load application templates failed - read dir returns error",
    92  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
    93  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntStartTransaction()
    94  			},
    95  			appTmplSvc: mockAppTmplService,
    96  			intSysSvc:  mockIntSysService,
    97  			readDirFunc: func(path string) ([]os.DirEntry, error) {
    98  				return nil, testErr
    99  			},
   100  			readFileFunc: mockReadFile,
   101  			expectedErr:  testErr,
   102  		},
   103  		{
   104  			name: "load application templates failed - unsupported file type",
   105  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   106  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntStartTransaction()
   107  			},
   108  			appTmplSvc: mockAppTmplService,
   109  			intSysSvc:  mockIntSysService,
   110  			readDirFunc: func(path string) ([]os.DirEntry, error) {
   111  				file := FakeFile{name: "test.txt"}
   112  				return []os.DirEntry{&file}, nil
   113  			},
   114  			readFileFunc: mockReadFile,
   115  			expectedErr:  fmt.Errorf("unsupported file format \".txt\", supported format: json"),
   116  		},
   117  		{
   118  			name: "load application templates failed - read file returns error",
   119  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   120  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntStartTransaction()
   121  			},
   122  			appTmplSvc:  mockAppTmplService,
   123  			intSysSvc:   mockIntSysService,
   124  			readDirFunc: mockReadDir,
   125  			readFileFunc: func(path string) ([]byte, error) {
   126  				return []byte("[]"), testErr
   127  			},
   128  			expectedErr: testErr,
   129  		},
   130  		{
   131  			name: "begin transaction failed",
   132  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   133  				return txtest.NewTransactionContextGenerator(testErr).ThatFailsOnBegin()
   134  			},
   135  			appTmplSvc:   mockAppTmplService,
   136  			intSysSvc:    mockIntSysService,
   137  			readDirFunc:  mockReadDir,
   138  			readFileFunc: mockReadFile,
   139  			expectedErr:  testErr,
   140  		},
   141  		{
   142  			name: "upsert application templates failed - GetByNameAndRegion returns error",
   143  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   144  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   145  			},
   146  			appTmplSvc: func() *automock.AppTmplService {
   147  				appTmplSvc := &automock.AppTmplService{}
   148  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(nil, testErr).Once()
   149  				return appTmplSvc
   150  			},
   151  			intSysSvc:   mockIntSysService,
   152  			readDirFunc: mockReadDir,
   153  			readFileFunc: func(path string) ([]byte, error) {
   154  				return []byte(applicationTemplatesJSON), nil
   155  			},
   156  			expectedErr: testErr,
   157  		},
   158  		{
   159  			name: "upsert application templates failed - create returns error",
   160  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   161  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   162  			},
   163  			appTmplSvc: func() *automock.AppTmplService {
   164  				appTmplSvc := &automock.AppTmplService{}
   165  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(nil, fmt.Errorf("Object not found")).Once()
   166  				appTmplSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.ApplicationTemplateInput")).Return("", testErr).Once()
   167  				return appTmplSvc
   168  			},
   169  			intSysSvc:   mockIntSysService,
   170  			readDirFunc: mockReadDir,
   171  			readFileFunc: func(path string) ([]byte, error) {
   172  				return []byte(applicationTemplatesJSON), nil
   173  			},
   174  			expectedErr: testErr,
   175  		},
   176  		{
   177  			name: "commit returns error",
   178  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   179  				return txtest.NewTransactionContextGenerator(testErr).ThatFailsOnCommit()
   180  			},
   181  			appTmplSvc: func() *automock.AppTmplService {
   182  				appTmplSvc := &automock.AppTmplService{}
   183  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(nil, fmt.Errorf("Object not found")).Once()
   184  				appTmplSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.ApplicationTemplateInput")).Return("", nil).Once()
   185  				return appTmplSvc
   186  			},
   187  			intSysSvc:   mockIntSysService,
   188  			readDirFunc: mockReadDir,
   189  			readFileFunc: func(path string) ([]byte, error) {
   190  				return []byte(applicationTemplatesJSON), nil
   191  			},
   192  			expectedErr: testErr,
   193  		},
   194  		{
   195  			name: "create application templates dependent entities failed - invalid intSys json object",
   196  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   197  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   198  			},
   199  			appTmplSvc:  mockAppTmplService,
   200  			intSysSvc:   mockIntSysService,
   201  			readDirFunc: mockReadDir,
   202  			readFileFunc: func(path string) ([]byte, error) {
   203  				applicationTemplatesWithInvalidIntSysJSON := "[{" +
   204  					"\"name\":\"" + applicationTemplateName + "\"," +
   205  					"\"intSystem\":123," +
   206  					"\"description\":\"app-tmpl-desc\"}]"
   207  				return []byte(applicationTemplatesWithInvalidIntSysJSON), nil
   208  			},
   209  			expectedErr: errors.New("the type of the integration system is float64 instead of map[string]interface{}. map[]"),
   210  		},
   211  		{
   212  			name: "extract integration system failed - invalid intSys name json field",
   213  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   214  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   215  			},
   216  			appTmplSvc:  mockAppTmplService,
   217  			intSysSvc:   mockIntSysService,
   218  			readDirFunc: mockReadDir,
   219  			readFileFunc: func(path string) ([]byte, error) {
   220  				applicationTemplatesWithInvalidIntSysNameJSON := "[{" +
   221  					"\"name\":\"" + applicationTemplateName + "\"," +
   222  					"\"intSystem\":{" +
   223  					"\"name\":123," +
   224  					"\"description\":\"int-sys-desc\"}," +
   225  					"\"description\":\"app-tmpl-desc\"}]"
   226  				return []byte(applicationTemplatesWithInvalidIntSysNameJSON), nil
   227  			},
   228  			expectedErr: errors.New("integration system name value must be string"),
   229  		},
   230  		{
   231  			name: "extract integration system failed - invalid intSys description json field",
   232  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   233  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   234  			},
   235  			appTmplSvc:  mockAppTmplService,
   236  			intSysSvc:   mockIntSysService,
   237  			readDirFunc: mockReadDir,
   238  			readFileFunc: func(path string) ([]byte, error) {
   239  				applicationTemplatesWithInvalidIntSysDescJSON := "[{" +
   240  					"\"name\":\"" + applicationTemplateName + "\"," +
   241  					"\"intSystem\":{" +
   242  					"\"name\":\"int-sys-name\"," +
   243  					"\"description\":123}," +
   244  					"\"description\":\"app-tmpl-desc\"}]"
   245  				return []byte(applicationTemplatesWithInvalidIntSysDescJSON), nil
   246  			},
   247  			expectedErr: errors.New("integration system description value must be string"),
   248  		},
   249  		{
   250  			name: "extract integration system failed - missing intSys name json field",
   251  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   252  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   253  			},
   254  			appTmplSvc:  mockAppTmplService,
   255  			intSysSvc:   mockIntSysService,
   256  			readDirFunc: mockReadDir,
   257  			readFileFunc: func(path string) ([]byte, error) {
   258  				applicationTemplatesWithMissingIntSysNameJSON := "[{" +
   259  					"\"name\":\"" + applicationTemplateName + "\"," +
   260  					"\"intSystem\":{" +
   261  					"\"description\":123}," +
   262  					"\"description\":\"app-tmpl-desc\"}]"
   263  				return []byte(applicationTemplatesWithMissingIntSysNameJSON), nil
   264  			},
   265  			expectedErr: errors.New("integration system name is missing"),
   266  		},
   267  		{
   268  			name: "extract integration system failed - missing intSys description json field",
   269  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   270  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   271  			},
   272  			appTmplSvc:  mockAppTmplService,
   273  			intSysSvc:   mockIntSysService,
   274  			readDirFunc: mockReadDir,
   275  			readFileFunc: func(path string) ([]byte, error) {
   276  				applicationTemplatesWithMissingIntSysDescJSON := "[{" +
   277  					"\"name\":\"" + applicationTemplateName + "\"," +
   278  					"\"intSystem\":{" +
   279  					"\"name\":\"int-sys-name\"}," +
   280  					"\"description\":\"app-tmpl-desc\"}]"
   281  				return []byte(applicationTemplatesWithMissingIntSysDescJSON), nil
   282  			},
   283  			expectedErr: errors.New("integration system description is missing"),
   284  		},
   285  		{
   286  			name: "list integration systems failed - list returns error",
   287  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   288  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   289  			},
   290  			appTmplSvc: mockAppTmplService,
   291  			intSysSvc: func() *automock.IntSysSvc {
   292  				intSysSvc := &automock.IntSysSvc{}
   293  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(model.IntegrationSystemPage{}, testErr).Once()
   294  				return intSysSvc
   295  			},
   296  			readDirFunc: mockReadDir,
   297  			readFileFunc: func(path string) ([]byte, error) {
   298  				return []byte(applicationTemplatesWithIntSysJSON), nil
   299  			},
   300  			expectedErr: testErr,
   301  		},
   302  		{
   303  			name: "create app templates dependent entities failed - create integration system returns error",
   304  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   305  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   306  			},
   307  			appTmplSvc: mockAppTmplService,
   308  			intSysSvc: func() *automock.IntSysSvc {
   309  				intSysSvc := &automock.IntSysSvc{}
   310  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   311  				intSysSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.IntegrationSystemInput")).Return("", testErr).Once()
   312  				return intSysSvc
   313  			},
   314  			readDirFunc: mockReadDir,
   315  			readFileFunc: func(path string) ([]byte, error) {
   316  				return []byte(applicationTemplatesWithIntSysJSON), nil
   317  			},
   318  			expectedErr: testErr,
   319  		},
   320  		{
   321  			name: "upsert application template failed - update returns error",
   322  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   323  				return txtest.NewTransactionContextGenerator(nil).ThatDoesntExpectCommit()
   324  			},
   325  			appTmplSvc: func() *automock.AppTmplService {
   326  				template := model.ApplicationTemplate{
   327  					ID:                   "id",
   328  					ApplicationInputJSON: "{\"test\":\"test\"}",
   329  				}
   330  				appTmplSvc := &automock.AppTmplService{}
   331  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(&template, nil).Once()
   332  				appTmplSvc.On("Update", txtest.CtxWithDBMatcher(), "id", mock.AnythingOfType("model.ApplicationTemplateUpdateInput")).Return(testErr).Once()
   333  				return appTmplSvc
   334  			},
   335  			intSysSvc: func() *automock.IntSysSvc {
   336  				intSysPage := model.IntegrationSystemPage{
   337  					Data: []*model.IntegrationSystem{
   338  						{
   339  							ID:          "id",
   340  							Name:        "int-sys-name",
   341  							Description: str.Ptr("int-sys-desc"),
   342  						},
   343  					},
   344  					PageInfo:   pageInfo,
   345  					TotalCount: 0,
   346  				}
   347  				intSysSvc := &automock.IntSysSvc{}
   348  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   349  				return intSysSvc
   350  			},
   351  			readDirFunc: mockReadDir,
   352  			readFileFunc: func(path string) ([]byte, error) {
   353  				return []byte(applicationTemplatesWithIntSysJSON), nil
   354  			},
   355  			expectedErr: testErr,
   356  		},
   357  		{
   358  			name: "Success",
   359  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   360  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   361  			},
   362  			appTmplSvc: func() *automock.AppTmplService {
   363  				appTmplSvc := &automock.AppTmplService{}
   364  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(nil, fmt.Errorf("Object not found")).Once()
   365  				appTmplSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.ApplicationTemplateInput")).Return("", nil).Once()
   366  				return appTmplSvc
   367  			},
   368  			intSysSvc: func() *automock.IntSysSvc {
   369  				intSysSvc := &automock.IntSysSvc{}
   370  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   371  				intSysSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.IntegrationSystemInput")).Return("int-sys-id", nil).Once()
   372  				return intSysSvc
   373  			},
   374  			readDirFunc: mockReadDir,
   375  			readFileFunc: func(path string) ([]byte, error) {
   376  				return []byte(applicationTemplatesWithIntSysJSON), nil
   377  			},
   378  		},
   379  		{
   380  			name: "Success - integration system already exists",
   381  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   382  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   383  			},
   384  			appTmplSvc: func() *automock.AppTmplService {
   385  				appTmplSvc := &automock.AppTmplService{}
   386  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(nil, fmt.Errorf("Object not found")).Once()
   387  				appTmplSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.ApplicationTemplateInput")).Return("", nil).Once()
   388  				return appTmplSvc
   389  			},
   390  			intSysSvc: func() *automock.IntSysSvc {
   391  				intSysPage := model.IntegrationSystemPage{
   392  					Data: []*model.IntegrationSystem{
   393  						{
   394  							ID:          "id",
   395  							Name:        "int-sys-name",
   396  							Description: str.Ptr("int-sys-desc"),
   397  						},
   398  					},
   399  					PageInfo:   pageInfo,
   400  					TotalCount: 0,
   401  				}
   402  				intSysSvc := &automock.IntSysSvc{}
   403  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   404  				return intSysSvc
   405  			},
   406  			readDirFunc: mockReadDir,
   407  			readFileFunc: func(path string) ([]byte, error) {
   408  				return []byte(applicationTemplatesWithIntSysJSON), nil
   409  			},
   410  		},
   411  		{
   412  			name: "Success - application template already exists, update triggered",
   413  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   414  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   415  			},
   416  			appTmplSvc: func() *automock.AppTmplService {
   417  				template := model.ApplicationTemplate{
   418  					ID:                   "id",
   419  					ApplicationInputJSON: "{\"test\":\"test\"}",
   420  				}
   421  				appTmplSvc := &automock.AppTmplService{}
   422  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(&template, nil).Once()
   423  				appTmplSvc.On("Update", txtest.CtxWithDBMatcher(), "id", mock.AnythingOfType("model.ApplicationTemplateUpdateInput")).Return(nil).Once()
   424  				return appTmplSvc
   425  			},
   426  			intSysSvc: func() *automock.IntSysSvc {
   427  				intSysPage := model.IntegrationSystemPage{
   428  					Data: []*model.IntegrationSystem{
   429  						{
   430  							ID:          "id",
   431  							Name:        "int-sys-name",
   432  							Description: str.Ptr("int-sys-desc"),
   433  						},
   434  					},
   435  					PageInfo:   pageInfo,
   436  					TotalCount: 0,
   437  				}
   438  				intSysSvc := &automock.IntSysSvc{}
   439  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   440  				return intSysSvc
   441  			},
   442  			readDirFunc: mockReadDir,
   443  			readFileFunc: func(path string) ([]byte, error) {
   444  				return []byte(applicationTemplatesWithIntSysJSON), nil
   445  			},
   446  		},
   447  		{
   448  			name: "Success - application template already exists, update triggered when only labels are different",
   449  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   450  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   451  			},
   452  			appTmplSvc: func() *automock.AppTmplService {
   453  				template := model.ApplicationTemplate{
   454  					ID:                   "id",
   455  					Name:                 "app-tmpl-name",
   456  					Description:          str.Ptr("app-tmpl-desc"),
   457  					ApplicationInputJSON: "{\"integrationSystemID\":\"id\",\"labels\":{\"legacy\":\"true\"},\"name\":\"name\"}",
   458  					Placeholders: []model.ApplicationTemplatePlaceholder{
   459  						{
   460  							Name:        "name",
   461  							Description: str.Ptr("description"),
   462  							JSONPath:    str.Ptr("jsonPath"),
   463  						},
   464  					},
   465  					Labels: map[string]interface{}{
   466  						"managed_app_provisioning": true,
   467  					},
   468  				}
   469  				appTmplSvc := &automock.AppTmplService{}
   470  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(&template, nil).Once()
   471  				appTmplSvc.On("Update", txtest.CtxWithDBMatcher(), "id", mock.AnythingOfType("model.ApplicationTemplateUpdateInput")).Return(nil).Once()
   472  				return appTmplSvc
   473  			},
   474  			intSysSvc: func() *automock.IntSysSvc {
   475  				intSysPage := model.IntegrationSystemPage{
   476  					Data: []*model.IntegrationSystem{
   477  						{
   478  							ID:          "id",
   479  							Name:        "int-sys-name",
   480  							Description: str.Ptr("int-sys-desc"),
   481  						},
   482  					},
   483  					PageInfo:   pageInfo,
   484  					TotalCount: 0,
   485  				}
   486  				intSysSvc := &automock.IntSysSvc{}
   487  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   488  				return intSysSvc
   489  			},
   490  			readDirFunc: mockReadDir,
   491  			readFileFunc: func(path string) ([]byte, error) {
   492  				return []byte(applicationTemplatesWithIntSysJSONAndPlaceholders), nil
   493  			},
   494  		},
   495  		{
   496  			name: "Success - integration system already exists, missing labels in applicationInputJSON",
   497  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   498  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   499  			},
   500  			appTmplSvc: func() *automock.AppTmplService {
   501  				appTmplSvc := &automock.AppTmplService{}
   502  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(nil, fmt.Errorf("Object not found")).Once()
   503  				appTmplSvc.On("Create", txtest.CtxWithDBMatcher(), mock.AnythingOfType("model.ApplicationTemplateInput")).Return("", nil).Once()
   504  				return appTmplSvc
   505  			},
   506  			intSysSvc: func() *automock.IntSysSvc {
   507  				intSysPage := model.IntegrationSystemPage{
   508  					Data: []*model.IntegrationSystem{
   509  						{
   510  							ID:          "id",
   511  							Name:        "int-sys-name",
   512  							Description: str.Ptr("int-sys-desc"),
   513  						},
   514  					},
   515  					PageInfo:   pageInfo,
   516  					TotalCount: 0,
   517  				}
   518  				intSysSvc := &automock.IntSysSvc{}
   519  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   520  				return intSysSvc
   521  			},
   522  			readDirFunc: mockReadDir,
   523  			readFileFunc: func(path string) ([]byte, error) {
   524  				applicationTemplatesWithIntSysAndMissingLabelsJSON := "[{" +
   525  					"\"name\":\"" + applicationTemplateName + "\"," +
   526  					"\"applicationInputJSON\":\"{\\\"name\\\": \\\"name\\\"}\"," +
   527  					"\"intSystem\":{" +
   528  					"\"name\":\"int-sys-name\"," +
   529  					"\"description\":\"int-sys-desc\"}," +
   530  					"\"description\":\"app-tmpl-desc\"}]"
   531  				return []byte(applicationTemplatesWithIntSysAndMissingLabelsJSON), nil
   532  			},
   533  		},
   534  		{
   535  			name: "Success - application template already exists, update triggered when only webhooks length is different",
   536  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   537  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   538  			},
   539  			appTmplSvc: func() *automock.AppTmplService {
   540  				template := model.ApplicationTemplate{
   541  					ID:                   "id",
   542  					Name:                 "app-tmpl-name",
   543  					Description:          str.Ptr("app-tmpl-desc"),
   544  					ApplicationInputJSON: "{\"integrationSystemID\":\"id\",\"labels\":{\"legacy\":\"true\"},\"name\":\"name\"}",
   545  					Placeholders: []model.ApplicationTemplatePlaceholder{
   546  						{
   547  							Name:        "name",
   548  							Description: str.Ptr("description"),
   549  							JSONPath:    str.Ptr("jsonPath"),
   550  						},
   551  					},
   552  					Labels: map[string]interface{}{
   553  						"managed_app_provisioning": false,
   554  					},
   555  					Webhooks: []model.Webhook{
   556  						fixWebhookModel(whID1, whType),
   557  					},
   558  				}
   559  				appTmplSvc := &automock.AppTmplService{}
   560  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(&template, nil).Once()
   561  				appTmplSvc.On("Update", txtest.CtxWithDBMatcher(), "id", mock.AnythingOfType("model.ApplicationTemplateUpdateInput")).Return(nil).Once()
   562  				return appTmplSvc
   563  			},
   564  			intSysSvc: func() *automock.IntSysSvc {
   565  				intSysPage := model.IntegrationSystemPage{
   566  					Data: []*model.IntegrationSystem{
   567  						{
   568  							ID:          "id",
   569  							Name:        "int-sys-name",
   570  							Description: str.Ptr("int-sys-desc"),
   571  						},
   572  					},
   573  					PageInfo:   pageInfo,
   574  					TotalCount: 0,
   575  				}
   576  				intSysSvc := &automock.IntSysSvc{}
   577  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   578  				return intSysSvc
   579  			},
   580  			readDirFunc: mockReadDir,
   581  			readFileFunc: func(path string) ([]byte, error) {
   582  				return []byte(applicationTemplatesWithIntSysJSONAndWebhooks), nil
   583  			},
   584  		},
   585  		{
   586  			name: "Success - application template already exists, update triggered when only webhooks internals are different",
   587  			mockTransactioner: func() (*pAutomock.PersistenceTx, *pAutomock.Transactioner) {
   588  				return txtest.NewTransactionContextGenerator(nil).ThatSucceeds()
   589  			},
   590  			appTmplSvc: func() *automock.AppTmplService {
   591  				template := model.ApplicationTemplate{
   592  					ID:                   "id",
   593  					Name:                 "app-tmpl-name",
   594  					Description:          str.Ptr("app-tmpl-desc"),
   595  					ApplicationInputJSON: "{\"integrationSystemID\":\"id\",\"labels\":{\"legacy\":\"true\"},\"name\":\"name\"}",
   596  					Placeholders: []model.ApplicationTemplatePlaceholder{
   597  						{
   598  							Name:        "name",
   599  							Description: str.Ptr("description"),
   600  							JSONPath:    str.Ptr("jsonPath"),
   601  						},
   602  					},
   603  					Labels: map[string]interface{}{
   604  						"managed_app_provisioning": false,
   605  					},
   606  					Webhooks: []model.Webhook{
   607  						fixWebhookModel(whID1, whType),
   608  						fixWebhookModel(whID2, model.WebhookModeSync),
   609  					},
   610  				}
   611  				appTmplSvc := &automock.AppTmplService{}
   612  				appTmplSvc.On("GetByNameAndRegion", txtest.CtxWithDBMatcher(), applicationTemplateName, nil).Return(&template, nil).Once()
   613  				appTmplSvc.On("Update", txtest.CtxWithDBMatcher(), "id", mock.AnythingOfType("model.ApplicationTemplateUpdateInput")).Return(nil).Once()
   614  				return appTmplSvc
   615  			},
   616  			intSysSvc: func() *automock.IntSysSvc {
   617  				intSysPage := model.IntegrationSystemPage{
   618  					Data: []*model.IntegrationSystem{
   619  						{
   620  							ID:          "id",
   621  							Name:        "int-sys-name",
   622  							Description: str.Ptr("int-sys-desc"),
   623  						},
   624  					},
   625  					PageInfo:   pageInfo,
   626  					TotalCount: 0,
   627  				}
   628  				intSysSvc := &automock.IntSysSvc{}
   629  				intSysSvc.On("List", txtest.CtxWithDBMatcher(), 200, "").Return(intSysPage, nil).Once()
   630  				return intSysSvc
   631  			},
   632  			readDirFunc: mockReadDir,
   633  			readFileFunc: func(path string) ([]byte, error) {
   634  				return []byte(applicationTemplatesWithIntSysJSONAndWebhooks), nil
   635  			},
   636  		},
   637  	}
   638  
   639  	for _, testCase := range tests {
   640  		t.Run(testCase.name, func(t *testing.T) {
   641  			appTmplSvc := testCase.appTmplSvc()
   642  			intSysSvc := testCase.intSysSvc()
   643  			mockedTx, transactioner := testCase.mockTransactioner()
   644  			defer mock.AssertExpectationsForObjects(t, appTmplSvc, intSysSvc, mockedTx, transactioner)
   645  
   646  			dataLoader := systemfetcher.NewDataLoader(transactioner, systemfetcher.Config{}, appTmplSvc, intSysSvc)
   647  			err := dataLoader.LoadData(context.TODO(), testCase.readDirFunc, testCase.readFileFunc)
   648  
   649  			if testCase.expectedErr != nil {
   650  				require.Contains(t, err.Error(), testCase.expectedErr.Error())
   651  			} else {
   652  				require.NoError(t, err)
   653  			}
   654  		})
   655  	}
   656  }
   657  
   658  func mockReadFile(_ string) ([]byte, error) {
   659  	return []byte("[]"), nil
   660  }
   661  
   662  func mockReadDir(_ string) ([]os.DirEntry, error) {
   663  	file := FakeFile{name: tempFileName}
   664  	return []os.DirEntry{&file}, nil
   665  }
   666  
   667  func mockAppTmplService() *automock.AppTmplService {
   668  	return &automock.AppTmplService{}
   669  }
   670  
   671  func mockIntSysService() *automock.IntSysSvc {
   672  	return &automock.IntSysSvc{}
   673  }
   674  
   675  type FakeFile struct {
   676  	name string
   677  }
   678  
   679  func (f *FakeFile) Type() fs.FileMode {
   680  	return 0
   681  }
   682  
   683  func (f *FakeFile) Info() (fs.FileInfo, error) {
   684  	return nil, nil
   685  }
   686  
   687  func (f *FakeFile) Name() string {
   688  	return f.name
   689  }
   690  
   691  func (f *FakeFile) IsDir() bool {
   692  	return false
   693  }