github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/internal/datafs/wdfs_test.go (about) 1 package datafs 2 3 import ( 4 "fmt" 5 "io" 6 "io/fs" 7 "os" 8 "path/filepath" 9 "runtime" 10 "testing" 11 12 "github.com/hack-pad/hackpadfs" 13 "github.com/hack-pad/hackpadfs/mem" 14 osfs "github.com/hack-pad/hackpadfs/os" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 tfs "gotest.tools/v3/fs" 18 ) 19 20 func TestWDFS_ReadOps(t *testing.T) { 21 wd, _ := os.Getwd() 22 t.Cleanup(func() { 23 _ = os.Chdir(wd) 24 }) 25 _ = os.Chdir("/") 26 27 memfs, _ := mem.NewFS() 28 29 _ = memfs.Mkdir("tmp", 0o777) 30 _ = hackpadfs.WriteFullFile(memfs, "tmp/foo", []byte("hello world"), 0o777) 31 _ = hackpadfs.WriteFullFile(memfs, "tmp/one.txt", []byte("one"), 0o644) 32 _ = hackpadfs.WriteFullFile(memfs, "tmp/two.txt", []byte("two"), 0o644) 33 _ = hackpadfs.WriteFullFile(memfs, "tmp/three.txt", []byte("three"), 0o644) 34 _ = memfs.Mkdir("tmp/sub", 0o777) 35 _ = hackpadfs.WriteFullFile(memfs, "tmp/sub/bar", []byte("goodnight moon"), 0o777) 36 37 fsys := WrapWdFS(memfs).(*wdFS) 38 39 f, err := fsys.Open("/tmp/foo") 40 require.NoError(t, err) 41 42 b, err := io.ReadAll(f) 43 require.NoError(t, err) 44 assert.Equal(t, "hello world", string(b)) 45 46 fi, err := fs.Stat(fsys, "/tmp/sub/bar") 47 require.NoError(t, err) 48 assert.True(t, fi.Mode().IsRegular()) 49 50 b, err = fs.ReadFile(fsys, "/tmp/sub/bar") 51 require.NoError(t, err) 52 assert.Equal(t, "goodnight moon", string(b)) 53 54 des, err := fs.ReadDir(fsys, "/tmp") 55 require.NoError(t, err) 56 assert.Len(t, des, 5) 57 58 // note the relative path here, a requirement of fsys.Sub 59 subfs, err := fs.Sub(fsys, "tmp/sub") 60 require.NoError(t, err) 61 62 b, err = fs.ReadFile(subfs, "bar") 63 require.NoError(t, err) 64 assert.Equal(t, "goodnight moon", string(b)) 65 } 66 67 func TestWDFS_WriteOps(t *testing.T) { 68 // this test is backed by the real filesystem so we can test permissions 69 // and have some confidence it'll run on Windows 70 tmpDir := tfs.NewDir(t, "gomplate-wdfs-test") 71 tmpPath := tmpDir.Path() 72 vol := filepath.VolumeName(tmpPath) 73 if vol != "" { 74 tmpPath = tmpPath[len(vol):] 75 } else if tmpPath[0] == '/' { 76 vol = "/" 77 tmpPath = tmpPath[1:] 78 } 79 80 var osfsys fs.FS 81 var err error 82 if vol != "/" { 83 osfsys, err = osfs.NewFS().SubVolume(vol) 84 require.NoError(t, err) 85 } else { 86 osfsys = osfs.NewFS() 87 } 88 89 osfsys, err = hackpadfs.Sub(osfsys, tmpPath) 90 require.NoError(t, err) 91 92 fsys := &wdFS{ 93 vol: vol, 94 fsys: osfsys, 95 } 96 97 err = fsys.Mkdir("/tmp", 0o700) 98 require.NoError(t, err, "failed to create /tmp: %q", tmpDir.Path()) 99 100 // use os.Stat to make sure the directory was created in the right place 101 fi, err := os.Stat(filepath.Join(vol, tmpPath, "tmp")) 102 require.NoError(t, err) 103 assert.True(t, fi.Mode().IsDir()) 104 105 err = hackpadfs.WriteFullFile(fsys, "/tmp/foo", []byte("hello world"), 0o600) 106 require.NoError(t, err) 107 err = hackpadfs.WriteFullFile(fsys, "/tmp/one.txt", []byte("one"), 0o644) 108 require.NoError(t, err) 109 err = hackpadfs.WriteFullFile(fsys, "/tmp/two.txt", []byte("two"), 0o644) 110 require.NoError(t, err) 111 err = hackpadfs.WriteFullFile(fsys, "/tmp/three.txt", []byte("three"), 0o644) 112 require.NoError(t, err) 113 114 err = fsys.MkdirAll("/tmp/sub", 0o777) 115 require.NoError(t, err) 116 err = hackpadfs.WriteFullFile(fsys, "/tmp/sub/bar", []byte("goodnight moon"), 0o777) 117 require.NoError(t, err) 118 119 b, err := fs.ReadFile(fsys, "/tmp/foo") 120 require.NoError(t, err) 121 assert.Equal(t, "hello world", string(b)) 122 123 b, err = fs.ReadFile(fsys, "/tmp/sub/bar") 124 require.NoError(t, err) 125 assert.Equal(t, "goodnight moon", string(b)) 126 127 err = fsys.Chmod("/tmp/foo", 0o444) 128 require.NoError(t, err) 129 130 // check permissions 131 fi, err = fsys.Stat("/tmp/foo") 132 require.NoError(t, err) 133 assert.True(t, fi.Mode().IsRegular()) 134 assert.Equal(t, "0444", fmt.Sprintf("%#o", fi.Mode().Perm())) 135 136 // now delete it 137 err = fsys.Remove("/tmp/foo") 138 require.NoError(t, err) 139 140 // and check that it's gone 141 _, err = fsys.Stat("/tmp/foo") 142 require.ErrorIs(t, err, fs.ErrNotExist) 143 144 // make sure we can write to a subfs 145 subfs, err := fs.Sub(fsys, "tmp") 146 require.NoError(t, err) 147 require.NotNil(t, subfs) 148 149 // this is no longer a wdFS so we need to make sure not to use absolute 150 // paths - the path is relative to the root of the subfs 151 err = hackpadfs.WriteFullFile(subfs, "foo", []byte("hello world"), 0o600) 152 require.NoError(t, err) 153 154 b, err = fs.ReadFile(subfs, "foo") 155 require.NoError(t, err) 156 assert.Equal(t, "hello world", string(b)) 157 } 158 159 func skipWindows(t *testing.T) { 160 t.Helper() 161 if runtime.GOOS == "windows" { 162 t.Skip("skipping non-Windows test") 163 } 164 } 165 166 func skipNonWindows(t *testing.T) { 167 t.Helper() 168 if runtime.GOOS != "windows" { 169 t.Skip("skipping Windows test") 170 } 171 } 172 173 func TestResolveLocalPath_NonWindows(t *testing.T) { 174 skipWindows(t) 175 176 wd, _ := os.Getwd() 177 fsys := &wdFS{vol: "/", fsys: osfs.NewFS()} 178 179 wd = wd[1:] 180 181 testdata := []struct { 182 path string 183 expected string 184 }{ 185 {"/tmp/foo", "tmp/foo"}, 186 {"tmp/foo", wd + "/tmp/foo"}, 187 {"./tmp/foo", wd + "/tmp/foo"}, 188 {"tmp/../foo", wd + "/foo"}, 189 {"/", "."}, 190 } 191 192 for _, td := range testdata { 193 td := td 194 t.Run(td.path, func(t *testing.T) { 195 root, path, err := ResolveLocalPath(fsys, td.path) 196 require.NoError(t, err) 197 assert.Equal(t, "/", root) 198 assert.Equal(t, td.expected, path) 199 }) 200 } 201 } 202 203 func TestResolveLocalPath_Windows(t *testing.T) { 204 skipNonWindows(t) 205 206 wd, _ := os.Getwd() 207 volname := filepath.VolumeName(wd) 208 wd = filepath.ToSlash(wd) 209 210 fsys := &wdFS{vol: volname, fsys: osfs.NewFS()} 211 212 wd = wd[len(volname)+1:] 213 214 testdata := []struct { 215 path string 216 expRoot string 217 expected string 218 }{ 219 {"C:/tmp/foo", "C:", "tmp/foo"}, 220 {"D:\\tmp\\foo", "D:", "tmp/foo"}, 221 {"/tmp/foo", volname, "tmp/foo"}, 222 {"tmp/foo", volname, wd + "/tmp/foo"}, 223 {"./tmp/foo", volname, wd + "/tmp/foo"}, 224 {"tmp/../foo", volname, wd + "/foo"}, 225 } 226 227 for _, td := range testdata { 228 td := td 229 t.Run(td.path, func(t *testing.T) { 230 root, path, err := ResolveLocalPath(fsys, td.path) 231 require.NoError(t, err) 232 assert.Equal(t, td.expRoot, root) 233 assert.Equal(t, td.expected, path) 234 }) 235 } 236 } 237 238 func TestWdFS_ResolveLocalPath_NonWindows(t *testing.T) { 239 skipWindows(t) 240 241 wd, _ := os.Getwd() 242 wd = wd[1:] 243 244 testdata := []struct { 245 path string 246 expected string 247 }{ 248 {"/tmp/foo", "tmp/foo"}, 249 {"tmp/foo", wd + "/tmp/foo"}, 250 {"./tmp/foo", wd + "/tmp/foo"}, 251 {"tmp/../foo", wd + "/foo"}, 252 {"/", "."}, 253 } 254 255 for _, td := range testdata { 256 root, path, err := resolveLocalPath("/", td.path) 257 require.NoError(t, err) 258 assert.Equal(t, "/", root) 259 assert.Equal(t, td.expected, path) 260 } 261 } 262 263 func TestWdFS_ResolveLocalPath_Windows(t *testing.T) { 264 skipNonWindows(t) 265 266 wd, _ := os.Getwd() 267 volname := filepath.VolumeName(wd) 268 wd = filepath.ToSlash(wd) 269 wd = wd[len(volname)+1:] 270 271 testdata := []struct { 272 path string 273 expRoot string 274 expected string 275 }{ 276 {"C:/tmp/foo", "C:", "tmp/foo"}, 277 {`D:\tmp\foo`, "D:", "tmp/foo"}, 278 {"/tmp/foo", volname, "tmp/foo"}, 279 {"tmp/foo", volname, wd + "/tmp/foo"}, 280 {"./tmp/foo", volname, wd + "/tmp/foo"}, 281 {"tmp/../foo", volname, wd + "/foo"}, 282 {`\\?\C:\tmp\foo`, "C:", "tmp/foo"}, 283 {`\\somehost\share\foo\bar`, "//somehost/share", "foo/bar"}, 284 {`//?/C:/tmp/foo`, "C:", "tmp/foo"}, 285 {`//somehost/share/foo/bar`, "//somehost/share", "foo/bar"}, 286 } 287 288 for _, td := range testdata { 289 td := td 290 t.Run(td.path, func(t *testing.T) { 291 root, path, err := resolveLocalPath(volname, td.path) 292 require.NoError(t, err) 293 assert.Equal(t, td.expRoot, root) 294 assert.Equal(t, td.expected, path) 295 }) 296 } 297 } 298 299 func TestWin32PathType(t *testing.T) { 300 testdata := []struct { 301 path string 302 expected winPathtype 303 }{ 304 {"", winPathUnknown}, 305 {`\`, winPathRooted}, 306 {`\\`, winPathUncAbsolute}, 307 {`x`, winPathRelative}, 308 {`x:`, winPathDriveRelative}, 309 {"C:/tmp/foo", winPathDriveAbsolute}, 310 {`D:\tmp\foo`, winPathDriveAbsolute}, 311 {"/tmp/foo", winPathRooted}, 312 {"tmp/foo", winPathRelative}, 313 {"./tmp/foo", winPathRelative}, 314 {"tmp/../foo", winPathRelative}, 315 {`\\?\C:\tmp\foo`, winPathLocalDevice}, 316 {`\\somehost\share\foo\bar`, winPathUncAbsolute}, 317 {`//./C:/tmp/foo`, winPathLocalDevice}, 318 {`//./pipe/foo`, winPathLocalDevice}, 319 {`//./COM2`, winPathLocalDevice}, 320 {`\\.`, winPathRootLocalDevice}, 321 {`//?`, winPathRootLocalDevice}, 322 {`/??/C:/`, winPathNT}, 323 {`/??/UNC/server/foo`, winPathNT}, 324 } 325 326 for _, td := range testdata { 327 td := td 328 t.Run(td.path, func(t *testing.T) { 329 assert.Equal(t, td.expected, win32PathType(td.path)) 330 }) 331 } 332 }