...

Source file src/github.com/redhat-developer/odo/pkg/sync/sync_test.go

Documentation: github.com/redhat-developer/odo/pkg/sync

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/devfile/library/v2/pkg/devfile/generator"
    13  	"github.com/golang/mock/gomock"
    14  	"github.com/google/go-cmp/cmp"
    15  
    16  	"github.com/redhat-developer/odo/pkg/exec"
    17  	"github.com/redhat-developer/odo/pkg/kclient"
    18  	"github.com/redhat-developer/odo/pkg/util"
    19  	"github.com/redhat-developer/odo/tests/helper"
    20  )
    21  
    22  func TestGetCmdToCreateSyncFolder(t *testing.T) {
    23  	tests := []struct {
    24  		name       string
    25  		syncFolder string
    26  		want       []string
    27  	}{
    28  		{
    29  			name:       "Case 1: Sync to /projects",
    30  			syncFolder: generator.DevfileSourceVolumeMount,
    31  			want:       []string{"mkdir", "-p", generator.DevfileSourceVolumeMount},
    32  		},
    33  		{
    34  			name:       "Case 2: Sync subdir of /projects",
    35  			syncFolder: generator.DevfileSourceVolumeMount + "/someproject",
    36  			want:       []string{"mkdir", "-p", generator.DevfileSourceVolumeMount + "/someproject"},
    37  		},
    38  	}
    39  	for _, tt := range tests {
    40  		cmdArr := getCmdToCreateSyncFolder(tt.syncFolder)
    41  		if diff := cmp.Diff(tt.want, cmdArr); diff != "" {
    42  			t.Errorf("getCmdToCreateSyncFolder() mismatch (-want +got):\n%s", diff)
    43  		}
    44  	}
    45  }
    46  
    47  func TestGetCmdToDeleteFiles(t *testing.T) {
    48  	syncFolder := "/projects/hello-world"
    49  
    50  	tests := []struct {
    51  		name       string
    52  		delFiles   []string
    53  		syncFolder string
    54  		want       []string
    55  	}{
    56  		{
    57  			name:       "Case 1: One deleted file",
    58  			delFiles:   []string{"test.txt"},
    59  			syncFolder: generator.DevfileSourceVolumeMount,
    60  			want:       []string{"rm", "-rf", generator.DevfileSourceVolumeMount + "/test.txt"},
    61  		},
    62  		{
    63  			name:       "Case 2: Multiple deleted files, default sync folder",
    64  			delFiles:   []string{"test.txt", "hello.c"},
    65  			syncFolder: generator.DevfileSourceVolumeMount,
    66  			want:       []string{"rm", "-rf", generator.DevfileSourceVolumeMount + "/test.txt", generator.DevfileSourceVolumeMount + "/hello.c"},
    67  		},
    68  		{
    69  			name:       "Case 2: Multiple deleted files, different sync folder",
    70  			delFiles:   []string{"test.txt", "hello.c"},
    71  			syncFolder: syncFolder,
    72  			want:       []string{"rm", "-rf", syncFolder + "/test.txt", syncFolder + "/hello.c"},
    73  		},
    74  	}
    75  	for _, tt := range tests {
    76  		cmdArr := getCmdToDeleteFiles(tt.delFiles, tt.syncFolder)
    77  		if diff := cmp.Diff(tt.want, cmdArr); diff != "" {
    78  			t.Errorf("getCmdToDeleteFiles() mismatch (-want +got):\n%s", diff)
    79  		}
    80  	}
    81  }
    82  
    83  func TestSyncFiles(t *testing.T) {
    84  
    85  	testComponentName := "test"
    86  
    87  	// create a temp dir for the file indexer
    88  	directory := t.TempDir()
    89  
    90  	jsFile, e := os.Create(filepath.Join(directory, "red.js"))
    91  	if e != nil {
    92  		t.Errorf("TestSyncFiles error: error creating temporary file for the indexer: %v", e)
    93  	}
    94  
    95  	ctrl := gomock.NewController(t)
    96  	kc := kclient.NewMockClientInterface(ctrl)
    97  	kc.EXPECT().ExecCMDInContainer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
    98  		Return(nil).AnyTimes()
    99  
   100  	// Assert that Bar() is invoked.
   101  	defer ctrl.Finish()
   102  
   103  	tests := []struct {
   104  		name               string
   105  		syncParameters     SyncParameters
   106  		wantErr            bool
   107  		wantIsPushRequired bool
   108  	}{
   109  		{
   110  			name: "Case 1: Component does not exist",
   111  			syncParameters: SyncParameters{
   112  				Path:              directory,
   113  				WatchFiles:        []string{},
   114  				WatchDeletedFiles: []string{},
   115  				IgnoredFiles:      []string{},
   116  				CompInfo: ComponentInfo{
   117  					ContainerName: "abcd",
   118  				},
   119  				ForcePush: true,
   120  			},
   121  			wantErr:            false,
   122  			wantIsPushRequired: true,
   123  		},
   124  		{
   125  			name: "Case 2: Component does exist",
   126  			syncParameters: SyncParameters{
   127  				Path:              directory,
   128  				WatchFiles:        []string{},
   129  				WatchDeletedFiles: []string{},
   130  				IgnoredFiles:      []string{},
   131  				CompInfo: ComponentInfo{
   132  					ContainerName: "abcd",
   133  				},
   134  				ForcePush: false,
   135  			},
   136  			wantErr:            false,
   137  			wantIsPushRequired: false, // always false after case 1
   138  		},
   139  		{
   140  			name: "Case 3: FakeErrorClient error",
   141  			syncParameters: SyncParameters{
   142  				Path:              directory,
   143  				WatchFiles:        []string{},
   144  				WatchDeletedFiles: []string{},
   145  				IgnoredFiles:      []string{},
   146  				CompInfo: ComponentInfo{
   147  					ContainerName: "abcd",
   148  				},
   149  				ForcePush: false,
   150  			},
   151  			wantErr:            true,
   152  			wantIsPushRequired: false,
   153  		},
   154  		{
   155  			name: "Case 4: File change",
   156  			syncParameters: SyncParameters{
   157  				Path:              directory,
   158  				WatchFiles:        []string{path.Join(directory, "test.log")},
   159  				WatchDeletedFiles: []string{},
   160  				IgnoredFiles:      []string{},
   161  				CompInfo: ComponentInfo{
   162  					ComponentName: testComponentName,
   163  					ContainerName: "abcd",
   164  				},
   165  				ForcePush: false,
   166  			},
   167  			wantErr:            false,
   168  			wantIsPushRequired: true,
   169  		},
   170  	}
   171  	for _, tt := range tests {
   172  		t.Run(tt.name, func(t *testing.T) {
   173  			execClient := exec.NewExecClient(kc)
   174  			syncAdapter := NewSyncClient(kc, execClient)
   175  			isPushRequired, err := syncAdapter.SyncFiles(context.Background(), tt.syncParameters)
   176  			if !tt.wantErr && err != nil {
   177  				t.Errorf("TestSyncFiles error: unexpected error when syncing files %v", err)
   178  			} else if !tt.wantErr && isPushRequired != tt.wantIsPushRequired {
   179  				t.Errorf("TestSyncFiles error: isPushRequired mismatch, wanted: %v, got: %v", tt.wantIsPushRequired, isPushRequired)
   180  			}
   181  		})
   182  	}
   183  
   184  	err := jsFile.Close()
   185  	if err != nil {
   186  		t.Errorf("TestSyncFiles error: error deleting the temp dir %s, err: %v", directory, err)
   187  	}
   188  }
   189  
   190  func TestPushLocal(t *testing.T) {
   191  
   192  	testComponentName := "test"
   193  
   194  	// create a temp dir for the file indexer
   195  	directory := t.TempDir()
   196  
   197  	newFilePath := filepath.Join(directory, "foobar.txt")
   198  	if err := helper.CreateFileWithContent(newFilePath, "hello world"); err != nil {
   199  		t.Errorf("TestPushLocal error: the foobar.txt file was not created: %v", err)
   200  	}
   201  
   202  	ctrl := gomock.NewController(t)
   203  	kc := kclient.NewMockClientInterface(ctrl)
   204  	kc.EXPECT().ExecCMDInContainer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   205  		Return(nil).AnyTimes()
   206  
   207  	// Assert that Bar() is invoked.
   208  	defer ctrl.Finish()
   209  
   210  	syncClient := func(ComponentInfo, string, io.Reader) error {
   211  		return nil
   212  	}
   213  
   214  	errorSyncClient := func(ComponentInfo, string, io.Reader) error {
   215  		return errors.New("err")
   216  	}
   217  
   218  	tests := []struct {
   219  		name        string
   220  		client      SyncExtracter
   221  		path        string
   222  		files       []string
   223  		delFiles    []string
   224  		isForcePush bool
   225  		compInfo    ComponentInfo
   226  		wantErr     bool
   227  	}{
   228  		{
   229  			name:        "Case 1: File change",
   230  			client:      syncClient,
   231  			path:        directory,
   232  			files:       []string{path.Join(directory, "test.log")},
   233  			delFiles:    []string{},
   234  			isForcePush: false,
   235  			compInfo: ComponentInfo{
   236  				ContainerName: "abcd",
   237  			},
   238  			wantErr: false,
   239  		},
   240  		{
   241  			name:        "Case 2: File change with fake error client",
   242  			client:      errorSyncClient,
   243  			path:        directory,
   244  			files:       []string{path.Join(directory, "test.log")},
   245  			delFiles:    []string{},
   246  			isForcePush: false,
   247  			compInfo: ComponentInfo{
   248  				ContainerName: "abcd",
   249  			},
   250  			wantErr: true,
   251  		},
   252  		{
   253  			name:        "Case 3: No file change",
   254  			client:      syncClient,
   255  			path:        directory,
   256  			files:       []string{},
   257  			delFiles:    []string{},
   258  			isForcePush: false,
   259  			compInfo: ComponentInfo{
   260  				ContainerName: "abcd",
   261  			},
   262  			wantErr: false,
   263  		},
   264  		{
   265  			name:        "Case 4: Deleted file",
   266  			client:      syncClient,
   267  			path:        directory,
   268  			files:       []string{},
   269  			delFiles:    []string{path.Join(directory, "test.log")},
   270  			isForcePush: false,
   271  			compInfo: ComponentInfo{
   272  				ContainerName: "abcd",
   273  			},
   274  			wantErr: false,
   275  		},
   276  		{
   277  			name:        "Case 5: Force push",
   278  			client:      syncClient,
   279  			path:        directory,
   280  			files:       []string{},
   281  			delFiles:    []string{},
   282  			isForcePush: true,
   283  			compInfo: ComponentInfo{
   284  				ContainerName: "abcd",
   285  			},
   286  			wantErr: false,
   287  		},
   288  		{
   289  			name:        "Case 6: Source mapping folder set",
   290  			client:      syncClient,
   291  			path:        directory,
   292  			files:       []string{},
   293  			delFiles:    []string{},
   294  			isForcePush: false,
   295  			compInfo: ComponentInfo{
   296  				ComponentName: testComponentName,
   297  				ContainerName: "abcd",
   298  				SyncFolder:    "/some/path",
   299  			},
   300  			wantErr: false,
   301  		},
   302  	}
   303  	for _, tt := range tests {
   304  		t.Run(tt.name, func(t *testing.T) {
   305  			execClient := exec.NewExecClient(kc)
   306  			syncAdapter := NewSyncClient(kc, execClient)
   307  			err := syncAdapter.pushLocal(context.Background(), tt.path, tt.files, tt.delFiles, tt.isForcePush, []string{}, tt.compInfo, util.IndexerRet{})
   308  			if !tt.wantErr && err != nil {
   309  				t.Errorf("TestPushLocal error: error pushing files: %v", err)
   310  			}
   311  
   312  		})
   313  	}
   314  }
   315  
   316  func TestUpdateIndexWithWatchChanges(t *testing.T) {
   317  
   318  	tests := []struct {
   319  		name                 string
   320  		initialFilesToCreate []string
   321  		watchDeletedFiles    []string
   322  		watchAddedFiles      []string
   323  		expectedFilesInIndex []string
   324  	}{
   325  		{
   326  			name:                 "Case 1 - Watch file deleted should remove file from index",
   327  			initialFilesToCreate: []string{"file1", "file2"},
   328  			watchDeletedFiles:    []string{"file1"},
   329  			expectedFilesInIndex: []string{"file2"},
   330  		},
   331  		{
   332  			name:                 "Case 2 - Watch file added should add file to index",
   333  			initialFilesToCreate: []string{"file1"},
   334  			watchAddedFiles:      []string{"file2"},
   335  			expectedFilesInIndex: []string{"file1", "file2"},
   336  		},
   337  		{
   338  			name:                 "Case 3 - No watch changes should mean no index changes",
   339  			initialFilesToCreate: []string{"file1"},
   340  			expectedFilesInIndex: []string{"file1"},
   341  		},
   342  	}
   343  	for _, tt := range tests {
   344  
   345  		// create a temp dir for the fake component
   346  		directory := t.TempDir()
   347  
   348  		fileIndexPath, err := util.ResolveIndexFilePath(directory)
   349  		if err != nil {
   350  			t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to resolve index file path: %v", err)
   351  		}
   352  
   353  		if err := os.MkdirAll(filepath.Dir(fileIndexPath), 0750); err != nil {
   354  			t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to create directories for %s: %v", fileIndexPath, err)
   355  		}
   356  
   357  		t.Run(tt.name, func(t *testing.T) {
   358  
   359  			indexData := map[string]util.FileData{}
   360  
   361  			// Create initial files
   362  			for _, fileToCreate := range tt.initialFilesToCreate {
   363  				filePath := filepath.Join(directory, fileToCreate)
   364  
   365  				if err := os.WriteFile(filePath, []byte("non-empty-string"), 0644); err != nil {
   366  					t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to write to index file path: %v", err)
   367  				}
   368  
   369  				key, fileDatum, err := util.GenerateNewFileDataEntry(filePath, directory)
   370  				if err != nil {
   371  					t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to generate new file: %v", err)
   372  				}
   373  				indexData[key] = *fileDatum
   374  			}
   375  
   376  			// Write the index based on those files
   377  			if err := util.WriteFile(indexData, fileIndexPath); err != nil {
   378  				t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to write index file: %v", err)
   379  			}
   380  
   381  			syncParams := SyncParameters{
   382  				Path: directory,
   383  			}
   384  
   385  			// Add deleted files to pushParams (also delete the files)
   386  			for _, deletedFile := range tt.watchDeletedFiles {
   387  				deletedFilePath := filepath.Join(directory, deletedFile)
   388  				syncParams.WatchDeletedFiles = append(syncParams.WatchDeletedFiles, deletedFilePath)
   389  
   390  				if err := os.Remove(deletedFilePath); err != nil {
   391  					t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to delete file %s %v", deletedFilePath, err)
   392  				}
   393  			}
   394  
   395  			// Add added files to pushParams (also create the files)
   396  			for _, addedFile := range tt.watchAddedFiles {
   397  				addedFilePath := filepath.Join(directory, addedFile)
   398  				syncParams.WatchFiles = append(syncParams.WatchFiles, addedFilePath)
   399  
   400  				if err := os.WriteFile(addedFilePath, []byte("non-empty-string"), 0644); err != nil {
   401  					t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to write to index file path: %v", err)
   402  				}
   403  			}
   404  
   405  			if err := updateIndexWithWatchChanges(syncParams); err != nil {
   406  				t.Fatalf("TestUpdateIndexWithWatchChangesLocal: unexpected error: %v", err)
   407  			}
   408  
   409  			postFileIndex, err := util.ReadFileIndex(fileIndexPath)
   410  			if err != nil || postFileIndex == nil {
   411  				t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: read new file index: %v", err)
   412  			}
   413  
   414  			// Locate expected files
   415  			if len(postFileIndex.Files) != len(tt.expectedFilesInIndex) {
   416  				t.Fatalf("Mismatch between number expected files and actual files in index, post-index: %v   expected: %v", postFileIndex.Files, tt.expectedFilesInIndex)
   417  			}
   418  			for _, expectedFile := range tt.expectedFilesInIndex {
   419  				if _, exists := postFileIndex.Files[expectedFile]; !exists {
   420  					t.Fatalf("Unable to find '%s' in post index file, %v", expectedFile, postFileIndex.Files)
   421  				}
   422  			}
   423  		})
   424  	}
   425  }
   426  

View as plain text