github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/fs/fs_test.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package fs
    18  
    19  import (
    20  	"io"
    21  	"io/fs"
    22  	"os"
    23  	"path"
    24  	"reflect"
    25  	"testing"
    26  
    27  	billy "github.com/go-git/go-billy/v5"
    28  	"github.com/go-git/go-billy/v5/osfs"
    29  	"github.com/go-git/go-billy/v5/util"
    30  	git "github.com/libgit2/git2go/v34"
    31  )
    32  
    33  func TestFs(t *testing.T) {
    34  	tcs := []struct {
    35  		Name string
    36  		Data func(fs billy.Filesystem) error
    37  	}{
    38  		{
    39  			Name: "Writing one file",
    40  			Data: func(fs billy.Filesystem) error {
    41  				err := fs.MkdirAll("foo", 0777)
    42  				if err != nil {
    43  					return err
    44  				}
    45  				return util.WriteFile(fs, "foo/bar", []byte("baz"), 0666)
    46  			},
    47  		},
    48  		{
    49  			Name: "Deep path",
    50  			Data: func(fs billy.Filesystem) error {
    51  				err := fs.MkdirAll("foo/bar/baz/buz", 0777)
    52  				if err != nil {
    53  					return err
    54  				}
    55  				err = fs.MkdirAll("foo/bar/baz/boz/", 0777)
    56  				if err != nil {
    57  					return err
    58  				}
    59  				err = util.WriteFile(fs, "foo/bar/baz/buz/zup", []byte("baz"), 0666)
    60  				if err != nil {
    61  					return err
    62  				}
    63  				err = util.WriteFile(fs, "foo/bar/baz/boz/zup", []byte("baz"), 0666)
    64  				if err != nil {
    65  					return err
    66  				}
    67  				return nil
    68  			},
    69  		},
    70  		{
    71  			Name: "Symlink",
    72  			Data: func(fs billy.Filesystem) error {
    73  				err := fs.MkdirAll("foo", 0777)
    74  				if err != nil {
    75  					return err
    76  				}
    77  				err = fs.Symlink("foo", "bar")
    78  				if err != nil {
    79  					return err
    80  				}
    81  				err = util.WriteFile(fs, "bar/baz", []byte("baz"), 0666)
    82  				if err != nil {
    83  					return err
    84  				}
    85  				return nil
    86  			},
    87  		},
    88  	}
    89  
    90  	for _, tc := range tcs {
    91  		tc := tc
    92  		t.Run(tc.Name, func(t *testing.T) {
    93  			t.Parallel()
    94  			repo, err := git.InitRepository(t.TempDir(), true)
    95  			if err != nil {
    96  				t.Fatal(err)
    97  			}
    98  			// create an actual file directory
    99  			actual := osfs.New(t.TempDir())
   100  			err = tc.Data(actual)
   101  			if err != nil {
   102  				t.Fatal(err)
   103  			}
   104  
   105  			// build a tree fs and compare the result
   106  			tree := NewEmptyTreeBuildFS(repo)
   107  			err = tc.Data(tree)
   108  			if err != nil {
   109  				t.Fatal(err)
   110  			}
   111  			compareDir(t, actual, tree, ".")
   112  
   113  			// write the tree into git
   114  			oid, _, err := tree.insert()
   115  			if err != nil {
   116  				t.Fatal(err)
   117  			}
   118  
   119  			// read the tree from git again
   120  			tree2 := NewTreeBuildFS(repo, oid)
   121  			compareDir(t, actual, tree2, ".")
   122  
   123  			// checkout the tree into a folder an compare again
   124  			gitTree, err := repo.LookupTree(oid)
   125  			if err != nil {
   126  				t.Fatal(err)
   127  			}
   128  			tmpDir := t.TempDir()
   129  			err = repo.CheckoutTree(gitTree, &git.CheckoutOpts{
   130  				Strategy:        git.CheckoutForce,
   131  				TargetDirectory: tmpDir,
   132  			})
   133  			checkedOut := osfs.New(tmpDir)
   134  			compareDir(t, tree, checkedOut, ".")
   135  		})
   136  	}
   137  }
   138  
   139  func compareDir(t *testing.T, expected, actual billy.Filesystem, dir string) {
   140  	t.Helper()
   141  	ar, err := actual.ReadDir(dir)
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  	br, err := expected.ReadDir(dir)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	type mapEntry struct{ a, b os.FileInfo }
   150  	files := map[string]*mapEntry{}
   151  	for _, af := range ar {
   152  		files[af.Name()] = &mapEntry{a: af}
   153  	}
   154  	for _, bf := range br {
   155  		if e, ok := files[bf.Name()]; ok {
   156  			e.b = bf
   157  		} else {
   158  			files[bf.Name()] = &mapEntry{b: bf}
   159  		}
   160  	}
   161  	for name, entry := range files {
   162  		p := path.Join(dir, name)
   163  		if entry.a == nil {
   164  			t.Errorf("missing file: %s", p)
   165  		} else if entry.b == nil {
   166  			t.Errorf("unexpected file: %s", p)
   167  		} else {
   168  			if entry.a.Mode()&fs.ModeType != entry.b.Mode()&fs.ModeType {
   169  				t.Errorf("mismatched mode for %s: expected %q, actual %q", p, entry.b.Mode(), entry.a.Mode())
   170  			} else {
   171  				if entry.a.IsDir() {
   172  					compareDir(t, expected, actual, p)
   173  				} else if entry.a.Mode().IsRegular() {
   174  					compareContent(t, expected, actual, p)
   175  				}
   176  			}
   177  			astat, err := actual.Stat(p)
   178  			if err != nil {
   179  				t.Fatal(err)
   180  			}
   181  			bstat, err := expected.Stat(p)
   182  			if err != nil {
   183  				t.Fatal(err)
   184  			}
   185  			if astat.Name() != bstat.Name() {
   186  				t.Errorf("mismateched stat name for %s: expected %q, actual %q", p, bstat.Name(), astat.Name())
   187  			}
   188  		}
   189  	}
   190  }
   191  
   192  func compareContent(t *testing.T, expected, actual billy.Filesystem, file string) {
   193  	t.Helper()
   194  	af, err := actual.Open(file)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	defer af.Close()
   199  	ac, err := io.ReadAll(af)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  
   204  	bf, err := expected.Open(file)
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	defer bf.Close()
   209  	bc, err := io.ReadAll(bf)
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	if !reflect.DeepEqual(ac, bc) {
   215  		t.Errorf("content differs for %s:\n\texpected: %q\n\tactual: %q", file, bc, ac)
   216  	}
   217  }
   218  
   219  func dumpFs(t *testing.T, fs billy.Filesystem, indent string) {
   220  	infos, err := fs.ReadDir(".")
   221  	if err != nil {
   222  		t.Logf("%s err: %q\n", indent, err)
   223  	} else {
   224  		for _, i := range infos {
   225  			t.Logf("%s - %s\n", indent, i.Name())
   226  			if i.Mode()&os.ModeSymlink != 0 {
   227  				lnk, _ := fs.Readlink(i.Name())
   228  				t.Logf("%s   linked to: %s\n", indent, lnk)
   229  			}
   230  			if i.IsDir() {
   231  				ch, _ := fs.Chroot(i.Name())
   232  				dumpFs(t, ch, indent+"  ")
   233  			}
   234  		}
   235  	}
   236  }