1 package util
2
3 import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8
9 "github.com/google/go-cmp/cmp"
10 "github.com/google/go-cmp/cmp/cmpopts"
11
12 "github.com/redhat-developer/odo/pkg/testingutil/filesystem"
13 )
14
15 func TestCheckGitIgnoreFile(t *testing.T) {
16
17
18 fs := filesystem.NewFakeFs()
19
20 contextDir, err := fs.TempDir(os.TempDir(), "context")
21 if err != nil {
22 t.Error(err)
23 }
24
25 gitignorePath := filepath.Join(contextDir, DotGitIgnoreFile)
26
27 tests := []struct {
28 testName string
29 create bool
30 gitIgnoreCreate func(create bool, contextDir string, fs filesystem.Filesystem) error
31 directory string
32 want string
33 wantErr bool
34 }{
35 {
36 testName: "Test when .gitignore does not exist",
37 create: true,
38 gitIgnoreCreate: mockDirectoryInfo,
39 directory: contextDir,
40 want: gitignorePath,
41 wantErr: false,
42 },
43 {
44 testName: "Test when .gitignore exists",
45 create: false,
46 gitIgnoreCreate: mockDirectoryInfo,
47 directory: contextDir,
48 want: gitignorePath,
49 wantErr: false,
50 },
51 }
52
53 for _, tt := range tests {
54
55 err := tt.gitIgnoreCreate(tt.create, tt.directory, fs)
56 if err != nil {
57 t.Error(err)
58 }
59
60 t.Run(tt.testName, func(t *testing.T) {
61
62 gitIgnoreFilePath, isNew, err := touchGitIgnoreFile(tt.directory, fs)
63
64 if tt.want != gitIgnoreFilePath {
65 t.Errorf("touchGitIgnoreFile unexpected error %v, while creating .gitignore file", err)
66 }
67
68 if !tt.wantErr == (err != nil) {
69 t.Errorf("touchGitIgnoreFile unexpected error %v, wantErr %v", err, tt.wantErr)
70 }
71
72 if tt.create != isNew {
73 t.Errorf("touchGitIgnoreFile: expected tt.create=%v, got %v", tt.create, isNew)
74 }
75 })
76 }
77
78 }
79
80 func TestAddOdoDirectory(t *testing.T) {
81
82
83 fs := filesystem.NewFakeFs()
84
85 contextDir, err := fs.TempDir(os.TempDir(), "context")
86 if err != nil {
87 t.Error(err)
88 }
89
90 gitignorePath := filepath.Join(contextDir, DotGitIgnoreFile)
91
92 tests := []struct {
93 testName string
94 create bool
95 gitIgnoreCreate func(create bool, contextDir string, fs filesystem.Filesystem) error
96 directory string
97 wantErr bool
98 }{
99 {
100 testName: "Test when .odo added to .gitignore",
101 create: false,
102 gitIgnoreCreate: mockDirectoryInfo,
103 directory: gitignorePath,
104 wantErr: false,
105 },
106 }
107
108 for _, tt := range tests {
109
110 err := tt.gitIgnoreCreate(tt.create, tt.directory, fs)
111 if err != nil {
112 t.Error(err)
113 }
114
115 t.Run(tt.testName, func(t *testing.T) {
116
117 err := addOdoDirectory(tt.directory, fs)
118
119 if !tt.wantErr == (err != nil) {
120 t.Errorf("addOdoFileIndex unexpected error %v, wantErr %v", err, tt.wantErr)
121 }
122
123 })
124 }
125 }
126
127 func mockDirectoryInfo(create bool, contextDir string, fs filesystem.Filesystem) error {
128
129 if !create {
130 err := fs.MkdirAll(filepath.Join(contextDir, DotGitIgnoreFile), os.ModePerm)
131 if err != nil {
132 return err
133 }
134 }
135
136 return nil
137 }
138
139 func TestCalculateFileDataKeyFromPath(t *testing.T) {
140
141
142 directory := t.TempDir()
143
144 tests := []struct {
145 absolutePath string
146 rootDirectory string
147 expectedResult string
148 }{
149 {
150 absolutePath: filepath.Join(directory, "/path/file1"),
151 rootDirectory: filepath.Join(directory, "/path"),
152 expectedResult: "file1",
153 },
154 {
155 absolutePath: filepath.Join(directory, "/path/path2/file1"),
156 rootDirectory: filepath.Join(directory, "/path/"),
157 expectedResult: "path2/file1",
158 },
159 {
160 absolutePath: filepath.Join(directory, "/path"),
161 rootDirectory: filepath.Join(directory, "/"),
162 expectedResult: "path",
163 },
164 }
165
166 for _, tt := range tests {
167
168 t.Run("Expect result: "+tt.expectedResult, func(t *testing.T) {
169
170 result, err := CalculateFileDataKeyFromPath(tt.absolutePath, tt.rootDirectory)
171 if err != nil {
172 t.Fatalf("unexpecter error occurred %v", err)
173 }
174
175 if result != filepath.FromSlash(tt.expectedResult) {
176 t.Fatalf("unexpected result: %v %v", tt.expectedResult, result)
177 }
178 })
179 }
180 }
181
182 func TestGenerateNewFileDataEntry(t *testing.T) {
183
184
185 directory := t.TempDir()
186
187 tests := []struct {
188 testName string
189 absolutePath string
190 rootDirectory string
191 expectedKey string
192 }{
193 {
194 absolutePath: filepath.Join(directory, "/path1/file1"),
195 rootDirectory: filepath.Join(directory, "/path1"),
196 expectedKey: "file1",
197 },
198 {
199 absolutePath: filepath.Join(directory, "/path2/path2/file1"),
200 rootDirectory: filepath.Join(directory, "/path2"),
201 expectedKey: "path2/file1",
202 },
203 {
204 absolutePath: filepath.Join(directory, "/path3"),
205 rootDirectory: filepath.Join(directory, "/"),
206 expectedKey: "path3",
207 },
208 }
209
210 for _, tt := range tests {
211
212 t.Run("Expected key '"+tt.expectedKey+"'", func(t *testing.T) {
213
214 if err := os.MkdirAll(filepath.Dir(tt.absolutePath), 0750); err != nil {
215 t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to create directories for %s: %v", tt.absolutePath, err)
216 }
217
218 if err := os.WriteFile(tt.absolutePath, []byte("non-empty-string"), 0644); err != nil {
219 t.Fatalf("TestUpdateIndexWithWatchChangesLocal error: unable to write to index file path: %v", err)
220 }
221
222 key, filedata, err := GenerateNewFileDataEntry(tt.absolutePath, tt.rootDirectory)
223
224 if err != nil {
225 t.Fatalf("Unexpected error occurred %v", err)
226 }
227
228
229 key = strings.ReplaceAll(key, "\\", "/")
230
231 if key != tt.expectedKey {
232 t.Fatalf("Key %s did not match expected key %s", key, tt.expectedKey)
233 }
234
235 if filedata == nil {
236 t.Fatalf("Filedata should not be null")
237 }
238
239 if filedata.Size == 0 || filedata.LastModifiedDate.IsZero() {
240 t.Fatalf("Invalid filedata values %v %v", filedata.Size, filedata.LastModifiedDate)
241 }
242
243 })
244 }
245 }
246
247 func createAndStat(fileName, tempDirectoryName string, fs filesystem.Filesystem) (filesystem.File, os.FileInfo, error) {
248 file, err := fs.Create(filepath.Join(tempDirectoryName, fileName))
249 if err != nil {
250 return nil, nil, err
251 }
252 stat, err := fs.Stat(file.Name())
253 if err != nil {
254 return nil, nil, err
255 }
256 return file, stat, nil
257 }
258
259 func Test_recursiveChecker(t *testing.T) {
260 fs := filesystem.DefaultFs{}
261
262 tempDirectoryName, err := fs.TempDir(os.TempDir(), "dir0")
263 if err != nil {
264 t.Errorf("unexpected error: %v", err)
265 }
266
267 jsFileName := "red.js"
268 jsFile, jsFileStat, err := createAndStat(jsFileName, tempDirectoryName, fs)
269 if err != nil {
270 t.Errorf("unexpected error: %v", err)
271 }
272 jsFileAbsPath := jsFile.Name()
273
274 readmeFileName := "README.txt"
275 readmeFile, readmeFileStat, err := createAndStat(readmeFileName, tempDirectoryName, fs)
276 if err != nil {
277 t.Errorf("unexpected error: %v", err)
278 }
279 readmeFileAbsPath := readmeFile.Name()
280
281 viewsFolderName := "views"
282 viewsFolderPath := filepath.Join(tempDirectoryName, viewsFolderName)
283 err = fs.MkdirAll(viewsFolderPath, 0755)
284 if err != nil {
285 t.Errorf("unexpected error: %v", err)
286 }
287
288 htmlRelFilePath := filepath.Join(viewsFolderName, "view.html")
289 htmlFile, htmlFileStat, err := createAndStat(filepath.Join("views", "view.html"), tempDirectoryName, fs)
290 if err != nil {
291 t.Errorf("unexpected error: %v", err)
292 }
293 htmlFileAbsPath := htmlFile.Name()
294
295 targetFolderName := "target"
296 targetFolderRelPath := filepath.Join(viewsFolderName, targetFolderName)
297 targetFolderPath := filepath.Join(tempDirectoryName, targetFolderRelPath)
298 err = fs.MkdirAll(targetFolderPath, 0755)
299 if err != nil {
300 t.Errorf("unexpected error: %v", err)
301 }
302
303 targetFileName := "someFile.txt"
304 targetFileRelPath := filepath.Join(viewsFolderName, targetFolderName, targetFileName)
305 targetFilePath := filepath.Join(tempDirectoryName, targetFileRelPath)
306 _, targetFileStat, err := createAndStat(targetFileRelPath, tempDirectoryName, fs)
307 if err != nil {
308 t.Errorf("unexpected error: %v", err)
309 }
310
311 targetFolderStat, err := fs.Stat(filepath.Join(tempDirectoryName, viewsFolderName, targetFolderName))
312 if err != nil {
313 t.Errorf("unexpected error: %v", err)
314 }
315
316 viewsFolderStat, err := fs.Stat(filepath.Join(tempDirectoryName, viewsFolderName))
317 if err != nil {
318 t.Errorf("unexpected error: %v", err)
319 }
320
321 specialCharFolderName := "[devfile-registry]"
322 specialCharFolderPath := filepath.Join(tempDirectoryName, specialCharFolderName)
323 err = fs.MkdirAll(specialCharFolderPath, 0755)
324 if err != nil {
325 t.Errorf("unexpected error: %v", err)
326 }
327
328 fileInsideSpecialCharFolderRelPath := filepath.Join(specialCharFolderName, "index.tsx")
329 fileInsideSpecialCharFolderFile, fileInsideSpecialCharFolderStat, err := createAndStat(fileInsideSpecialCharFolderRelPath, tempDirectoryName, fs)
330 if err != nil {
331 t.Errorf("unexpected error: %v", err)
332 }
333 fileInsideSpecialCharFolderAbsPath := fileInsideSpecialCharFolderFile.Name()
334 specialCharFolderStat, err := fs.Stat(specialCharFolderPath)
335 if err != nil {
336 t.Errorf("unexpected error: %v", err)
337 }
338
339 defer os.RemoveAll(tempDirectoryName)
340
341 normalFileMap := map[string]FileData{
342 readmeFileName: {
343 Size: readmeFileStat.Size(),
344 LastModifiedDate: readmeFileStat.ModTime(),
345 },
346 jsFileName: {
347 Size: jsFileStat.Size(),
348 LastModifiedDate: jsFileStat.ModTime(),
349 },
350 viewsFolderName: {
351 Size: viewsFolderStat.Size(),
352 LastModifiedDate: viewsFolderStat.ModTime(),
353 },
354 htmlRelFilePath: {
355 Size: htmlFileStat.Size(),
356 LastModifiedDate: htmlFileStat.ModTime(),
357 },
358 targetFolderRelPath: {
359 Size: targetFolderStat.Size(),
360 LastModifiedDate: targetFolderStat.ModTime(),
361 },
362 targetFileRelPath: {
363 Size: targetFileStat.Size(),
364 LastModifiedDate: targetFileStat.ModTime(),
365 },
366 specialCharFolderName: {
367 Size: specialCharFolderStat.Size(),
368 LastModifiedDate: specialCharFolderStat.ModTime(),
369 },
370 fileInsideSpecialCharFolderRelPath: {
371 Size: fileInsideSpecialCharFolderStat.Size(),
372 LastModifiedDate: fileInsideSpecialCharFolderStat.ModTime(),
373 },
374 }
375
376 type args struct {
377 directory string
378 srcBase string
379 srcFile string
380 destBase string
381 destFile string
382 ignoreRules []string
383 remoteDirectories map[string]string
384 existingFileIndex FileIndex
385 }
386 tests := []struct {
387 name string
388 args args
389 want IndexerRet
390 emptyDir bool
391 wantErr bool
392 }{
393 {
394 name: "case 1: existing index is empty",
395 args: args{
396 directory: tempDirectoryName,
397 srcBase: tempDirectoryName,
398 ignoreRules: []string{},
399 remoteDirectories: map[string]string{},
400 existingFileIndex: FileIndex{},
401 },
402 want: IndexerRet{
403 FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
404 NewFileMap: normalFileMap,
405 },
406 wantErr: false,
407 },
408 {
409 name: "case 2: existing index exists and no file or folder changes occurs",
410 args: args{
411 directory: tempDirectoryName,
412 srcBase: tempDirectoryName,
413 ignoreRules: []string{},
414 remoteDirectories: map[string]string{},
415 existingFileIndex: FileIndex{
416 Files: normalFileMap,
417 },
418 },
419 want: IndexerRet{
420 NewFileMap: normalFileMap,
421 },
422 wantErr: false,
423 },
424 {
425 name: "case 3: file size changed",
426 args: args{
427 directory: tempDirectoryName,
428 srcBase: tempDirectoryName,
429 ignoreRules: []string{},
430 remoteDirectories: map[string]string{},
431 existingFileIndex: FileIndex{
432 Files: map[string]FileData{
433 readmeFileName: {
434 Size: readmeFileStat.Size() + 100,
435 LastModifiedDate: readmeFileStat.ModTime(),
436 },
437 jsFileName: normalFileMap[jsFileName],
438 viewsFolderName: normalFileMap[viewsFolderName],
439 htmlRelFilePath: normalFileMap[htmlRelFilePath],
440 targetFolderRelPath: normalFileMap[targetFolderRelPath],
441 targetFileRelPath: normalFileMap[targetFileRelPath],
442 specialCharFolderName: normalFileMap[specialCharFolderName],
443 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
444 },
445 },
446 },
447 want: IndexerRet{
448 FilesChanged: []string{readmeFileAbsPath},
449 NewFileMap: normalFileMap,
450 },
451 wantErr: false,
452 },
453 {
454 name: "case 4: folder size changed",
455 args: args{
456 directory: tempDirectoryName,
457 srcBase: tempDirectoryName,
458 ignoreRules: []string{},
459 remoteDirectories: map[string]string{},
460 existingFileIndex: FileIndex{
461 Files: map[string]FileData{
462 readmeFileName: normalFileMap[readmeFileName],
463 jsFileName: normalFileMap[jsFileName],
464 viewsFolderName: {
465 Size: viewsFolderStat.Size() + 100,
466 LastModifiedDate: viewsFolderStat.ModTime(),
467 },
468 htmlRelFilePath: normalFileMap[htmlRelFilePath],
469 targetFolderRelPath: normalFileMap[targetFolderRelPath],
470 targetFileRelPath: normalFileMap[targetFileRelPath],
471 specialCharFolderName: {
472 Size: specialCharFolderStat.Size() + 100,
473 LastModifiedDate: specialCharFolderStat.ModTime(),
474 },
475 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
476 },
477 },
478 },
479 want: IndexerRet{
480 FilesChanged: []string{viewsFolderPath, specialCharFolderPath},
481 NewFileMap: normalFileMap,
482 },
483 wantErr: false,
484 },
485 {
486 name: "case 5: file modified",
487 args: args{
488 directory: tempDirectoryName,
489 srcBase: tempDirectoryName,
490 ignoreRules: []string{},
491 remoteDirectories: map[string]string{},
492 existingFileIndex: FileIndex{
493 Files: map[string]FileData{
494 readmeFileName: {
495 Size: readmeFileStat.Size(),
496 LastModifiedDate: readmeFileStat.ModTime().Add(100),
497 },
498 jsFileName: normalFileMap[jsFileName],
499 viewsFolderName: normalFileMap[viewsFolderName],
500 htmlRelFilePath: normalFileMap[htmlRelFilePath],
501 targetFolderRelPath: normalFileMap[targetFolderRelPath],
502 targetFileRelPath: normalFileMap[targetFileRelPath],
503 specialCharFolderName: normalFileMap[specialCharFolderName],
504 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
505 },
506 },
507 },
508 want: IndexerRet{
509 FilesChanged: []string{readmeFileAbsPath},
510 NewFileMap: normalFileMap,
511 },
512 wantErr: false,
513 },
514 {
515 name: "case 6: folder modified",
516 args: args{
517 directory: tempDirectoryName,
518 srcBase: tempDirectoryName,
519 ignoreRules: []string{},
520 remoteDirectories: map[string]string{},
521 existingFileIndex: FileIndex{
522 Files: map[string]FileData{
523 readmeFileName: normalFileMap[readmeFileName],
524 jsFileName: normalFileMap[jsFileName],
525 viewsFolderName: {
526 Size: viewsFolderStat.Size(),
527 LastModifiedDate: viewsFolderStat.ModTime().Add(100),
528 },
529 htmlRelFilePath: normalFileMap[htmlRelFilePath],
530 targetFolderRelPath: normalFileMap[targetFolderRelPath],
531 targetFileRelPath: normalFileMap[targetFileRelPath],
532 specialCharFolderName: normalFileMap[specialCharFolderName],
533 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
534 },
535 },
536 },
537 want: IndexerRet{
538 FilesChanged: []string{viewsFolderPath},
539 NewFileMap: normalFileMap,
540 },
541 wantErr: false,
542 },
543 {
544 name: "case 7: both file and folder modified",
545 args: args{
546 directory: tempDirectoryName,
547 srcBase: tempDirectoryName,
548 ignoreRules: []string{},
549 remoteDirectories: map[string]string{},
550 existingFileIndex: FileIndex{
551 Files: map[string]FileData{
552 readmeFileName: {
553 Size: readmeFileStat.Size() + 100,
554 LastModifiedDate: readmeFileStat.ModTime(),
555 },
556 jsFileName: normalFileMap[jsFileName],
557 viewsFolderName: {
558 Size: viewsFolderStat.Size(),
559 LastModifiedDate: viewsFolderStat.ModTime().Add(100),
560 },
561 htmlRelFilePath: normalFileMap[htmlRelFilePath],
562 targetFolderRelPath: normalFileMap[targetFolderRelPath],
563 targetFileRelPath: normalFileMap[targetFileRelPath],
564 specialCharFolderName: normalFileMap[specialCharFolderName],
565 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
566 },
567 },
568 },
569 want: IndexerRet{
570 FilesChanged: []string{readmeFileAbsPath, viewsFolderPath},
571 NewFileMap: normalFileMap,
572 },
573 wantErr: false,
574 },
575
576 {
577 name: "case 8: ignore file with changes if remote exists",
578 args: args{
579 directory: tempDirectoryName,
580 srcBase: tempDirectoryName,
581 ignoreRules: []string{},
582 remoteDirectories: map[string]string{
583 htmlRelFilePath: filepath.Join("new", "Folder", "views.html"),
584 },
585 existingFileIndex: FileIndex{
586 Files: normalFileMap,
587 },
588 },
589 want: IndexerRet{
590 NewFileMap: map[string]FileData{
591 readmeFileStat.Name(): {
592 Size: readmeFileStat.Size(),
593 LastModifiedDate: readmeFileStat.ModTime(),
594 RemoteAttribute: "README.txt",
595 },
596 jsFileName: {
597 Size: jsFileStat.Size(),
598 LastModifiedDate: jsFileStat.ModTime(),
599 RemoteAttribute: "red.js",
600 },
601 viewsFolderName: {
602 Size: viewsFolderStat.Size(),
603 LastModifiedDate: viewsFolderStat.ModTime(),
604 RemoteAttribute: "views",
605 },
606 targetFolderRelPath: {
607 Size: targetFolderStat.Size(),
608 LastModifiedDate: targetFolderStat.ModTime(),
609 RemoteAttribute: targetFolderRelPath,
610 },
611 targetFileRelPath: {
612 Size: targetFileStat.Size(),
613 LastModifiedDate: targetFileStat.ModTime(),
614 RemoteAttribute: targetFileRelPath,
615 },
616 specialCharFolderName: {
617 Size: specialCharFolderStat.Size(),
618 LastModifiedDate: specialCharFolderStat.ModTime(),
619 RemoteAttribute: specialCharFolderName,
620 },
621 fileInsideSpecialCharFolderRelPath: {
622 Size: fileInsideSpecialCharFolderStat.Size(),
623 LastModifiedDate: fileInsideSpecialCharFolderStat.ModTime(),
624 RemoteAttribute: fileInsideSpecialCharFolderRelPath,
625 },
626 },
627 },
628 wantErr: false,
629 },
630 {
631 name: "case 9: remote removed for a file containing different remote destination",
632 args: args{
633 directory: tempDirectoryName,
634 srcBase: tempDirectoryName,
635 ignoreRules: []string{},
636 remoteDirectories: map[string]string{},
637 existingFileIndex: FileIndex{
638 Files: map[string]FileData{
639 readmeFileName: normalFileMap[readmeFileName],
640 jsFileName: normalFileMap[jsFileName],
641 viewsFolderName: normalFileMap[viewsFolderName],
642 htmlRelFilePath: {
643 Size: htmlFileStat.Size(),
644 LastModifiedDate: htmlFileStat.ModTime(),
645 RemoteAttribute: filepath.Join("new", "Folder", "views.html"),
646 },
647 targetFolderRelPath: normalFileMap[targetFolderRelPath],
648 targetFileRelPath: normalFileMap[targetFileRelPath],
649 specialCharFolderName: normalFileMap[specialCharFolderName],
650 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
651 },
652 },
653 },
654 want: IndexerRet{
655 FilesChanged: []string{htmlFileAbsPath},
656 RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder", "views.html")},
657 NewFileMap: normalFileMap,
658 },
659 wantErr: false,
660 },
661 {
662 name: "case 10: remote removed for a folder containing different remote destination",
663 args: args{
664 directory: tempDirectoryName,
665 srcBase: tempDirectoryName,
666 ignoreRules: []string{},
667 remoteDirectories: map[string]string{},
668 existingFileIndex: FileIndex{
669 Files: map[string]FileData{
670 readmeFileName: normalFileMap[readmeFileName],
671 jsFileName: normalFileMap[jsFileName],
672 viewsFolderName: {
673 Size: viewsFolderStat.Size(),
674 LastModifiedDate: viewsFolderStat.ModTime(),
675 RemoteAttribute: "new/Folder/views",
676 },
677 htmlRelFilePath: {
678 Size: htmlFileStat.Size(),
679 LastModifiedDate: htmlFileStat.ModTime(),
680 RemoteAttribute: filepath.Join("new", "Folder", "views.html"),
681 },
682 targetFolderRelPath: {
683 Size: htmlFileStat.Size(),
684 LastModifiedDate: htmlFileStat.ModTime(),
685 RemoteAttribute: "new/Folder/target",
686 },
687 targetFileRelPath: {
688 Size: htmlFileStat.Size(),
689 LastModifiedDate: htmlFileStat.ModTime(),
690 RemoteAttribute: "new/Folder/target/someFile.txt",
691 },
692 specialCharFolderName: normalFileMap[specialCharFolderName],
693 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
694 },
695 },
696 },
697 want: IndexerRet{
698 FilesChanged: []string{viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath},
699 RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), "new/Folder/target", "new/Folder/target/someFile.txt", filepath.Join("new", "Folder", "views.html"), "new/Folder/views"},
700 NewFileMap: normalFileMap,
701 },
702 wantErr: false,
703 },
704 {
705 name: "case 11: folder remote changed to local path",
706 args: args{
707 directory: tempDirectoryName,
708 srcBase: tempDirectoryName,
709 srcFile: viewsFolderName,
710 destFile: viewsFolderName,
711 ignoreRules: []string{},
712 remoteDirectories: map[string]string{
713 viewsFolderName: viewsFolderName,
714 },
715 existingFileIndex: FileIndex{
716 Files: map[string]FileData{
717 viewsFolderName: {
718 Size: viewsFolderStat.Size(),
719 LastModifiedDate: viewsFolderStat.ModTime(),
720 RemoteAttribute: "new/Folder/views",
721 },
722 htmlRelFilePath: {
723 Size: htmlFileStat.Size(),
724 LastModifiedDate: htmlFileStat.ModTime(),
725 RemoteAttribute: "new/Folder/views/view.html",
726 },
727 },
728 },
729 },
730 want: IndexerRet{
731 FilesChanged: []string{viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath},
732 RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), "new/Folder/views", "new/Folder/views/view.html"},
733 NewFileMap: map[string]FileData{
734 viewsFolderName: {
735 Size: viewsFolderStat.Size(),
736 LastModifiedDate: viewsFolderStat.ModTime(),
737 RemoteAttribute: filepath.ToSlash(viewsFolderName),
738 },
739 targetFolderRelPath: {
740 Size: targetFolderStat.Size(),
741 LastModifiedDate: targetFolderStat.ModTime(),
742 RemoteAttribute: filepath.ToSlash(targetFolderRelPath),
743 },
744 targetFileRelPath: {
745 Size: targetFileStat.Size(),
746 LastModifiedDate: targetFileStat.ModTime(),
747 RemoteAttribute: filepath.ToSlash(targetFileRelPath),
748 },
749 htmlRelFilePath: {
750 Size: htmlFileStat.Size(),
751 LastModifiedDate: htmlFileStat.ModTime(),
752 RemoteAttribute: filepath.ToSlash(htmlRelFilePath),
753 }},
754 },
755 wantErr: false,
756 },
757
758 {
759 name: "case 12: only a single file is checked and others are ignored",
760 args: args{
761 directory: tempDirectoryName,
762 srcBase: filepath.Join(tempDirectoryName, "views"),
763 srcFile: "view.html",
764 ignoreRules: []string{},
765 remoteDirectories: map[string]string{},
766 existingFileIndex: FileIndex{
767 Files: map[string]FileData{
768 htmlRelFilePath: {
769 Size: htmlFileStat.Size() + 100,
770 LastModifiedDate: htmlFileStat.ModTime(),
771 RemoteAttribute: "",
772 },
773 readmeFileStat.Name(): {
774 Size: readmeFileStat.Size() + 100,
775 LastModifiedDate: readmeFileStat.ModTime(),
776 },
777 jsFileName: normalFileMap["red.js"],
778 viewsFolderName: normalFileMap["views"],
779 },
780 },
781 },
782 want: IndexerRet{
783 FilesChanged: []string{htmlFileAbsPath},
784 NewFileMap: map[string]FileData{
785 htmlRelFilePath: {
786 Size: htmlFileStat.Size(),
787 LastModifiedDate: htmlFileStat.ModTime(),
788 },
789 },
790 },
791 wantErr: false,
792 },
793 {
794 name: "case 13: only a single file with a different remote is checked",
795 args: args{
796 directory: tempDirectoryName,
797 srcBase: tempDirectoryName,
798 srcFile: "README.txt",
799 ignoreRules: []string{},
800 remoteDirectories: map[string]string{
801 readmeFileStat.Name(): "new/Folder/text/README.txt",
802 },
803 existingFileIndex: FileIndex{
804 Files: normalFileMap,
805 },
806 },
807 want: IndexerRet{
808 FilesChanged: []string{readmeFileAbsPath},
809 RemoteDeleted: []string{filepath.ToSlash(readmeFileStat.Name())},
810 NewFileMap: map[string]FileData{
811 readmeFileStat.Name(): {
812 Size: readmeFileStat.Size(),
813 LastModifiedDate: readmeFileStat.ModTime(),
814 RemoteAttribute: "new/Folder/text/README.txt",
815 }},
816 },
817 wantErr: false,
818 },
819 {
820 name: "case 14: only a single file is checked with a remote removed",
821 args: args{
822 directory: tempDirectoryName,
823 srcBase: tempDirectoryName,
824 srcFile: "README.txt",
825 destBase: tempDirectoryName,
826 destFile: "README.txt",
827 ignoreRules: []string{},
828 remoteDirectories: map[string]string{},
829 existingFileIndex: FileIndex{
830 Files: map[string]FileData{
831 htmlRelFilePath: normalFileMap["views/view.html"],
832 readmeFileStat.Name(): {
833 Size: readmeFileStat.Size(),
834 LastModifiedDate: readmeFileStat.ModTime(),
835 RemoteAttribute: "new/Folder/text/README.txt",
836 },
837 jsFileName: normalFileMap[jsFileName],
838 viewsFolderName: normalFileMap[viewsFolderName],
839 },
840 },
841 },
842 want: IndexerRet{
843 FilesChanged: []string{readmeFileAbsPath},
844 RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder", "text"), filepath.Join("new", "Folder", "text", readmeFileName)},
845 NewFileMap: map[string]FileData{
846 readmeFileName: normalFileMap[readmeFileName],
847 },
848 },
849 wantErr: false,
850 },
851 {
852 name: "case 15: only a single file is checked with the same remote path earlier",
853 args: args{
854 directory: tempDirectoryName,
855 srcBase: tempDirectoryName,
856 srcFile: "README.txt",
857 destBase: tempDirectoryName,
858 destFile: "README.txt",
859 ignoreRules: []string{},
860 remoteDirectories: map[string]string{},
861 existingFileIndex: FileIndex{
862 Files: map[string]FileData{
863 htmlRelFilePath: normalFileMap["views/view.html"],
864 readmeFileStat.Name(): {
865 Size: readmeFileStat.Size(),
866 LastModifiedDate: readmeFileStat.ModTime(),
867 RemoteAttribute: "README.txt",
868 },
869 jsFileName: normalFileMap["red.js"],
870 viewsFolderName: normalFileMap["views"],
871 },
872 },
873 },
874 want: IndexerRet{
875 NewFileMap: map[string]FileData{
876 readmeFileStat.Name(): normalFileMap["README.txt"],
877 },
878 },
879 wantErr: false,
880 },
881 {
882 name: "case 16: only a single file is checked and there is no modification",
883 args: args{
884 directory: tempDirectoryName,
885 srcBase: viewsFolderPath,
886 srcFile: "view.html",
887 ignoreRules: []string{},
888 remoteDirectories: map[string]string{
889 htmlRelFilePath: "new/views/view.html",
890 },
891 existingFileIndex: FileIndex{
892 Files: map[string]FileData{
893 htmlRelFilePath: {
894 Size: htmlFileStat.Size(),
895 LastModifiedDate: htmlFileStat.ModTime(),
896 RemoteAttribute: "new/views/view.html",
897 },
898 },
899 },
900 },
901 want: IndexerRet{
902 NewFileMap: map[string]FileData{
903 htmlRelFilePath: {
904 Size: htmlFileStat.Size(),
905 LastModifiedDate: htmlFileStat.ModTime(),
906 RemoteAttribute: "new/views/view.html",
907 },
908 },
909 },
910 wantErr: false,
911 },
912 {
913 name: "case 17: file remote changed to local path",
914 args: args{
915 directory: tempDirectoryName,
916 srcBase: tempDirectoryName,
917 srcFile: "README.txt",
918 ignoreRules: []string{},
919 remoteDirectories: map[string]string{
920 readmeFileName: readmeFileName,
921 },
922 existingFileIndex: FileIndex{
923 Files: map[string]FileData{
924 readmeFileName: {
925 Size: readmeFileStat.Size(),
926 LastModifiedDate: readmeFileStat.ModTime(),
927 RemoteAttribute: "new/Folder/README.txt",
928 },
929 },
930 },
931 },
932 want: IndexerRet{
933 FilesChanged: []string{readmeFileAbsPath},
934 RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder", "README.txt")},
935 NewFileMap: map[string]FileData{
936 readmeFileName: {
937 Size: readmeFileStat.Size(),
938 LastModifiedDate: readmeFileStat.ModTime(),
939 RemoteAttribute: readmeFileStat.Name(),
940 }},
941 },
942 wantErr: false,
943 },
944
945 {
946 name: "case 18: file doesn't exist",
947 args: args{
948 directory: tempDirectoryName,
949 srcBase: viewsFolderPath,
950 srcFile: "views.html",
951 ignoreRules: []string{},
952 remoteDirectories: map[string]string{},
953 existingFileIndex: FileIndex{},
954 },
955 want: IndexerRet{
956 NewFileMap: map[string]FileData{},
957 },
958 wantErr: true,
959 },
960 {
961 name: "case 19: folder doesn't exist",
962 args: args{
963 directory: tempDirectoryName,
964 srcBase: tempDirectoryName + "blah",
965 ignoreRules: []string{},
966 remoteDirectories: map[string]string{},
967 existingFileIndex: FileIndex{
968 Files: map[string]FileData{},
969 },
970 },
971 want: IndexerRet{
972 NewFileMap: map[string]FileData{},
973 },
974 wantErr: true,
975 },
976
977 {
978 name: "case 20: ignore given file",
979 args: args{
980 directory: tempDirectoryName,
981 srcBase: tempDirectoryName,
982 ignoreRules: []string{"*.html"},
983 remoteDirectories: map[string]string{},
984 existingFileIndex: FileIndex{
985 Files: map[string]FileData{},
986 },
987 },
988 want: IndexerRet{
989 FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, targetFolderPath, targetFilePath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
990 NewFileMap: map[string]FileData{
991 jsFileName: normalFileMap["red.js"],
992 viewsFolderName: normalFileMap["views"],
993 readmeFileStat.Name(): normalFileMap["README.txt"],
994 targetFolderRelPath: normalFileMap[targetFolderRelPath],
995 targetFileRelPath: normalFileMap[targetFileRelPath],
996 specialCharFolderName: normalFileMap[specialCharFolderName],
997 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
998 },
999 },
1000 wantErr: false,
1001 },
1002 {
1003 name: "case 21: ignore given folder",
1004 args: args{
1005 directory: tempDirectoryName,
1006 srcBase: tempDirectoryName,
1007 ignoreRules: []string{viewsFolderName},
1008 remoteDirectories: map[string]string{},
1009 existingFileIndex: FileIndex{
1010 Files: map[string]FileData{},
1011 },
1012 },
1013 want: IndexerRet{
1014 FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
1015 NewFileMap: map[string]FileData{
1016 jsFileName: normalFileMap["red.js"],
1017 readmeFileStat.Name(): normalFileMap["README.txt"],
1018 specialCharFolderName: normalFileMap[specialCharFolderName],
1019 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1020 },
1021 },
1022 wantErr: false,
1023 },
1024
1025 {
1026 name: "case 22: only empty Dir with different remote location is checked",
1027 args: args{
1028 directory: tempDirectoryName,
1029 srcBase: filepath.Join(tempDirectoryName, "emptyDir"),
1030 srcFile: "",
1031 destBase: filepath.Join(tempDirectoryName, "emptyDir"),
1032 destFile: "",
1033 ignoreRules: []string{},
1034 remoteDirectories: map[string]string{
1035 "emptyDir": "new/Folder/",
1036 },
1037 existingFileIndex: FileIndex{
1038 Files: normalFileMap,
1039 },
1040 },
1041 emptyDir: true,
1042 want: IndexerRet{
1043 FilesChanged: []string{filepath.Join(tempDirectoryName, "emptyDir")},
1044 NewFileMap: map[string]FileData{},
1045 },
1046 wantErr: false,
1047 },
1048 {
1049 name: "case 23: folder containing a empty directory",
1050 args: args{
1051 directory: tempDirectoryName,
1052 srcBase: tempDirectoryName,
1053 ignoreRules: []string{},
1054 remoteDirectories: map[string]string{},
1055 existingFileIndex: FileIndex{
1056 Files: map[string]FileData{},
1057 },
1058 },
1059 emptyDir: true,
1060 want: IndexerRet{
1061 FilesChanged: []string{readmeFileAbsPath, filepath.Join(tempDirectoryName, "emptyDir"), jsFileAbsPath, viewsFolderPath, targetFolderPath, targetFilePath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
1062 NewFileMap: normalFileMap,
1063 },
1064 wantErr: false,
1065 },
1066 {
1067 name: "Case 24: subfolder is ignored",
1068 args: args{
1069 directory: tempDirectoryName,
1070 srcBase: tempDirectoryName,
1071 ignoreRules: []string{"target/"},
1072 remoteDirectories: map[string]string{},
1073 existingFileIndex: FileIndex{
1074 Files: map[string]FileData{},
1075 },
1076 },
1077 want: IndexerRet{
1078 FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, targetFolderPath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
1079 NewFileMap: map[string]FileData{
1080 readmeFileName: normalFileMap[readmeFileName],
1081 jsFileName: normalFileMap[jsFileName],
1082 viewsFolderName: normalFileMap[viewsFolderName],
1083 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1084 targetFolderRelPath: normalFileMap[targetFolderRelPath],
1085 specialCharFolderName: normalFileMap[specialCharFolderName],
1086 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1087 },
1088 },
1089 },
1090 }
1091 for _, tt := range tests {
1092 t.Run(tt.name, func(t *testing.T) {
1093 if tt.emptyDir {
1094 emptyDirPath := filepath.Join(tempDirectoryName, "emptyDir")
1095 err = fs.MkdirAll(emptyDirPath, 0755)
1096 if err != nil {
1097 t.Errorf("unexpected error: %v", err)
1098 }
1099
1100 defer func(name string) {
1101 err := os.Remove(name)
1102 if err != nil {
1103 t.Errorf("enexpected error: %v", err)
1104 }
1105 }(emptyDirPath)
1106
1107 emptyDirStat, err := fs.Stat(emptyDirPath)
1108 if err != nil {
1109 t.Errorf("enexpected error: %v", err)
1110 }
1111
1112 tt.want.NewFileMap[emptyDirStat.Name()] = FileData{
1113 Size: emptyDirStat.Size(),
1114 LastModifiedDate: emptyDirStat.ModTime(),
1115 RemoteAttribute: tt.args.remoteDirectories[emptyDirStat.Name()],
1116 }
1117 }
1118 pathsOptions := recursiveCheckerPathOptions{tt.args.directory, tt.args.srcBase, tt.args.srcFile, tt.args.destBase, tt.args.destFile}
1119 got, err := recursiveChecker(pathsOptions, tt.args.ignoreRules, tt.args.remoteDirectories, tt.args.existingFileIndex)
1120 if (err != nil) != tt.wantErr {
1121 t.Errorf("recursiveChecker() error = %v, wantErr %v", err, tt.wantErr)
1122 return
1123 }
1124
1125 if err != nil && tt.wantErr {
1126 return
1127 }
1128
1129 sortOpt := cmpopts.SortSlices(func(x, y string) bool {
1130 return x < y
1131 })
1132 if diff := cmp.Diff(tt.want.FilesChanged, got.FilesChanged, sortOpt); diff != "" {
1133 t.Errorf("recursiveChecker() FilesChanged mismatch (-want +got):\n%s", diff)
1134 }
1135
1136 if diff := cmp.Diff(tt.want.FilesDeleted, got.FilesDeleted, sortOpt); diff != "" {
1137 t.Errorf("recursiveChecker() FilesDeleted mismatch (-want +got):\n%s", diff)
1138 }
1139
1140 if diff := cmp.Diff(tt.want.RemoteDeleted, got.RemoteDeleted, sortOpt); diff != "" {
1141 t.Errorf("recursiveChecker() RemoteDeleted mismatch (-want +got):\n%s", diff)
1142 }
1143
1144 if diff := cmp.Diff(tt.want.NewFileMap, got.NewFileMap); diff != "" {
1145 t.Errorf("recursiveChecker() NewFileMap mismatch (-want +got):\n%s", diff)
1146 }
1147 })
1148 }
1149 }
1150
1151 func Test_runIndexerWithExistingFileIndex(t *testing.T) {
1152 fs := filesystem.DefaultFs{}
1153
1154 tempDirectoryName, err := fs.TempDir(os.TempDir(), "dir0")
1155 if err != nil {
1156 t.Errorf("unexpected error: %v", err)
1157 }
1158
1159 jsFileName := "red.js"
1160 jsFile, jsFileStat, err := createAndStat(jsFileName, tempDirectoryName, fs)
1161 if err != nil {
1162 t.Errorf("unexpected error: %v", err)
1163 }
1164 jsFileAbsPath := jsFile.Name()
1165
1166 readmeFileName := "README.txt"
1167 readmeFile, readmeFileStat, err := createAndStat(readmeFileName, tempDirectoryName, fs)
1168 if err != nil {
1169 t.Errorf("unexpected error: %v", err)
1170 }
1171 readmeFileAbsPath := readmeFile.Name()
1172
1173 viewsFolderName := "views"
1174 viewsFolderPath := filepath.Join(tempDirectoryName, viewsFolderName)
1175 err = fs.MkdirAll(viewsFolderPath, 0755)
1176 if err != nil {
1177 t.Errorf("unexpected error: %v", err)
1178 }
1179
1180 htmlRelFilePath := filepath.Join(viewsFolderName, "view.html")
1181 htmlFile, htmlFileStat, err := createAndStat(filepath.Join("views", "view.html"), tempDirectoryName, fs)
1182 if err != nil {
1183 t.Errorf("unexpected error: %v", err)
1184 }
1185 htmlFileAbsPath := htmlFile.Name()
1186
1187 viewsFolderStat, err := fs.Stat(filepath.Join(tempDirectoryName, viewsFolderName))
1188 if err != nil {
1189 t.Errorf("unexpected error: %v", err)
1190 }
1191
1192 specialCharFolderName := "[devfile-registry]"
1193 specialCharFolderPath := filepath.Join(tempDirectoryName, specialCharFolderName)
1194 err = fs.MkdirAll(specialCharFolderPath, 0755)
1195 if err != nil {
1196 t.Errorf("unexpected error: %v", err)
1197 }
1198
1199 fileInsideSpecialCharFolderRelPath := filepath.Join(specialCharFolderName, "index.tsx")
1200 fileInsideSpecialCharFolderFile, fileInsideSpecialCharFolderFileStat, err := createAndStat(fileInsideSpecialCharFolderRelPath, tempDirectoryName, fs)
1201 if err != nil {
1202 t.Errorf("unexpected error: %v", err)
1203 }
1204 fileInsideSpecialCharFolderAbsPath := fileInsideSpecialCharFolderFile.Name()
1205
1206 specialCharFolderStat, err := fs.Stat(specialCharFolderPath)
1207 if err != nil {
1208 t.Errorf("unexpected error: %v", err)
1209 }
1210
1211 defer os.RemoveAll(tempDirectoryName)
1212
1213 normalFileMap := map[string]FileData{
1214 readmeFileName: {
1215 Size: readmeFileStat.Size(),
1216 LastModifiedDate: readmeFileStat.ModTime(),
1217 },
1218 jsFileName: {
1219 Size: jsFileStat.Size(),
1220 LastModifiedDate: jsFileStat.ModTime(),
1221 },
1222 viewsFolderName: {
1223 Size: viewsFolderStat.Size(),
1224 LastModifiedDate: viewsFolderStat.ModTime(),
1225 },
1226 htmlRelFilePath: {
1227 Size: htmlFileStat.Size(),
1228 LastModifiedDate: htmlFileStat.ModTime(),
1229 },
1230 specialCharFolderName: {
1231 Size: specialCharFolderStat.Size(),
1232 LastModifiedDate: specialCharFolderStat.ModTime(),
1233 },
1234 fileInsideSpecialCharFolderRelPath: {
1235 Size: fileInsideSpecialCharFolderFileStat.Size(),
1236 LastModifiedDate: fileInsideSpecialCharFolderFileStat.ModTime(),
1237 },
1238 }
1239
1240 type args struct {
1241 directory string
1242 ignoreRules []string
1243 remoteDirectories map[string]string
1244 existingFileIndex *FileIndex
1245 }
1246 tests := []struct {
1247 name string
1248 args args
1249 wantRet IndexerRet
1250 wantErr bool
1251 }{
1252 {
1253 name: "case 1: normal directory with no existing file index data",
1254 args: args{
1255 directory: tempDirectoryName,
1256 ignoreRules: []string{},
1257 remoteDirectories: map[string]string{},
1258 existingFileIndex: &FileIndex{},
1259 },
1260 wantRet: IndexerRet{
1261 FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
1262 NewFileMap: normalFileMap,
1263 },
1264 wantErr: false,
1265 },
1266 {
1267 name: "case 2: normal directory with existing file index data",
1268 args: args{
1269 directory: tempDirectoryName,
1270 ignoreRules: []string{},
1271 remoteDirectories: map[string]string{},
1272 existingFileIndex: &FileIndex{
1273 Files: normalFileMap,
1274 },
1275 },
1276 wantRet: IndexerRet{
1277 NewFileMap: normalFileMap,
1278 },
1279 wantErr: false,
1280 },
1281 {
1282 name: "case 3: normal directory with existing file index data and new files are added",
1283 args: args{
1284 directory: tempDirectoryName,
1285 ignoreRules: []string{},
1286 remoteDirectories: map[string]string{},
1287 existingFileIndex: &FileIndex{
1288 Files: map[string]FileData{
1289 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1290 },
1291 },
1292 },
1293 wantRet: IndexerRet{
1294 FilesChanged: []string{readmeFileAbsPath, jsFileAbsPath, viewsFolderPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
1295 NewFileMap: normalFileMap,
1296 },
1297 wantErr: false,
1298 },
1299 {
1300 name: "case 4: normal directory with existing file index data and files are deleted",
1301 args: args{
1302 directory: tempDirectoryName,
1303 ignoreRules: []string{},
1304 remoteDirectories: map[string]string{},
1305 existingFileIndex: &FileIndex{
1306 Files: map[string]FileData{
1307 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1308 jsFileName: normalFileMap[jsFileName],
1309 viewsFolderName: normalFileMap[viewsFolderName],
1310 readmeFileName: normalFileMap[readmeFileName],
1311 specialCharFolderName: normalFileMap[specialCharFolderName],
1312 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1313 "blah": {},
1314 },
1315 },
1316 },
1317 wantRet: IndexerRet{
1318 FilesDeleted: []string{"blah"},
1319 NewFileMap: normalFileMap,
1320 },
1321 wantErr: false,
1322 },
1323
1324 {
1325 name: "case 5: with remote directories and no existing file index",
1326 args: args{
1327 directory: tempDirectoryName,
1328 ignoreRules: []string{},
1329 remoteDirectories: map[string]string{viewsFolderName: filepath.Join("new", "Folder"), htmlRelFilePath: filepath.Join("new", "Folder0", "views.html")},
1330 existingFileIndex: &FileIndex{},
1331 },
1332 wantRet: IndexerRet{
1333 FilesChanged: []string{viewsFolderPath, htmlFileAbsPath},
1334 NewFileMap: map[string]FileData{
1335 htmlRelFilePath: {
1336 Size: htmlFileStat.Size(),
1337 LastModifiedDate: htmlFileStat.ModTime(),
1338 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1339 },
1340 viewsFolderName: {
1341 Size: viewsFolderStat.Size(),
1342 LastModifiedDate: viewsFolderStat.ModTime(),
1343 RemoteAttribute: filepath.Join("new", "Folder"),
1344 },
1345 },
1346 },
1347 wantErr: false,
1348 },
1349 {
1350 name: "case 6: with remote directories and no modification",
1351 args: args{
1352 directory: tempDirectoryName,
1353 ignoreRules: []string{},
1354 remoteDirectories: map[string]string{htmlRelFilePath: filepath.Join("new", "Folder0", "views.html"), viewsFolderName: filepath.Join("new", "Folder")},
1355 existingFileIndex: &FileIndex{
1356 Files: map[string]FileData{
1357 htmlRelFilePath: {
1358 Size: htmlFileStat.Size(),
1359 LastModifiedDate: htmlFileStat.ModTime(),
1360 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1361 },
1362 viewsFolderName: {
1363 Size: viewsFolderStat.Size(),
1364 LastModifiedDate: viewsFolderStat.ModTime(),
1365 RemoteAttribute: filepath.Join("new", "Folder"),
1366 },
1367 },
1368 },
1369 },
1370 wantRet: IndexerRet{
1371 NewFileMap: map[string]FileData{
1372 htmlRelFilePath: {
1373 Size: htmlFileStat.Size(),
1374 LastModifiedDate: htmlFileStat.ModTime(),
1375 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1376 },
1377 viewsFolderName: {
1378 Size: viewsFolderStat.Size(),
1379 LastModifiedDate: viewsFolderStat.ModTime(),
1380 RemoteAttribute: filepath.Join("new", "Folder"),
1381 },
1382 },
1383 },
1384 wantErr: false,
1385 },
1386 {
1387 name: "case 7: with remote directories and files deleted",
1388 args: args{
1389 directory: tempDirectoryName,
1390 ignoreRules: []string{},
1391 remoteDirectories: map[string]string{
1392 htmlRelFilePath: filepath.Join("new", "Folder0", "views.html"),
1393 viewsFolderName: filepath.Join("new", "Folder"),
1394 },
1395 existingFileIndex: &FileIndex{
1396 Files: map[string]FileData{
1397 htmlRelFilePath: {
1398 Size: htmlFileStat.Size(),
1399 LastModifiedDate: htmlFileStat.ModTime(),
1400 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1401 },
1402 viewsFolderName: {
1403 Size: viewsFolderStat.Size(),
1404 LastModifiedDate: viewsFolderStat.ModTime(),
1405 RemoteAttribute: filepath.Join("new", "Folder"),
1406 },
1407 "blah": {},
1408 },
1409 },
1410 },
1411 wantRet: IndexerRet{
1412 FilesDeleted: []string{"blah"},
1413 NewFileMap: map[string]FileData{
1414 htmlRelFilePath: {
1415 Size: htmlFileStat.Size(),
1416 LastModifiedDate: htmlFileStat.ModTime(),
1417 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1418 },
1419 viewsFolderName: {
1420 Size: viewsFolderStat.Size(),
1421 LastModifiedDate: viewsFolderStat.ModTime(),
1422 RemoteAttribute: filepath.Join("new", "Folder"),
1423 },
1424 },
1425 },
1426 wantErr: false,
1427 },
1428 {
1429 name: "case 8: remote changed",
1430 args: args{
1431 directory: tempDirectoryName,
1432 ignoreRules: []string{},
1433 remoteDirectories: map[string]string{
1434 htmlRelFilePath: filepath.Join("new", "Folder0", "views.html"),
1435 viewsFolderName: filepath.Join("new", "blah", "Folder"),
1436 },
1437 existingFileIndex: &FileIndex{
1438 Files: map[string]FileData{
1439 htmlRelFilePath: {
1440 Size: htmlFileStat.Size(),
1441 LastModifiedDate: htmlFileStat.ModTime(),
1442 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1443 },
1444 viewsFolderName: {
1445 Size: viewsFolderStat.Size(),
1446 LastModifiedDate: viewsFolderStat.ModTime(),
1447 RemoteAttribute: filepath.Join("new", "Folder"),
1448 },
1449 },
1450 },
1451 },
1452 wantRet: IndexerRet{
1453 FilesChanged: []string{viewsFolderPath},
1454 RemoteDeleted: []string{filepath.Join("new", "Folder")},
1455 NewFileMap: map[string]FileData{
1456 htmlRelFilePath: {
1457 Size: htmlFileStat.Size(),
1458 LastModifiedDate: htmlFileStat.ModTime(),
1459 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1460 },
1461 viewsFolderName: {
1462 Size: viewsFolderStat.Size(),
1463 LastModifiedDate: viewsFolderStat.ModTime(),
1464 RemoteAttribute: filepath.Join("new", "blah", "Folder"),
1465 },
1466 },
1467 },
1468 wantErr: false,
1469 },
1470 {
1471 name: "case 9: remote of a file removed",
1472 args: args{
1473 directory: tempDirectoryName,
1474 ignoreRules: []string{},
1475 remoteDirectories: map[string]string{htmlRelFilePath: filepath.Join("new", "Folder0", "views.html")},
1476 existingFileIndex: &FileIndex{
1477 Files: map[string]FileData{
1478 htmlRelFilePath: {
1479 Size: htmlFileStat.Size(),
1480 LastModifiedDate: htmlFileStat.ModTime(),
1481 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1482 },
1483 viewsFolderName: {
1484 Size: viewsFolderStat.Size(),
1485 LastModifiedDate: viewsFolderStat.ModTime(),
1486 RemoteAttribute: filepath.Join("new", "Folder"),
1487 },
1488 },
1489 },
1490 },
1491 wantRet: IndexerRet{
1492 RemoteDeleted: []string{filepath.Join("new", "Folder")},
1493 NewFileMap: map[string]FileData{
1494 htmlRelFilePath: {
1495 Size: htmlFileStat.Size(),
1496 LastModifiedDate: htmlFileStat.ModTime(),
1497 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1498 },
1499 },
1500 },
1501 wantErr: false,
1502 },
1503 {
1504 name: "case 10: all remotes removed",
1505 args: args{
1506 directory: tempDirectoryName,
1507 ignoreRules: []string{},
1508 remoteDirectories: map[string]string{},
1509 existingFileIndex: &FileIndex{
1510 Files: map[string]FileData{
1511 readmeFileName: {
1512 Size: readmeFileStat.Size(),
1513 LastModifiedDate: readmeFileStat.ModTime(),
1514 RemoteAttribute: readmeFileStat.Name(),
1515 },
1516 htmlRelFilePath: {
1517 Size: htmlFileStat.Size(),
1518 LastModifiedDate: htmlFileStat.ModTime(),
1519 RemoteAttribute: filepath.Join("new", "Folder0", "views.html"),
1520 },
1521 viewsFolderName: {
1522 Size: viewsFolderStat.Size(),
1523 LastModifiedDate: viewsFolderStat.ModTime(),
1524 RemoteAttribute: filepath.Join("new", "Folder"),
1525 },
1526 },
1527 },
1528 },
1529 wantRet: IndexerRet{
1530 FilesChanged: []string{jsFileAbsPath, viewsFolderPath, htmlFileAbsPath, specialCharFolderPath, fileInsideSpecialCharFolderAbsPath},
1531 RemoteDeleted: []string{"new", filepath.Join("new", "Folder"), filepath.Join("new", "Folder0"), filepath.Join("new", "Folder0", "views.html")},
1532 NewFileMap: normalFileMap,
1533 },
1534 wantErr: false,
1535 },
1536 {
1537 name: "case 11: remote added for a file but local path and remote destination are same",
1538 args: args{
1539 directory: tempDirectoryName,
1540 ignoreRules: []string{},
1541 remoteDirectories: map[string]string{htmlRelFilePath: htmlRelFilePath},
1542 existingFileIndex: &FileIndex{
1543 Files: map[string]FileData{
1544 htmlRelFilePath: {
1545 Size: htmlFileStat.Size(),
1546 LastModifiedDate: htmlFileStat.ModTime(),
1547 },
1548 },
1549 },
1550 },
1551 wantRet: IndexerRet{
1552 NewFileMap: map[string]FileData{
1553 htmlRelFilePath: {
1554 Size: htmlFileStat.Size(),
1555 LastModifiedDate: htmlFileStat.ModTime(),
1556 RemoteAttribute: filepath.ToSlash(htmlRelFilePath),
1557 },
1558 },
1559 },
1560 wantErr: false,
1561 },
1562
1563 {
1564 name: "case 12: ignore a modified file due to ignore rules",
1565 args: args{
1566 directory: tempDirectoryName,
1567 ignoreRules: []string{readmeFileName},
1568 remoteDirectories: map[string]string{},
1569 existingFileIndex: &FileIndex{
1570 Files: map[string]FileData{
1571 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1572 viewsFolderName: normalFileMap[viewsFolderName],
1573 jsFileName: normalFileMap[jsFileName],
1574 specialCharFolderName: normalFileMap[specialCharFolderName],
1575 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1576 },
1577 },
1578 },
1579 wantRet: IndexerRet{
1580 NewFileMap: map[string]FileData{
1581 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1582 viewsFolderName: normalFileMap[viewsFolderName],
1583 jsFileName: normalFileMap[jsFileName],
1584 specialCharFolderName: normalFileMap[specialCharFolderName],
1585 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1586 },
1587 },
1588 wantErr: false,
1589 },
1590 {
1591 name: "case 13: ignore a deleted file due to ignore rules",
1592 args: args{
1593 directory: tempDirectoryName,
1594 ignoreRules: []string{"blah"},
1595 remoteDirectories: map[string]string{},
1596 existingFileIndex: &FileIndex{
1597 Files: map[string]FileData{
1598 readmeFileName: normalFileMap[readmeFileName],
1599 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1600 viewsFolderName: normalFileMap[viewsFolderName],
1601 jsFileName: normalFileMap[jsFileName],
1602 "blah": {},
1603 specialCharFolderName: normalFileMap[specialCharFolderName],
1604 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1605 },
1606 },
1607 },
1608 wantRet: IndexerRet{
1609 NewFileMap: normalFileMap,
1610 },
1611 wantErr: false,
1612 },
1613 {
1614 name: "case 14: ignore a added file due to ignore rules",
1615 args: args{
1616 directory: tempDirectoryName,
1617 ignoreRules: []string{readmeFileName},
1618 remoteDirectories: map[string]string{},
1619 existingFileIndex: &FileIndex{
1620 Files: map[string]FileData{
1621 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1622 viewsFolderName: normalFileMap[viewsFolderName],
1623 jsFileName: normalFileMap[jsFileName],
1624 specialCharFolderName: normalFileMap[specialCharFolderName],
1625 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1626 },
1627 },
1628 },
1629 wantRet: IndexerRet{
1630 NewFileMap: map[string]FileData{
1631 htmlRelFilePath: normalFileMap[htmlRelFilePath],
1632 viewsFolderName: normalFileMap[viewsFolderName],
1633 jsFileName: normalFileMap[jsFileName],
1634 specialCharFolderName: normalFileMap[specialCharFolderName],
1635 fileInsideSpecialCharFolderRelPath: normalFileMap[fileInsideSpecialCharFolderRelPath],
1636 },
1637 },
1638 wantErr: false,
1639 },
1640 {
1641 name: "case 15: local file doesn't exist",
1642 args: args{
1643 directory: tempDirectoryName,
1644 ignoreRules: []string{},
1645 remoteDirectories: map[string]string{htmlRelFilePath + "blah": htmlRelFilePath},
1646 existingFileIndex: &FileIndex{
1647 Files: map[string]FileData{},
1648 },
1649 },
1650 wantRet: IndexerRet{},
1651 wantErr: true,
1652 },
1653 {
1654 name: "case 16: local folder doesn't exist",
1655 args: args{
1656 directory: tempDirectoryName,
1657 ignoreRules: []string{},
1658 remoteDirectories: map[string]string{viewsFolderPath + "blah": viewsFolderPath},
1659 existingFileIndex: &FileIndex{
1660 Files: map[string]FileData{},
1661 },
1662 },
1663 wantRet: IndexerRet{},
1664 wantErr: true,
1665 },
1666 }
1667 for _, tt := range tests {
1668 t.Run(tt.name, func(t *testing.T) {
1669 gotRet, err := runIndexerWithExistingFileIndex(tt.args.directory, tt.args.ignoreRules, tt.args.remoteDirectories, tt.args.existingFileIndex)
1670 if (err != nil) != tt.wantErr {
1671 t.Errorf("runIndexerWithExistingFileIndex() error = %v, wantErr %v", err, tt.wantErr)
1672 return
1673 }
1674
1675 if err != nil && tt.wantErr {
1676 return
1677 }
1678
1679 sortOpt := cmpopts.SortSlices(func(x, y string) bool {
1680 return x < y
1681 })
1682
1683 if diff := cmp.Diff(tt.wantRet.FilesChanged, gotRet.FilesChanged, sortOpt); diff != "" {
1684 t.Errorf("runIndexerWithExistingFileIndex() FilesChanged mismatch (-want +got):\n%s", diff)
1685 }
1686 if diff := cmp.Diff(tt.wantRet.NewFileMap, gotRet.NewFileMap); diff != "" {
1687 t.Errorf("runIndexerWithExistingFileIndex() NewFileMap mismatch (-want +got):\n%s", diff)
1688 }
1689
1690 if diff := cmp.Diff(tt.wantRet.FilesDeleted, gotRet.FilesDeleted, sortOpt); diff != "" {
1691 t.Errorf("runIndexerWithExistingFileIndex() FilesDeleted mismatch (-want +got):\n%s", diff)
1692 }
1693 if diff := cmp.Diff(tt.wantRet.RemoteDeleted, gotRet.RemoteDeleted, sortOpt); diff != "" {
1694 t.Errorf("runIndexerWithExistingFileIndex() RemoteDeleted mismatch (-want +got):\n%s", diff)
1695 }
1696 })
1697 }
1698 }
1699
1700
1701 func Test_globEscape(t *testing.T) {
1702 cases := []struct {
1703 value string
1704 want string
1705 }{
1706 {"abc d", "abc d"},
1707 {"*abc d", "[*]abc d"},
1708 {"*****", "[*][*][*][*][*]"},
1709 {"[]*abDEFG?", "[[]][*]abDEFG[?]"},
1710 {"a*", "a[*]"},
1711 {"a*b*c*d*e*/f", "a[*]b[*]c[*]d[*]e[*]/f"},
1712 {"a*b?c*x", "a[*]b[?]c[*]x"},
1713 {"ab[c]", "ab[[]c]"},
1714 {"ab[b-d]", "ab[[]b-d]"},
1715 {"ab[^c]", "ab[[]^c]"},
1716 {"ab[^b-d]", "ab[[]^b-d]"},
1717 {"a???b", "a[?][?][?]b"},
1718 {"a\\\\???b", "a\\\\[?][?][?]b"},
1719 {"foo\\[bar]xyzzy", "foo\\[[]bar]xyzzy"},
1720 {"(\\*\\?\\[\\])", "(\\[*]\\[?]\\[[]\\])"},
1721 {"a\\?\\?\\?b", "a\\[?]\\[?]\\[?]b"},
1722 {"a\\\\?\\?\\?b", "a\\\\[?]\\[?]\\[?]b"},
1723 {"a[^a][^a][^a]b☺", "a[[]^a][[]^a][[]^a]b☺"},
1724 {"[a-ζ]*", "[[]a-ζ][*]"},
1725 {"*[a-ζ]*", "[*][[]a-ζ][*]"},
1726 {"[\\]a]", "[[]\\]a]"},
1727 {"lmnopqrstuva]", "lmnopqrstuva]"},
1728 {"こんにちは", "こんにちは"},
1729 {"こ[んに]ちは", "こ[[]んに]ちは"},
1730 }
1731 for _, tc := range cases {
1732 if got := globEscape(tc.value); got != tc.want {
1733 t.Errorf("GlobEscape: %q expected %q got %q", tc.value, tc.want, got)
1734 }
1735 }
1736 }
1737
View as plain text