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 }