mirror of https://github.com/pkg/sftp.git
				
				
				
			
		
			
				
	
	
		
			253 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| 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 | ssh_FXF_READ
 | |
| 	case "Put":
 | |
| 		flags = flags | ssh_FXF_WRITE
 | |
| 	}
 | |
| 	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(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 }
 | |
| 
 | |
| // 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)
 | |
| 		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))
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| 	checkOkStatus(t, rpkt)
 | |
| 	pkt = &sshFxpWritePacket{ID: 1, Handle: "a", Offset: 5, Length: 5,
 | |
| 		Data: []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 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 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)
 | |
| }
 | |
| func TestRequestInfoReadlink(t *testing.T) {
 | |
| 	handlers := newTestHandlers()
 | |
| 	request := testRequest("Readlink")
 | |
| 	pkt := fakePacket{myid: 1}
 | |
| 	rpkt := request.call(handlers, pkt)
 | |
| 	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)
 | |
| 	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)
 | |
| 	assert.IsType(t, &sshFxpNamePacket{}, rpkt)
 | |
| }
 |