Skip to content
This repository was archived by the owner on Mar 29, 2023. It is now read-only.

create implicit directories from multipart requests #6

Merged
merged 3 commits into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 80 additions & 116 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,24 @@ func TestSliceFiles(t *testing.T) {
"2": NewBytesFile([]byte("beep")),
"3": NewBytesFile([]byte("boop")),
})
buf := make([]byte, 20)

it := sf.Entries()

if !it.Next() {
t.Fatal("Expected a file")
}
rf := ToFile(it.Node())
if rf == nil {
t.Fatal("Expected a regular file")
}
read, err := rf.Read(buf)
if read != 11 || err != nil {
t.Fatal("NextFile got a file in the wrong order")
}

if !it.Next() {
t.Fatal("Expected a file")
}
if !it.Next() {
t.Fatal("Expected a file")
}
if it.Next() {
t.Fatal("Wild file appeared!")
}

if err := sf.Close(); err != nil {
t.Fatal("Should be able to call `Close` on a SliceFile")
}
CheckDir(t, sf, []Event{
{
kind: TFile,
name: "1",
value: "Some text!\n",
},
{
kind: TFile,
name: "2",
value: "beep",
},
{
kind: TFile,
name: "3",
value: "boop",
},
})
}

func TestReaderFiles(t *testing.T) {
Expand All @@ -59,7 +48,6 @@ func TestReaderFiles(t *testing.T) {
t.Fatal("Expected EOF when reading after close")
}
}

func TestMultipartFiles(t *testing.T) {
data := `
--Boundary!
Expand All @@ -82,97 +70,73 @@ Content-Type: application/symlink
Content-Disposition: file; filename="dir/simlynk"

anotherfile
--Boundary!
Content-Type: text/plain
Content-Disposition: file; filename="implicit1/implicit2/deep_implicit"

implicit file1
--Boundary!
Content-Type: text/plain
Content-Disposition: file; filename="implicit1/shallow_implicit"

implicit file2
--Boundary!--

`

reader := strings.NewReader(data)
mpReader := multipart.NewReader(reader, "Boundary!")
buf := make([]byte, 20)

// test properties of a file created from the first part
part, err := mpReader.NextPart()
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpname, mpf, err := newFileFromPart("", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil multipartFile, nil error")
}
mf, ok := mpf.(File)
if !ok {
t.Fatal("Expected file to not be a directory")
}
if mpname != "name" {
t.Fatal("Expected filename to be \"name\"")
}
if n, err := mf.Read(buf); n != 4 || !(err == io.EOF || err == nil) {
t.Fatal("Expected to be able to read 4 bytes", n, err)
}
if err := mf.Close(); err != nil {
t.Fatal("Expected to be able to close file")
}

// test properties of file created from second part (directory)
part, err = mpReader.NextPart()
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpname, mpf, err = newFileFromPart("", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil multipartFile, nil error")
}
md, ok := mpf.(Directory)
if !ok {
t.Fatal("Expected file to be a directory")
}
if mpname != "dir" {
t.Fatal("Expected filename to be \"dir\"")
}
if err := md.Close(); err != nil {
t.Fatal("Should be able to call `Close` on a directory")
}

// test properties of file created from third part (nested file)
part, err = mpReader.NextPart()
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpname, mpf, err = newFileFromPart("dir/", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil multipartFile, nil error")
}
mf, ok = mpf.(File)
if !ok {
t.Fatal("Expected file to not be a directory")
}
if mpname != "nested" {
t.Fatalf("Expected filename to be \"nested\", got %s", mpname)
}
if n, err := mf.Read(buf); n != 12 || !(err == nil || err == io.EOF) {
t.Fatalf("expected to be able to read 12 bytes from file: %s (got %d)", err, n)
}
if err := mpf.Close(); err != nil {
t.Fatalf("should be able to close file: %s", err)
}

// test properties of symlink created from fourth part (symlink)
part, err = mpReader.NextPart()
if part == nil || err != nil {
t.Fatal("Expected non-nil part, nil error")
}
mpname, mpf, err = newFileFromPart("dir/", part, &peekReader{r: mpReader})
if mpf == nil || err != nil {
t.Fatal("Expected non-nil multipartFile, nil error")
}
ms, ok := mpf.(*Symlink)
if !ok {
t.Fatal("Expected file to not be a directory")
}
if mpname != "simlynk" {
t.Fatal("Expected filename to be \"dir/simlynk\"")
}
if ms.Target != "anotherfile" {
t.Fatal("expected link to point to anotherfile")
}
dir, err := NewFileFromPartReader(mpReader, multipartFormdataType)
if err != nil {
t.Fatal(err)
}

CheckDir(t, dir, []Event{
{
kind: TFile,
name: "name",
value: "beep",
},
{
kind: TDirStart,
name: "dir",
},
{
kind: TFile,
name: "nested",
value: "some content",
},
{
kind: TSymlink,
name: "simlynk",
value: "anotherfile",
},
{
kind: TDirEnd,
},
{
kind: TDirStart,
name: "implicit1",
},
{
kind: TDirStart,
name: "implicit2",
},
{
kind: TFile,
name: "deep_implicit",
value: "implicit file1",
},
{
kind: TDirEnd,
},
{
kind: TFile,
name: "shallow_implicit",
value: "implicit file2",
},
{
kind: TDirEnd,
},
})
}
126 changes: 126 additions & 0 deletions helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package files

import (
"io/ioutil"
"testing"
)

type Kind int

const (
TFile Kind = iota
TSymlink
TDirStart
TDirEnd
)

type Event struct {
kind Kind
name string
value string
}

func CheckDir(t *testing.T, dir Directory, expected []Event) {
expectedIndex := 0
expect := func() (Event, int) {
t.Helper()

if expectedIndex > len(expected) {
t.Fatal("no more expected entries")
}
i := expectedIndex
expectedIndex++

// Add an implicit "end" event at the end. It makes this
// function a bit easier to write.
next := Event{kind: TDirEnd}
if i < len(expected) {
next = expected[i]
}
return next, i
}
var check func(d Directory)
check = func(d Directory) {
it := d.Entries()

for it.Next() {
next, i := expect()

if it.Name() != next.name {
t.Fatalf("[%d] expected filename to be %q", i, next.name)
}

switch next.kind {
case TFile:
mf, ok := it.Node().(File)
if !ok {
t.Fatalf("[%d] expected file to be a normal file: %T", i, it.Node())
}
out, err := ioutil.ReadAll(mf)
if err != nil {
t.Errorf("[%d] failed to read file", i)
continue
}
if string(out) != next.value {
t.Errorf(
"[%d] while reading %q, expected %q, got %q",
i,
it.Name(),
next.value,
string(out),
)
continue
}
case TSymlink:
mf, ok := it.Node().(*Symlink)
if !ok {
t.Errorf("[%d] expected file to be a symlink: %T", i, it.Node())
continue
}
if mf.Target != next.value {
t.Errorf(
"[%d] target of symlink %q should have been %q but was %q",
i,
it.Name(),
next.value,
mf.Target,
)
continue
}
case TDirStart:
mf, ok := it.Node().(Directory)
if !ok {
t.Fatalf(
"[%d] expected file to be a directory: %T",
i,
it.Node(),
)
}
check(mf)
case TDirEnd:
t.Errorf(
"[%d] expected end of directory, found %#v at %q",
i,
it.Node(),
it.Name(),
)
return
default:
t.Fatal("unhandled type", next.kind)
}
if err := it.Node().Close(); err != nil {
t.Fatalf("[%d] expected to be able to close node", i)
}
}
next, i := expect()

if it.Err() != nil {
t.Fatalf("[%d] got error: %s", i, it.Err())
}

if next.kind != TDirEnd {
t.Fatalf("[%d] found end of directory, expected %#v", i, next)
}
}
check(dir)
}
Loading