213 lines
5 KiB
Go
213 lines
5 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
|
|
}
|
|
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 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) { testInfoMethod(t, "List") }
|
|
func TestRequestInfoReadlink(t *testing.T) { testInfoMethod(t, "Readlink") }
|
|
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")
|
|
}
|
|
|
|
func TestOpendirHandleReuse(t *testing.T) {
|
|
handlers := newTestHandlers()
|
|
request := testRequest("Stat")
|
|
pkt := fakePacket{myid: 1}
|
|
rpkt := request.call(handlers, pkt)
|
|
assert.IsType(t, &sshFxpStatResponse{}, rpkt)
|
|
|
|
request.Method = "List"
|
|
pkt = fakePacket{myid: 2}
|
|
rpkt = request.call(handlers, pkt)
|
|
assert.IsType(t, &sshFxpNamePacket{}, rpkt)
|
|
}
|