| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- package dirfs
- import (
- "context"
- "errors"
- "io/fs"
- "net/http/httptest"
- "os"
- "path/filepath"
- "testing"
- "time"
- "github.com/google/go-cmp/cmp"
- "github.com/tailscale/xnet/webdav"
- "tailscale.com/drive/driveimpl/shared"
- "tailscale.com/tstest"
- )
- func TestStat(t *testing.T) {
- cfs, _, _, clock := createFileSystem(t)
- tests := []struct {
- label string
- name string
- expected fs.FileInfo
- err error
- }{
- {
- label: "root folder",
- name: "",
- expected: &shared.StaticFileInfo{
- Named: "",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- },
- {
- label: "static root folder",
- name: "/domain",
- expected: &shared.StaticFileInfo{
- Named: "domain",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- },
- {
- label: "remote1",
- name: "/domain/remote1",
- expected: &shared.StaticFileInfo{
- Named: "remote1",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- },
- {
- label: "remote2",
- name: "/domain/remote2",
- expected: &shared.StaticFileInfo{
- Named: "remote2",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- },
- {
- label: "non-existent remote",
- name: "remote3",
- err: os.ErrNotExist,
- },
- }
- ctx := context.Background()
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- fi, err := cfs.Stat(ctx, test.name)
- if test.err != nil {
- if !errors.Is(err, test.err) {
- t.Errorf("got %v, want %v", err, test.err)
- }
- } else {
- if err != nil {
- t.Errorf("unable to stat file: %v", err)
- } else {
- infosEqual(t, test.expected, fi)
- }
- }
- })
- }
- }
- func TestListDir(t *testing.T) {
- cfs, _, _, clock := createFileSystem(t)
- tests := []struct {
- label string
- name string
- expected []fs.FileInfo
- err error
- }{
- {
- label: "root folder",
- name: "",
- expected: []fs.FileInfo{
- &shared.StaticFileInfo{
- Named: "domain",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- },
- },
- {
- label: "static root folder",
- name: "/domain",
- expected: []fs.FileInfo{
- &shared.StaticFileInfo{
- Named: "remote1",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- &shared.StaticFileInfo{
- Named: "remote2",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- &shared.StaticFileInfo{
- Named: "remote4",
- Sized: 0,
- Moded: 0555,
- ModdedTime: clock.Now(),
- Dir: true,
- },
- },
- },
- }
- ctx := context.Background()
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- var infos []fs.FileInfo
- file, err := cfs.OpenFile(ctx, test.name, os.O_RDONLY, 0)
- if err == nil {
- defer file.Close()
- infos, err = file.Readdir(0)
- }
- if test.err != nil {
- if !errors.Is(err, test.err) {
- t.Errorf("got %v, want %v", err, test.err)
- }
- } else {
- if err != nil {
- t.Errorf("unable to stat file: %v", err)
- } else {
- if len(infos) != len(test.expected) {
- t.Errorf("wrong number of file infos, want %d, got %d", len(test.expected), len(infos))
- } else {
- for i, expected := range test.expected {
- infosEqual(t, expected, infos[i])
- }
- }
- }
- }
- })
- }
- }
- func TestMkdir(t *testing.T) {
- fs, _, _, _ := createFileSystem(t)
- tests := []struct {
- label string
- name string
- perm os.FileMode
- err error
- }{
- {
- label: "attempt to create root folder",
- name: "/",
- },
- {
- label: "attempt to create static root folder",
- name: "/domain",
- },
- {
- label: "attempt to create remote",
- name: "/domain/remote1",
- },
- {
- label: "attempt to create non-existent remote",
- name: "/domain/remote3",
- err: os.ErrPermission,
- },
- }
- ctx := context.Background()
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- err := fs.Mkdir(ctx, test.name, test.perm)
- if test.err != nil {
- if !errors.Is(err, test.err) {
- t.Errorf("got %v, want %v", err, test.err)
- }
- } else if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- })
- }
- }
- func TestRemoveAll(t *testing.T) {
- fs, _, _, _ := createFileSystem(t)
- tests := []struct {
- label string
- name string
- err error
- }{
- {
- label: "attempt to remove root folder",
- name: "/",
- err: os.ErrPermission,
- },
- }
- ctx := context.Background()
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- err := fs.RemoveAll(ctx, test.name)
- if !errors.Is(err, test.err) {
- t.Errorf("got %v, want %v", err, test.err)
- }
- })
- }
- }
- func TestRename(t *testing.T) {
- fs, _, _, _ := createFileSystem(t)
- tests := []struct {
- label string
- oldName string
- newName string
- err error
- }{
- {
- label: "attempt to move root folder",
- oldName: "/",
- newName: "/domain/remote2/copy.txt",
- err: os.ErrPermission,
- },
- }
- ctx := context.Background()
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- err := fs.Rename(ctx, test.oldName, test.newName)
- if !errors.Is(err, test.err) {
- t.Errorf("got %v, want: %v", err, test.err)
- }
- })
- }
- }
- func createFileSystem(t *testing.T) (webdav.FileSystem, string, string, *tstest.Clock) {
- s1, dir1 := startRemote(t)
- s2, dir2 := startRemote(t)
- // Make some files, use perms 0666 as lowest common denominator that works
- // on both UNIX and Windows.
- err := os.WriteFile(filepath.Join(dir1, "file1.txt"), []byte("12345"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- err = os.WriteFile(filepath.Join(dir2, "file2.txt"), []byte("54321"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- // make some directories
- err = os.Mkdir(filepath.Join(dir1, "dir1"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- err = os.Mkdir(filepath.Join(dir2, "dir2"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- clock := tstest.NewClock(tstest.ClockOpts{Start: time.Now()})
- fs := &FS{
- Clock: clock,
- StaticRoot: "domain",
- Children: []*Child{
- {Name: "remote1"},
- {Name: "remote2"},
- {Name: "remote4"},
- },
- }
- t.Cleanup(func() {
- defer s1.Close()
- defer os.RemoveAll(dir1)
- defer s2.Close()
- defer os.RemoveAll(dir2)
- })
- return fs, dir1, dir2, clock
- }
- func startRemote(t *testing.T) (*httptest.Server, string) {
- dir := t.TempDir()
- h := &webdav.Handler{
- FileSystem: webdav.Dir(dir),
- LockSystem: webdav.NewMemLS(),
- }
- s := httptest.NewServer(h)
- t.Cleanup(s.Close)
- return s, dir
- }
- func infosEqual(t *testing.T, expected, actual fs.FileInfo) {
- t.Helper()
- sfi, ok := actual.(*shared.StaticFileInfo)
- if ok {
- // zero out BirthedTime because we don't want to compare that
- sfi.BirthedTime = time.Time{}
- }
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("Wrong file info (-got, +want):\n%s", diff)
- }
- }
|