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 } return bytes.NewReader(t.filecontents), nil } func (t *testHandler) Filewrite(r *Request) (io.WriterAt, error) { if t.err != nil { return nil, t.err } return io.WriterAt(t.output), nil } func (t *testHandler) Filecmd(r *Request) error { return t.err } func (t *testHandler) Filelist(r *Request) (ListerAt, error) { if t.err != nil { return nil, t.err } 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.") func testRequest(method string) *Request { request := &Request{ Filepath: "./request_test.go", Method: method, Attrs: []byte("foo"), 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(ssh_FX_OK), "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 } func TestRequestGet(t *testing.T) { handlers := newTestHandlers() request := testRequest("Get") // req.length is 5, so we test reads in 5 byte chunks for i, txt := range []string{"file-", "data."} { pkt := &sshFxpReadPacket{uint32(i), "a", uint64(i * 5), 5} rpkt := request.call(handlers, pkt) 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) assert.Equal(t, rpkt, statusFromError(rpkt, cmdErr)) } func TestRequestPut(t *testing.T) { handlers := newTestHandlers() request := testRequest("Put") pkt := &sshFxpWritePacket{0, "a", 0, 5, []byte("file-")} rpkt := request.call(handlers, pkt) checkOkStatus(t, rpkt) pkt = &sshFxpWritePacket{1, "a", 5, 5, []byte("data.")} rpkt = request.call(handlers, pkt) 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) checkOkStatus(t, rpkt) handlers.returnError(errTest) rpkt = request.call(handlers, pkt) assert.Equal(t, rpkt, statusFromError(rpkt, errTest)) } func TestRequestInfoList(t *testing.T) { testInfoMethod(t, "List") } func TestRequestInfoReadlink(t *testing.T) { testInfoMethod(t, "Readlink") } func TestRequestInfoStat(t *testing.T) { handlers := newTestHandlers() request := testRequest("Stat") pkt := fakePacket{myid: 1} rpkt := request.call(handlers, pkt) spkt, ok := rpkt.(*sshFxpStatResponse) assert.True(t, ok) assert.Equal(t, spkt.info.Name(), "request_test.go") } func testInfoMethod(t *testing.T, method string) { handlers := newTestHandlers() request := testRequest(method) pkt := fakePacket{myid: 1} rpkt := request.call(handlers, pkt) npkt, ok := rpkt.(*sshFxpNamePacket) assert.True(t, ok) assert.IsType(t, sshFxpNameAttr{}, npkt.NameAttrs[0]) assert.Equal(t, npkt.NameAttrs[0].Name, "request_test.go") }