go-hep.org/x/hep@v0.38.1/xrootd/fshandler_test.go (about)

     1  // Copyright ©2018 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package xrootd_test // import "go-hep.org/x/hep/xrootd"
     6  
     7  import (
     8  	"context"
     9  	"crypto/rand"
    10  	"fmt"
    11  	"net"
    12  	"os"
    13  	"path"
    14  	"reflect"
    15  	"sync"
    16  	"testing"
    17  
    18  	"go-hep.org/x/hep/xrootd"
    19  	"go-hep.org/x/hep/xrootd/xrdfs"
    20  	"go-hep.org/x/hep/xrootd/xrdproto"
    21  	"go-hep.org/x/hep/xrootd/xrdproto/ping"
    22  )
    23  
    24  func getTCPAddr() (string, error) {
    25  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
    26  	if err != nil {
    27  		return "", err
    28  	}
    29  	l, err := net.ListenTCP("tcp", addr)
    30  	if err != nil {
    31  		return "", err
    32  	}
    33  	defer l.Close()
    34  	return l.Addr().String(), nil
    35  }
    36  
    37  func createServer(errorHandler func(err error)) (srv *xrootd.Server, addr, baseDir string, err error) {
    38  	baseDir, err = os.MkdirTemp("", "xrd-srv-")
    39  	if err != nil {
    40  		return nil, "", "", fmt.Errorf("xrd-srv: could not create test dir: %w", err)
    41  	}
    42  
    43  	addr, err = getTCPAddr()
    44  	if err != nil {
    45  		return nil, "", "", fmt.Errorf("xrd-srv: could not get free port to listen: %w", err)
    46  	}
    47  
    48  	listener, err := net.Listen("tcp", addr)
    49  	if err != nil {
    50  		return nil, "", "", fmt.Errorf("xrd-srv: could not listen on %q: %w", addr, err)
    51  	}
    52  
    53  	srv = xrootd.NewServer(xrootd.NewFSHandler(baseDir), func(err error) {
    54  		errorHandler(fmt.Errorf("xrd-srv: an error occured: %w", err))
    55  	})
    56  
    57  	go func() {
    58  		if err = srv.Serve(listener); err != nil && err != xrootd.ErrServerClosed {
    59  			errorHandler(fmt.Errorf("xrd-srv: could not serve: %w", err))
    60  		}
    61  	}()
    62  
    63  	return srv, addr, baseDir, nil
    64  }
    65  
    66  func createClient(addr string) (*xrootd.Client, error) {
    67  	return xrootd.NewClient(context.Background(), addr, "gopher")
    68  }
    69  
    70  func TestHandler_Dirlist(t *testing.T) {
    71  	srv, addr, baseDir, err := createServer(func(err error) {
    72  		t.Error(err)
    73  	})
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	defer os.RemoveAll(baseDir)
    78  	defer func() {
    79  		_ = srv.Shutdown(context.Background())
    80  	}()
    81  
    82  	file := path.Join(baseDir, "file1.txt")
    83  	err = os.WriteFile(file, nil, 0777)
    84  	if err != nil {
    85  		t.Fatalf("could not create test file: %v", err)
    86  	}
    87  
    88  	fileInfo, err := os.Stat(file)
    89  	if err != nil {
    90  		t.Fatalf("could not stat test file: %v", err)
    91  	}
    92  
    93  	cli, err := createClient(addr)
    94  	if err != nil {
    95  		t.Fatalf("could not create client: %v", err)
    96  	}
    97  	defer cli.Close()
    98  
    99  	got, err := cli.FS().Dirlist(context.Background(), "/")
   100  	if err != nil {
   101  		t.Fatalf("could not call Dirlist: %v", err)
   102  	}
   103  
   104  	want := []xrdfs.EntryStat{
   105  		{
   106  			EntryName:   "file1.txt",
   107  			HasStatInfo: true,
   108  			Flags:       xrdfs.StatIsWritable | xrdfs.StatIsReadable,
   109  			Mtime:       fileInfo.ModTime().Unix(),
   110  		},
   111  	}
   112  
   113  	if !reflect.DeepEqual(got, want) {
   114  		t.Fatalf("wrong Dirlist response:\ngot = %v\nwant = %v", got, want)
   115  	}
   116  }
   117  func TestHandler_Dirlist_WhenPathIsInvalid(t *testing.T) {
   118  	srv, addr, baseDir, err := createServer(func(err error) {
   119  		t.Error(err)
   120  	})
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	defer os.RemoveAll(baseDir)
   125  	defer func() {
   126  		_ = srv.Shutdown(context.Background())
   127  	}()
   128  
   129  	cli, err := createClient(addr)
   130  	if err != nil {
   131  		t.Fatalf("could not create client: %v", err)
   132  	}
   133  	defer cli.Close()
   134  
   135  	_, err = cli.FS().Dirlist(context.Background(), "/path/not/exist")
   136  	serverError, ok := err.(xrdproto.ServerError)
   137  	if !ok {
   138  		t.Fatalf("could not cast err to ServerError: %v", err)
   139  	}
   140  	if serverError.Code != xrdproto.IOError {
   141  		t.Fatalf("wrong error code:\ngot = %v\nwant = %v", serverError.Code, xrdproto.IOError)
   142  	}
   143  }
   144  
   145  func TestHandler_Dirlist_With1000Requests(t *testing.T) {
   146  	srv, addr, baseDir, err := createServer(func(err error) {
   147  		t.Error(err)
   148  	})
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  	defer os.RemoveAll(baseDir)
   153  	defer func() {
   154  		_ = srv.Shutdown(context.Background())
   155  	}()
   156  
   157  	file := path.Join(baseDir, "file1.txt")
   158  	err = os.WriteFile(file, nil, 0777)
   159  	if err != nil {
   160  		t.Fatalf("could not create test file: %v", err)
   161  	}
   162  
   163  	cli, err := createClient(addr)
   164  	if err != nil {
   165  		t.Fatalf("could not create client: %v", err)
   166  	}
   167  	defer cli.Close()
   168  
   169  	var wg sync.WaitGroup
   170  	wg.Add(1000)
   171  	for range 1000 {
   172  		go func() {
   173  			defer wg.Done()
   174  			_, err := cli.FS().Dirlist(context.Background(), "/")
   175  			if err != nil {
   176  				t.Errorf("could not call Dirlist: %v", err)
   177  			}
   178  		}()
   179  	}
   180  	wg.Wait()
   181  }
   182  
   183  func BenchmarkHandler_Dirlist(b *testing.B) {
   184  	srv, addr, baseDir, err := createServer(func(err error) {
   185  		b.Error(err)
   186  	})
   187  	if err != nil {
   188  		b.Fatal(err)
   189  	}
   190  	defer os.RemoveAll(baseDir)
   191  	defer func() {
   192  		_ = srv.Shutdown(context.Background())
   193  	}()
   194  
   195  	file := path.Join(baseDir, "file1.txt")
   196  	err = os.WriteFile(file, nil, 0777)
   197  	if err != nil {
   198  		b.Fatalf("could not create test file: %v", err)
   199  	}
   200  
   201  	cli, err := createClient(addr)
   202  	if err != nil {
   203  		b.Fatalf("could not create client: %v", err)
   204  	}
   205  
   206  	defer cli.Close()
   207  	b.ResetTimer()
   208  	for n := 0; n < b.N; n++ {
   209  		_, err = cli.FS().Dirlist(context.Background(), "/")
   210  		if err != nil {
   211  			b.Fatalf("could not call Dirlist: %v", err)
   212  		}
   213  	}
   214  }
   215  
   216  func TestHandler_Open(t *testing.T) {
   217  	for _, tc := range []struct {
   218  		testName      string
   219  		file          string
   220  		mode          xrdfs.OpenMode
   221  		options       xrdfs.OpenOptions
   222  		createFile    bool
   223  		errCode       xrdproto.ServerErrorCode
   224  		checkStatInfo bool
   225  	}{
   226  		{
   227  			testName:   "Readonly | created file",
   228  			options:    xrdfs.OpenOptionsOpenRead,
   229  			createFile: true,
   230  			file:       "test1.txt",
   231  		},
   232  		{
   233  			testName:   "Read & write | created file",
   234  			options:    xrdfs.OpenOptionsOpenUpdate,
   235  			createFile: true,
   236  			file:       "test1.txt",
   237  		},
   238  		{
   239  			testName:   "Append | created file",
   240  			options:    xrdfs.OpenOptionsOpenAppend,
   241  			createFile: true,
   242  			file:       "test1.txt",
   243  		},
   244  		{
   245  			testName: "Read & Write | new file",
   246  			options:  xrdfs.OpenOptionsOpenUpdate | xrdfs.OpenOptionsNew,
   247  			file:     "test1.txt",
   248  		},
   249  		{
   250  			testName:   "Read & Write | create existing file",
   251  			options:    xrdfs.OpenOptionsOpenUpdate | xrdfs.OpenOptionsNew,
   252  			createFile: true,
   253  			errCode:    xrdproto.IOError,
   254  			file:       "test1.txt",
   255  		},
   256  		{
   257  			testName:   "Read & Write | recreate file",
   258  			options:    xrdfs.OpenOptionsOpenUpdate | xrdfs.OpenOptionsDelete,
   259  			createFile: true,
   260  			file:       "test1.txt",
   261  		},
   262  		{
   263  			testName: "Read & Write | new file in new directory without OpenOptionsMkPath",
   264  			options:  xrdfs.OpenOptionsOpenUpdate | xrdfs.OpenOptionsNew,
   265  			file:     path.Join("testdir", "test1.txt"),
   266  			errCode:  xrdproto.IOError,
   267  		},
   268  		{
   269  			testName: "Read & Write | new file in new directory with OpenOptionsMkPath",
   270  			options:  xrdfs.OpenOptionsOpenUpdate | xrdfs.OpenOptionsNew | xrdfs.OpenOptionsMkPath,
   271  			file:     path.Join("testdir", "test1.txt"),
   272  			mode:     xrdfs.OpenModeOwnerRead | xrdfs.OpenModeOwnerWrite | xrdfs.OpenModeOwnerExecute,
   273  		},
   274  		{
   275  			testName:      "Read & Write | created file | with stat info",
   276  			options:       xrdfs.OpenOptionsOpenUpdate | xrdfs.OpenOptionsReturnStatus,
   277  			file:          "test1.txt",
   278  			createFile:    true,
   279  			checkStatInfo: true,
   280  		},
   281  	} {
   282  		t.Run(tc.testName, func(t *testing.T) {
   283  			srv, addr, baseDir, err := createServer(func(err error) {
   284  				t.Error(err)
   285  			})
   286  			if err != nil {
   287  				t.Fatal(err)
   288  			}
   289  			defer os.RemoveAll(baseDir)
   290  			defer func() {
   291  				_ = srv.Shutdown(context.Background())
   292  			}()
   293  
   294  			if tc.createFile {
   295  				err = os.WriteFile(path.Join(baseDir, tc.file), nil, 0777)
   296  				if err != nil {
   297  					t.Fatalf("could not create test file: %v", err)
   298  				}
   299  			}
   300  
   301  			cli, err := createClient(addr)
   302  			if err != nil {
   303  				t.Fatalf("could not create client: %v", err)
   304  			}
   305  			defer cli.Close()
   306  
   307  			got, err := cli.FS().Open(context.Background(), tc.file, tc.mode, tc.options)
   308  			if err != nil {
   309  				if serverError, ok := err.(xrdproto.ServerError); ok {
   310  					if serverError.Code != tc.errCode {
   311  						t.Fatalf("wrong error code:\ngot = %v\nwant = %v\nerror message = %q", serverError.Code, tc.errCode, serverError.Message)
   312  					}
   313  					return
   314  				}
   315  				t.Fatalf("could not call Open: %v", err)
   316  			}
   317  			if err == nil && tc.errCode != 0 {
   318  				t.Fatalf("unexpected successfull call\nwant error code = %v", tc.errCode)
   319  			}
   320  
   321  			if tc.checkStatInfo {
   322  				st, err := os.Stat(path.Join(baseDir, tc.file))
   323  				if err != nil {
   324  					t.Fatalf("could not read stat info: %v", err)
   325  				}
   326  				want := xrdfs.EntryStatFrom(st)
   327  				want.EntryName = ""
   328  				if !reflect.DeepEqual(*got.Info(), want) {
   329  					t.Fatalf("wrong stat:\ngot = %v\nwant = %v", *got.Info(), want)
   330  				}
   331  			}
   332  
   333  			err = got.Close(context.Background())
   334  			if err != nil {
   335  				t.Fatalf("could not call Close: %v", err)
   336  			}
   337  		})
   338  	}
   339  }
   340  
   341  func TestHandler_Read(t *testing.T) {
   342  	bigData := make([]byte, 10*1024)
   343  	_, err := rand.Read(bigData)
   344  	if err != nil {
   345  		t.Fatalf("could not prepare test data: %v", err)
   346  	}
   347  
   348  	for _, tc := range []struct {
   349  		testName string
   350  		data     []byte
   351  		want     []byte
   352  		offset   int64
   353  		length   int
   354  	}{
   355  		{
   356  			testName: "Without offset",
   357  			data:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   358  			length:   6,
   359  			want:     []byte{1, 2, 3, 4, 5, 6},
   360  		},
   361  		{
   362  			testName: "With offset",
   363  			data:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   364  			length:   6,
   365  			offset:   1,
   366  			want:     []byte{2, 3, 4, 5, 6, 7},
   367  		},
   368  		{
   369  			testName: "With offset with EOF",
   370  			data:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   371  			length:   20,
   372  			offset:   1,
   373  			want:     []byte{2, 3, 4, 5, 6, 7, 8},
   374  		},
   375  		{
   376  			testName: "With offset larger than file size",
   377  			data:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   378  			length:   20,
   379  			offset:   40,
   380  			want:     []byte{},
   381  		},
   382  		{
   383  			testName: "With big length",
   384  			data:     bigData,
   385  			length:   len(bigData),
   386  			offset:   40,
   387  			want:     bigData[40:],
   388  		},
   389  	} {
   390  		t.Run(tc.testName, func(t *testing.T) {
   391  			srv, addr, baseDir, err := createServer(func(err error) {
   392  				t.Error(err)
   393  			})
   394  			if err != nil {
   395  				t.Fatal(err)
   396  			}
   397  			defer os.RemoveAll(baseDir)
   398  			defer func() {
   399  				_ = srv.Shutdown(context.Background())
   400  			}()
   401  
   402  			file := path.Join(baseDir, "file1.txt")
   403  
   404  			err = os.WriteFile(file, tc.data, 0777)
   405  			if err != nil {
   406  				t.Fatalf("could not create test file: %v", err)
   407  			}
   408  
   409  			cli, err := createClient(addr)
   410  			if err != nil {
   411  				t.Fatalf("could not create client: %v", err)
   412  			}
   413  			defer cli.Close()
   414  
   415  			gotFile, err := cli.FS().Open(context.Background(), "file1.txt", xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead)
   416  			if err != nil {
   417  				t.Fatalf("could not call Open: %v", err)
   418  			}
   419  			defer gotFile.Close(context.Background())
   420  
   421  			got := make([]byte, tc.length)
   422  			_, err = gotFile.ReadAt(got, tc.offset)
   423  
   424  			if err != nil {
   425  				t.Fatalf("could not call ReadAt: %v", err)
   426  			}
   427  
   428  			if !reflect.DeepEqual(got[:len(tc.want)], tc.want) {
   429  				t.Fatalf("wrong data:\ngot = %v\nwant = %v", got[:len(tc.want)], tc.want)
   430  			}
   431  		})
   432  	}
   433  }
   434  
   435  func TestHandler_Write(t *testing.T) {
   436  	bigData := make([]byte, 10*1024)
   437  	_, err := rand.Read(bigData)
   438  	if err != nil {
   439  		t.Fatalf("could not prepare test data: %v", err)
   440  	}
   441  
   442  	for _, tc := range []struct {
   443  		testName    string
   444  		initialData []byte
   445  		data        []byte
   446  		want        []byte
   447  		n           int
   448  		offset      int64
   449  	}{
   450  		{
   451  			testName: "Without offset",
   452  			data:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   453  			want:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   454  			n:        8,
   455  		},
   456  		{
   457  			testName:    "Without offset, with partial rewrite",
   458  			initialData: []byte{1, 2, 3, 4, 5, 6, 7, 8},
   459  			data:        []byte{9, 8, 7, 6, 0},
   460  			want:        []byte{9, 8, 7, 6, 0, 6, 7, 8},
   461  			n:           5,
   462  		},
   463  		{
   464  			testName:    "With offset, with partial rewrite",
   465  			initialData: []byte{1, 2, 3, 4, 5, 6, 7, 8},
   466  			data:        []byte{1, 2, 3, 4, 5, 6, 7, 8},
   467  			offset:      1,
   468  			want:        []byte{1, 1, 2, 3, 4, 5, 6, 7, 8},
   469  			n:           8,
   470  		},
   471  		{
   472  			testName: "With offset larger than file size",
   473  			data:     []byte{1, 2, 3, 4, 5, 6, 7, 8},
   474  			offset:   2,
   475  			want:     []byte{0, 0, 1, 2, 3, 4, 5, 6, 7, 8},
   476  			n:        8,
   477  		},
   478  		{
   479  			testName: "With big length",
   480  			data:     bigData,
   481  			offset:   0,
   482  			want:     bigData,
   483  			n:        len(bigData),
   484  		},
   485  	} {
   486  		t.Run(tc.testName, func(t *testing.T) {
   487  			srv, addr, baseDir, err := createServer(func(err error) {
   488  				t.Error(err)
   489  			})
   490  			if err != nil {
   491  				t.Fatal(err)
   492  			}
   493  			defer os.RemoveAll(baseDir)
   494  			defer func() {
   495  				_ = srv.Shutdown(context.Background())
   496  			}()
   497  
   498  			file := path.Join(baseDir, "file1.txt")
   499  
   500  			err = os.WriteFile(file, tc.initialData, 0777)
   501  			if err != nil {
   502  				t.Fatalf("could not create test file: %v", err)
   503  			}
   504  
   505  			cli, err := createClient(addr)
   506  			if err != nil {
   507  				t.Fatalf("could not create client: %v", err)
   508  			}
   509  			defer cli.Close()
   510  
   511  			gotFile, err := cli.FS().Open(context.Background(), "file1.txt", xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsOpenUpdate)
   512  			if err != nil {
   513  				t.Fatalf("could not call Open: %v", err)
   514  			}
   515  			defer gotFile.Close(context.Background())
   516  
   517  			n, err := gotFile.WriteAt(tc.data, tc.offset)
   518  			if err != nil {
   519  				t.Fatalf("could not call WriteAt: %v", err)
   520  			}
   521  
   522  			if n != tc.n {
   523  				t.Fatalf("wrong length:\ngot = %v\nwant = %v", n, tc.n)
   524  			}
   525  
   526  			if err := gotFile.Sync(context.Background()); err != nil {
   527  				t.Fatalf("could not call Sync: %v", err)
   528  			}
   529  
   530  			got, err := os.ReadFile(file)
   531  			if err != nil {
   532  				t.Fatalf("could not read written data: %v", err)
   533  			}
   534  
   535  			if !reflect.DeepEqual(got, tc.want) {
   536  				t.Fatalf("wrong data:\ngot = %v\nwant = %v", got, tc.want)
   537  			}
   538  		})
   539  	}
   540  }
   541  
   542  func TestHandler_Stat(t *testing.T) {
   543  	for _, tc := range []struct {
   544  		testName string
   545  		isFile   bool
   546  		withPath bool
   547  	}{
   548  		{
   549  			testName: "Stat of file by file handle",
   550  			isFile:   true,
   551  			withPath: false,
   552  		},
   553  		{
   554  			testName: "Stat of file by path",
   555  			isFile:   true,
   556  			withPath: true,
   557  		},
   558  		{
   559  			testName: "Stat of directory by path",
   560  			isFile:   false,
   561  			withPath: true,
   562  		},
   563  	} {
   564  		t.Run(tc.testName, func(t *testing.T) {
   565  			srv, addr, baseDir, err := createServer(func(err error) {
   566  				t.Error(err)
   567  			})
   568  			if err != nil {
   569  				t.Fatal(err)
   570  			}
   571  			defer os.RemoveAll(baseDir)
   572  			defer func() {
   573  				_ = srv.Shutdown(context.Background())
   574  			}()
   575  
   576  			var entry string
   577  			if tc.isFile {
   578  				entry = "file1.txt"
   579  				err := os.WriteFile(path.Join(baseDir, entry), []byte{1, 2, 3, 4, 5}, 0777)
   580  				if err != nil {
   581  					t.Fatalf("could not create test file: %v", err)
   582  				}
   583  			} else {
   584  				entry = "dir1"
   585  				err := os.MkdirAll(path.Join(baseDir, entry), 0777)
   586  				if err != nil {
   587  					t.Fatalf("could not create test directory: %v", err)
   588  				}
   589  			}
   590  
   591  			stat, err := os.Stat(path.Join(baseDir, entry))
   592  			if err != nil {
   593  				t.Fatalf("could not read stat info: %v", err)
   594  			}
   595  
   596  			want := xrdfs.EntryStatFrom(stat)
   597  			want.EntryName = ""
   598  
   599  			cli, err := createClient(addr)
   600  			if err != nil {
   601  				t.Fatalf("could not create client: %v", err)
   602  			}
   603  			defer cli.Close()
   604  
   605  			var got xrdfs.EntryStat
   606  			if tc.withPath {
   607  				got, err = cli.FS().Stat(context.Background(), entry)
   608  				if err != nil {
   609  					t.Fatalf("could not call Stat: %v", err)
   610  				}
   611  			} else {
   612  				file, err := cli.FS().Open(context.Background(), entry, xrdfs.OpenModeOwnerRead, xrdfs.OpenOptionsOpenRead)
   613  				if err != nil {
   614  					t.Fatalf("could not call Open: %v", err)
   615  				}
   616  				got, err = file.Stat(context.Background())
   617  				if err != nil {
   618  					t.Fatalf("could not call Stat: %v", err)
   619  				}
   620  				if err := file.Close(context.Background()); err != nil {
   621  					t.Fatalf("could not call Close: %v", err)
   622  				}
   623  			}
   624  
   625  			if !reflect.DeepEqual(got, want) {
   626  				t.Fatalf("wrong stat:\ngot = %v\nwant = %v", got, want)
   627  			}
   628  		})
   629  	}
   630  }
   631  
   632  func TestHandler_Truncate(t *testing.T) {
   633  	for _, tc := range []struct {
   634  		testName string
   635  		data     []byte
   636  		want     []byte
   637  		size     int64
   638  		withPath bool
   639  	}{
   640  		{
   641  			testName: "Truncate file by file handle",
   642  			data:     []byte{1, 2, 3, 4, 5, 6},
   643  			want:     []byte{1, 2, 3},
   644  			size:     3,
   645  			withPath: false,
   646  		},
   647  		{
   648  			testName: "Truncate file by path",
   649  			data:     []byte{1, 2, 3, 4, 5, 6},
   650  			want:     []byte{1, 2, 3},
   651  			size:     3,
   652  			withPath: true,
   653  		},
   654  		{
   655  			testName: "Extend file by file handle",
   656  			data:     []byte{1, 2, 3, 4, 5, 6},
   657  			want:     []byte{1, 2, 3, 4, 5, 6, 0, 0},
   658  			size:     8,
   659  			withPath: false,
   660  		},
   661  		{
   662  			testName: "Extend file by path",
   663  			data:     []byte{1, 2, 3, 4, 5, 6},
   664  			want:     []byte{1, 2, 3, 4, 5, 6, 0, 0},
   665  			size:     8,
   666  			withPath: true,
   667  		},
   668  	} {
   669  		t.Run(tc.testName, func(t *testing.T) {
   670  			srv, addr, baseDir, err := createServer(func(err error) {
   671  				t.Error(err)
   672  			})
   673  			if err != nil {
   674  				t.Fatal(err)
   675  			}
   676  			defer os.RemoveAll(baseDir)
   677  			defer func() {
   678  				_ = srv.Shutdown(context.Background())
   679  			}()
   680  
   681  			entry := "file1.txt"
   682  			err = os.WriteFile(path.Join(baseDir, entry), tc.data, 0777)
   683  			if err != nil {
   684  				t.Fatalf("could not create test file: %v", err)
   685  			}
   686  
   687  			cli, err := createClient(addr)
   688  			if err != nil {
   689  				t.Fatalf("could not create client: %v", err)
   690  			}
   691  			defer cli.Close()
   692  
   693  			if tc.withPath {
   694  				err = cli.FS().Truncate(context.Background(), entry, tc.size)
   695  				if err != nil {
   696  					t.Fatalf("could not call Truncate: %v", err)
   697  				}
   698  			} else {
   699  				file, err := cli.FS().Open(context.Background(), entry, xrdfs.OpenModeOwnerWrite, xrdfs.OpenOptionsOpenUpdate)
   700  				if err != nil {
   701  					t.Fatalf("could not call Open: %v", err)
   702  				}
   703  				if err = file.Truncate(context.Background(), tc.size); err != nil {
   704  					t.Fatalf("could not call Truncate: %v", err)
   705  				}
   706  				if err := file.Sync(context.Background()); err != nil {
   707  					t.Fatalf("could not call Sync: %v", err)
   708  				}
   709  				if err := file.Close(context.Background()); err != nil {
   710  					t.Fatalf("could not call Close: %v", err)
   711  				}
   712  			}
   713  
   714  			s, err := os.Stat(path.Join(baseDir, entry))
   715  			if err != nil {
   716  				t.Fatalf("could not read stat info: %v", err)
   717  			}
   718  
   719  			if !reflect.DeepEqual(s.Size(), tc.size) {
   720  				t.Fatalf("wrong size:\ngot = %v\nwant = %v", s.Size(), tc.size)
   721  			}
   722  		})
   723  	}
   724  }
   725  
   726  func TestHandler_Rename(t *testing.T) {
   727  	for _, tc := range []struct {
   728  		testName     string
   729  		oldPathExist bool
   730  		newPathExist bool
   731  	}{
   732  		{
   733  			testName:     "Old path exists, new path doesn't exist",
   734  			oldPathExist: true,
   735  			newPathExist: false,
   736  		},
   737  		{
   738  			testName:     "Old path exists, new path exists",
   739  			oldPathExist: true,
   740  			newPathExist: true,
   741  		},
   742  		{
   743  			testName:     "Old path doesn't exist, new path doesn't exist",
   744  			oldPathExist: true,
   745  			newPathExist: false,
   746  		},
   747  	} {
   748  		t.Run(tc.testName, func(t *testing.T) {
   749  			srv, addr, baseDir, err := createServer(func(err error) {
   750  				t.Error(err)
   751  			})
   752  			if err != nil {
   753  				t.Fatal(err)
   754  			}
   755  			defer os.RemoveAll(baseDir)
   756  			defer func() {
   757  				_ = srv.Shutdown(context.Background())
   758  			}()
   759  
   760  			oldName := "old.txt"
   761  			newName := "new.txt"
   762  
   763  			if tc.oldPathExist {
   764  				if err := os.WriteFile(path.Join(baseDir, oldName), nil, 0777); err != nil {
   765  					t.Fatalf("could not create test file: %v", err)
   766  				}
   767  			}
   768  			if tc.newPathExist {
   769  				if err := os.WriteFile(path.Join(baseDir, newName), nil, 0777); err != nil {
   770  					t.Fatalf("could not create test file: %v", err)
   771  				}
   772  			}
   773  
   774  			cli, err := createClient(addr)
   775  			if err != nil {
   776  				t.Fatalf("could not create client: %v", err)
   777  			}
   778  			defer cli.Close()
   779  
   780  			if err := cli.FS().Rename(context.Background(), oldName, newName); err != nil {
   781  				t.Fatalf("could not call Rename: %v", err)
   782  			}
   783  
   784  			if _, err := os.Stat(path.Join(baseDir, oldName)); !os.IsNotExist(err) {
   785  				t.Fatalf("old file was not removed after rename")
   786  			}
   787  			if _, err := os.Stat(path.Join(baseDir, newName)); os.IsNotExist(err) {
   788  				t.Fatalf("new file was not created after rename")
   789  			}
   790  		})
   791  	}
   792  }
   793  
   794  func TestHandler_Mkdir(t *testing.T) {
   795  	for _, tc := range []struct {
   796  		testName   string
   797  		path       string
   798  		createFile bool
   799  		mkdirAll   bool
   800  		errCode    xrdproto.ServerErrorCode
   801  	}{
   802  		{
   803  			testName:   "existing dir",
   804  			createFile: true,
   805  			path:       "testdir",
   806  			errCode:    xrdproto.IOError,
   807  		},
   808  		{
   809  			testName:   "new dir",
   810  			createFile: false,
   811  			path:       "testdir",
   812  		},
   813  		{
   814  			testName:   "nested dir",
   815  			createFile: false,
   816  			path:       "nested/testdir",
   817  			errCode:    xrdproto.IOError,
   818  		},
   819  		{
   820  			testName:   "nested dir and MkdirAll",
   821  			createFile: false,
   822  			path:       "nested/testdir",
   823  			mkdirAll:   true,
   824  		},
   825  	} {
   826  		t.Run(tc.testName, func(t *testing.T) {
   827  			srv, addr, baseDir, err := createServer(func(err error) {
   828  				t.Error(err)
   829  			})
   830  			if err != nil {
   831  				t.Fatal(err)
   832  			}
   833  			defer os.RemoveAll(baseDir)
   834  			defer func() {
   835  				_ = srv.Shutdown(context.Background())
   836  			}()
   837  
   838  			if tc.createFile {
   839  				err = os.MkdirAll(path.Join(baseDir, tc.path), os.FileMode(0777))
   840  				if err != nil {
   841  					t.Fatalf("could not create test dir: %v", err)
   842  				}
   843  			}
   844  
   845  			cli, err := createClient(addr)
   846  			if err != nil {
   847  				t.Fatalf("could not create client: %v", err)
   848  			}
   849  			defer cli.Close()
   850  
   851  			if tc.mkdirAll {
   852  				err = cli.FS().MkdirAll(context.Background(), tc.path, xrdfs.OpenModeOwnerRead|xrdfs.OpenModeOwnerWrite|xrdfs.OpenModeOwnerExecute)
   853  			} else {
   854  				err = cli.FS().Mkdir(context.Background(), tc.path, xrdfs.OpenModeOwnerRead|xrdfs.OpenModeOwnerWrite|xrdfs.OpenModeOwnerExecute)
   855  			}
   856  			if err != nil {
   857  				if serverError, ok := err.(xrdproto.ServerError); ok {
   858  					if serverError.Code != tc.errCode {
   859  						t.Fatalf("wrong error code:\ngot = %v\nwant = %v\nerror message = %q", serverError.Code, tc.errCode, serverError.Message)
   860  					}
   861  					return
   862  				}
   863  				t.Fatalf("could not call Mkdir: %v", err)
   864  			}
   865  			if err == nil && tc.errCode != 0 {
   866  				t.Fatalf("unexpected successfull call\nwant error code = %v", tc.errCode)
   867  			}
   868  		})
   869  	}
   870  }
   871  
   872  func TestHandler_Remove(t *testing.T) {
   873  	for _, tc := range []struct {
   874  		testName   string
   875  		path       string
   876  		createFile bool
   877  		errCode    xrdproto.ServerErrorCode
   878  	}{
   879  		{
   880  			testName:   "existing file",
   881  			createFile: true,
   882  			path:       "testfile",
   883  		},
   884  		{
   885  			testName:   "non-existing file",
   886  			createFile: false,
   887  			path:       "testfile",
   888  			errCode:    xrdproto.IOError,
   889  		},
   890  	} {
   891  		t.Run(tc.testName, func(t *testing.T) {
   892  			srv, addr, baseDir, err := createServer(func(err error) {
   893  				t.Error(err)
   894  			})
   895  			if err != nil {
   896  				t.Fatal(err)
   897  			}
   898  			defer os.RemoveAll(baseDir)
   899  			defer func() {
   900  				_ = srv.Shutdown(context.Background())
   901  			}()
   902  
   903  			if tc.createFile {
   904  				f, err := os.Create(path.Join(baseDir, tc.path))
   905  				if err != nil {
   906  					t.Fatalf("could not create test file: %v", err)
   907  				}
   908  				err = f.Close()
   909  				if err != nil {
   910  					t.Fatalf("could not close test file: %v", err)
   911  				}
   912  			}
   913  
   914  			cli, err := createClient(addr)
   915  			if err != nil {
   916  				t.Fatalf("could not create client: %v", err)
   917  			}
   918  			defer cli.Close()
   919  
   920  			err = cli.FS().RemoveFile(context.Background(), tc.path)
   921  			if err != nil {
   922  				if serverError, ok := err.(xrdproto.ServerError); ok {
   923  					if serverError.Code != tc.errCode {
   924  						t.Fatalf("wrong error code:\ngot = %v\nwant = %v\nerror message = %q", serverError.Code, tc.errCode, serverError.Message)
   925  					}
   926  					return
   927  				}
   928  				t.Fatalf("could not call RemoveFile: %v", err)
   929  			}
   930  			if err == nil && tc.errCode != 0 {
   931  				t.Fatalf("unexpected successfull call\nwant error code = %v", tc.errCode)
   932  			}
   933  		})
   934  	}
   935  }
   936  
   937  func TestHandler_RemoveDir(t *testing.T) {
   938  	for _, tc := range []struct {
   939  		testName   string
   940  		path       string
   941  		createFile bool
   942  		createDir  bool
   943  		errCode    xrdproto.ServerErrorCode
   944  	}{
   945  		{
   946  			testName:   "empty existing dir",
   947  			createFile: false,
   948  			createDir:  true,
   949  			path:       "testdir",
   950  		},
   951  		{
   952  			testName:   "non-existing dir",
   953  			createFile: false,
   954  			createDir:  false,
   955  			path:       "testdir",
   956  			errCode:    xrdproto.IOError,
   957  		},
   958  		{
   959  			testName:   "non-empty existing dir",
   960  			createFile: true,
   961  			createDir:  true,
   962  			path:       "testdir",
   963  			errCode:    xrdproto.IOError,
   964  		},
   965  	} {
   966  		t.Run(tc.testName, func(t *testing.T) {
   967  			srv, addr, baseDir, err := createServer(func(err error) {
   968  				t.Error(err)
   969  			})
   970  			if err != nil {
   971  				t.Fatal(err)
   972  			}
   973  			defer os.RemoveAll(baseDir)
   974  			defer func() {
   975  				_ = srv.Shutdown(context.Background())
   976  			}()
   977  
   978  			dirPath := path.Join(baseDir, tc.path)
   979  			if tc.createDir {
   980  				err := os.MkdirAll(dirPath, os.FileMode(0777))
   981  				if err != nil {
   982  					t.Fatalf("could not create test dir: %v", err)
   983  				}
   984  			}
   985  
   986  			if tc.createFile {
   987  				f, err := os.Create(path.Join(dirPath, "file.txt"))
   988  				if err != nil {
   989  					t.Fatalf("could not create test file: %v", err)
   990  				}
   991  				err = f.Close()
   992  				if err != nil {
   993  					t.Fatalf("could not close test file: %v", err)
   994  				}
   995  			}
   996  
   997  			cli, err := createClient(addr)
   998  			if err != nil {
   999  				t.Fatalf("could not create client: %v", err)
  1000  			}
  1001  			defer cli.Close()
  1002  
  1003  			err = cli.FS().RemoveDir(context.Background(), tc.path)
  1004  			if err != nil {
  1005  				if serverError, ok := err.(xrdproto.ServerError); ok {
  1006  					if serverError.Code != tc.errCode {
  1007  						t.Fatalf("wrong error code:\ngot = %v\nwant = %v\nerror message = %q", serverError.Code, tc.errCode, serverError.Message)
  1008  					}
  1009  					return
  1010  				}
  1011  				t.Fatalf("could not call RemoveDir: %v", err)
  1012  			}
  1013  			if err == nil && tc.errCode != 0 {
  1014  				t.Fatalf("unexpected successfull call\nwant error code = %v", tc.errCode)
  1015  			}
  1016  		})
  1017  	}
  1018  }
  1019  
  1020  func TestHandler_Ping(t *testing.T) {
  1021  	srv, addr, baseDir, err := createServer(func(err error) {
  1022  		t.Error(err)
  1023  	})
  1024  	if err != nil {
  1025  		t.Fatal(err)
  1026  	}
  1027  	defer os.RemoveAll(baseDir)
  1028  	defer func() {
  1029  		_ = srv.Shutdown(context.Background())
  1030  	}()
  1031  
  1032  	cli, err := createClient(addr)
  1033  	if err != nil {
  1034  		t.Fatalf("could not create client: %v", err)
  1035  	}
  1036  	defer cli.Close()
  1037  
  1038  	_, err = cli.Send(context.Background(), nil, &ping.Request{})
  1039  	if err != nil {
  1040  		t.Fatalf("could not call Ping: %v", err)
  1041  	}
  1042  }