Skip to content

Commit

Permalink
steinfletcher#145 Add support for custom fs.FS
Browse files Browse the repository at this point in the history
  • Loading branch information
tjad committed Dec 8, 2024
1 parent 7d8ed83 commit a112154
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 3 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,35 @@ func TestApi(t *testing.T) {
}
```

#### Provide a multipart/form-data with custom filesystem

```go
inMemFS := fstest.MapFS{
"audio.wav": &fstest.MapFile{
Data: []byte{19,2,123,12,35,1},
Mode: fs.FileMode(0644),
ModTime: time.Now(),
},
"audio.mp3": &fstest.MapFile{
Data: []byte{21,13,88,123,9,8},
Mode: fs.FileMode(0644),
ModTime: time.Now(),
},
}

func TestApi(t *testing.T) {
apitest.Handler(handler).
UseFS(inMemFS).
Post("/hello").
MultipartFormData("a", "1", "2").
MultipartFile("file", "audio.wav", "audio.mp3").
Expect(t).
Status(http.StatusOK).
End()
}
```


#### Capture the request and response data

```go
Expand Down
16 changes: 13 additions & 3 deletions apitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (
"fmt"
"hash/fnv"
"io"
"io/fs"
"io/ioutil"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/textproto"
"net/url"
"os"
"path/filepath"
"runtime/debug"
"sort"
Expand Down Expand Up @@ -56,6 +56,7 @@ type APITest struct {
meta map[string]interface{}
started time.Time
finished time.Time
fileSystem fs.FS
}

// InboundRequest used to wrap the incoming request with a timestamp
Expand Down Expand Up @@ -98,6 +99,7 @@ func New(name ...string) *APITest {
if len(name) > 0 {
apiTest.name = name[0]
}
apiTest.fileSystem = OSFS{}

return apiTest
}
Expand Down Expand Up @@ -221,6 +223,14 @@ func (a *APITest) Response() *Response {
return a.response
}

// Use filesystem allows you to change to a custom fs.FS filesystem that you can define (Used by Request.MultipartFile).
// e.g: fstest.MapFS
// Your os filesystem is used by default
func (a *APITest) UseFS(fs fs.FS) *APITest {
a.fileSystem = fs
return a
}

// Request is the user defined request that will be invoked on the handler under test
type Request struct {
interceptor Intercept
Expand Down Expand Up @@ -532,13 +542,13 @@ func (r *Request) MultipartFile(name string, ff ...string) *Request {

for _, f := range ff {
func() {
file, err := os.Open(f)
file, err := r.apiTest.fileSystem.Open(f)
if err != nil {
r.apiTest.t.Fatal(err)
}
defer file.Close()

part, err := r.multipart.CreateFormFile(name, filepath.Base(file.Name()))
part, err := r.multipart.CreateFormFile(name, filepath.Base(f))
if err != nil {
r.apiTest.t.Fatal(err)
}
Expand Down
91 changes: 91 additions & 0 deletions apitest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/fs"
"io/ioutil"
"net/http"
"net/http/cookiejar"
Expand All @@ -13,6 +14,7 @@ import (
"reflect"
"strings"
"testing"
"testing/fstest"
"time"

"github.com/steinfletcher/apitest"
Expand Down Expand Up @@ -1285,6 +1287,95 @@ func TestApiTest_AddsMultipartFormData(t *testing.T) {
End()
}

func TestApiTest_AddsMultipartFormDataWithCustomFS(t *testing.T) {
handler := http.NewServeMux()

inMemFS := fstest.MapFS{
"audio.wav": &fstest.MapFile{
Data: []byte{19, 2, 123, 12, 35, 1},
Mode: fs.FileMode(0644),
ModTime: time.Now(),
},
"audio.mp3": &fstest.MapFile{
Data: []byte{21, 13, 88, 123, 9, 8},
Mode: fs.FileMode(0644),
ModTime: time.Now(),
},
}

handler.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header["Content-Type"][0], "multipart/form-data") {
w.WriteHeader(http.StatusBadRequest)
return
}

err := r.ParseMultipartForm(2 << 32)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}

expectedPostFormData := map[string][]string{
"name": {"John"},
"age": {"99"},
"children": {"Jack", "Ann"},
"pets": {"Toby", "Henry", "Alice"},
}

for key := range expectedPostFormData {
if !reflect.DeepEqual(expectedPostFormData[key], r.MultipartForm.Value[key]) {
w.WriteHeader(http.StatusBadRequest)
return
}
}

for _, exp := range []struct {
filename string
data []byte
}{
{
filename: "audio1.wav",
data: inMemFS["audio.wav"].Data,
},
{
filename: "audio.mp3",
data: inMemFS["audio.mp3"].Data,
},
} {
for _, file := range r.MultipartForm.File[exp.filename] {
assert.Equal(t, exp.filename, file.Filename)

f, err := file.Open()
if err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadAll(f)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, exp.data, data)
}
}

w.WriteHeader(http.StatusOK)
})

apitest.New().
UseFS(inMemFS).
Handler(handler).
Post("/hello").
MultipartFormData("name", "John").
MultipartFormData("age", "99").
MultipartFormData("children", "Jack").
MultipartFormData("children", "Ann").
MultipartFormData("pets", "Toby", "Henry", "Alice").
MultipartFile("audio1", "audio.wav").
MultipartFile("audio2", "audio.mp3").
Expect(t).
Status(http.StatusOK).
End()
}

func TestApiTest_CombineFormDataWithMultipart(t *testing.T) {
if os.Getenv("RUN_FATAL_TEST") == "FormData" {
apitest.New().
Expand Down
14 changes: 14 additions & 0 deletions filesystem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package apitest

import (
"io/fs"
"os"
)

//An implementation of fs.FS that wraps your OS's filesystem
type OSFS struct {
}
//Calls os.Open
func (OSFS) Open(name string) (file fs.File, err error) {
return os.Open(name)
}

0 comments on commit a112154

Please sign in to comment.