| package sftp |
| |
| import ( |
| "sync" |
| |
| "github.com/stretchr/testify/assert" |
| |
| "bytes" |
| "errors" |
| "io" |
| "os" |
| "testing" |
| ) |
| |
| type testHandler struct { |
| filecontents []byte // dummy contents |
| output io.WriterAt // dummy file out |
| err error // dummy error, should be file related |
| } |
| |
| func (t *testHandler) Fileread(r *Request) (io.ReaderAt, error) { |
| if t.err != nil { |
| return nil, t.err |
| } |
| _ = r.WithContext(r.Context()) // initialize context for deadlock testing |
| return bytes.NewReader(t.filecontents), nil |
| } |
| |
| func (t *testHandler) Filewrite(r *Request) (io.WriterAt, error) { |
| if t.err != nil { |
| return nil, t.err |
| } |
| _ = r.WithContext(r.Context()) // initialize context for deadlock testing |
| return io.WriterAt(t.output), nil |
| } |
| |
| func (t *testHandler) Filecmd(r *Request) error { |
| _ = r.WithContext(r.Context()) // initialize context for deadlock testing |
| return t.err |
| } |
| |
| func (t *testHandler) Filelist(r *Request) (ListerAt, error) { |
| if t.err != nil { |
| return nil, t.err |
| } |
| _ = r.WithContext(r.Context()) // initialize context for deadlock testing |
| f, err := os.Open(r.Filepath) |
| if err != nil { |
| return nil, err |
| } |
| fi, err := f.Stat() |
| if err != nil { |
| return nil, err |
| } |
| return listerat([]os.FileInfo{fi}), nil |
| } |
| |
| // make sure len(fakefile) == len(filecontents) |
| type fakefile [10]byte |
| |
| var filecontents = []byte("file-data.") |
| |
| // XXX need new for creating test requests that supports Open-ing |
| func testRequest(method string) *Request { |
| var flags uint32 |
| switch method { |
| case "Get": |
| flags = flags | sshFxfRead |
| case "Put": |
| flags = flags | sshFxfWrite |
| } |
| request := &Request{ |
| Filepath: "./request_test.go", |
| Method: method, |
| Attrs: []byte("foo"), |
| Flags: flags, |
| Target: "foo", |
| state: state{RWMutex: new(sync.RWMutex)}, |
| } |
| return request |
| } |
| |
| func (ff *fakefile) WriteAt(p []byte, off int64) (int, error) { |
| n := copy(ff[off:], p) |
| return n, nil |
| } |
| |
| func (ff fakefile) string() string { |
| b := make([]byte, len(ff)) |
| copy(b, ff[:]) |
| return string(b) |
| } |
| |
| func newTestHandlers() Handlers { |
| handler := &testHandler{ |
| filecontents: filecontents, |
| output: &fakefile{}, |
| err: nil, |
| } |
| return Handlers{ |
| FileGet: handler, |
| FilePut: handler, |
| FileCmd: handler, |
| FileList: handler, |
| } |
| } |
| |
| func (h Handlers) getOutString() string { |
| handler := h.FilePut.(*testHandler) |
| return handler.output.(*fakefile).string() |
| } |
| |
| var errTest = errors.New("test error") |
| |
| func (h *Handlers) returnError(err error) { |
| handler := h.FilePut.(*testHandler) |
| handler.err = err |
| } |
| |
| func getStatusMsg(p interface{}) string { |
| pkt := p.(*sshFxpStatusPacket) |
| return pkt.StatusError.msg |
| } |
| func checkOkStatus(t *testing.T, p interface{}) { |
| pkt := p.(*sshFxpStatusPacket) |
| assert.Equal(t, pkt.StatusError.Code, uint32(sshFxOk), |
| "sshFxpStatusPacket not OK\n", pkt.StatusError.msg) |
| } |
| |
| // fake/test packet |
| type fakePacket struct { |
| myid uint32 |
| handle string |
| } |
| |
| func (f fakePacket) id() uint32 { |
| return f.myid |
| } |
| |
| func (f fakePacket) getHandle() string { |
| return f.handle |
| } |
| func (fakePacket) UnmarshalBinary(d []byte) error { return nil } |
| |
| // XXX can't just set method to Get, need to use Open to setup Get/Put |
| func TestRequestGet(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Get") |
| pkt := fakePacket{myid: 1} |
| request.open(handlers, pkt) |
| // req.length is 5, so we test reads in 5 byte chunks |
| for i, txt := range []string{"file-", "data."} { |
| pkt := &sshFxpReadPacket{ID: uint32(i), Handle: "a", |
| Offset: uint64(i * 5), Len: 5} |
| rpkt := request.call(handlers, pkt, nil, 0) |
| dpkt := rpkt.(*sshFxpDataPacket) |
| assert.Equal(t, dpkt.id(), uint32(i)) |
| assert.Equal(t, string(dpkt.Data), txt) |
| } |
| } |
| |
| func TestRequestCustomError(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Stat") |
| pkt := fakePacket{myid: 1} |
| cmdErr := errors.New("stat not supported") |
| handlers.returnError(cmdErr) |
| rpkt := request.call(handlers, pkt, nil, 0) |
| assert.Equal(t, rpkt, statusFromError(pkt.myid, cmdErr)) |
| } |
| |
| // XXX can't just set method to Get, need to use Open to setup Get/Put |
| func TestRequestPut(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Put") |
| request.state.writerAt, _ = handlers.FilePut.Filewrite(request) |
| pkt := &sshFxpWritePacket{ID: 0, Handle: "a", Offset: 0, Length: 5, |
| Data: []byte("file-")} |
| rpkt := request.call(handlers, pkt, nil, 0) |
| checkOkStatus(t, rpkt) |
| pkt = &sshFxpWritePacket{ID: 1, Handle: "a", Offset: 5, Length: 5, |
| Data: []byte("data.")} |
| rpkt = request.call(handlers, pkt, nil, 0) |
| checkOkStatus(t, rpkt) |
| assert.Equal(t, "file-data.", handlers.getOutString()) |
| } |
| |
| func TestRequestCmdr(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Mkdir") |
| pkt := fakePacket{myid: 1} |
| rpkt := request.call(handlers, pkt, nil, 0) |
| checkOkStatus(t, rpkt) |
| |
| handlers.returnError(errTest) |
| rpkt = request.call(handlers, pkt, nil, 0) |
| assert.Equal(t, rpkt, statusFromError(pkt.myid, errTest)) |
| } |
| |
| func TestRequestInfoStat(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Stat") |
| pkt := fakePacket{myid: 1} |
| rpkt := request.call(handlers, pkt, nil, 0) |
| spkt, ok := rpkt.(*sshFxpStatResponse) |
| assert.True(t, ok) |
| assert.Equal(t, spkt.info.Name(), "request_test.go") |
| } |
| |
| func TestRequestInfoList(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("List") |
| request.handle = "1" |
| pkt := fakePacket{myid: 1} |
| rpkt := request.opendir(handlers, pkt) |
| hpkt, ok := rpkt.(*sshFxpHandlePacket) |
| if assert.True(t, ok) { |
| assert.Equal(t, hpkt.Handle, "1") |
| } |
| pkt = fakePacket{myid: 2} |
| request.call(handlers, pkt, nil, 0) |
| } |
| func TestRequestInfoReadlink(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Readlink") |
| pkt := fakePacket{myid: 1} |
| rpkt := request.call(handlers, pkt, nil, 0) |
| npkt, ok := rpkt.(*sshFxpNamePacket) |
| if assert.True(t, ok) { |
| assert.IsType(t, &sshFxpNameAttr{}, npkt.NameAttrs[0]) |
| assert.Equal(t, npkt.NameAttrs[0].Name, "request_test.go") |
| } |
| } |
| |
| func TestOpendirHandleReuse(t *testing.T) { |
| handlers := newTestHandlers() |
| request := testRequest("Stat") |
| request.handle = "1" |
| pkt := fakePacket{myid: 1} |
| rpkt := request.call(handlers, pkt, nil, 0) |
| assert.IsType(t, &sshFxpStatResponse{}, rpkt) |
| |
| request.Method = "List" |
| pkt = fakePacket{myid: 2} |
| rpkt = request.opendir(handlers, pkt) |
| if assert.IsType(t, &sshFxpHandlePacket{}, rpkt) { |
| hpkt := rpkt.(*sshFxpHandlePacket) |
| assert.Equal(t, hpkt.Handle, "1") |
| } |
| rpkt = request.call(handlers, pkt, nil, 0) |
| assert.IsType(t, &sshFxpNamePacket{}, rpkt) |
| } |