diff options
| author | Stefan Majewsky <majewsky@gmx.net> | 2018-02-19 19:19:53 +0100 |
|---|---|---|
| committer | Stefan Majewsky <majewsky@gmx.net> | 2018-02-19 19:19:53 +0100 |
| commit | 46081e2068aeea62783863515ab116bb6af20661 (patch) | |
| tree | d4dcd5e849909b08597209781dba6fba526ad2ae | |
| parent | d1dec3782fb5f9aa5775dafb0ea1225af6279ed2 (diff) | |
| download | go-schwift-46081e2068aeea62783863515ab116bb6af20661.tar.gz | |
test coverage for object upload, download, metadata update
| -rw-r--r-- | object.go | 39 | ||||
| -rw-r--r-- | object_iterator.go | 2 | ||||
| -rw-r--r-- | object_iterator_test.go | 7 | ||||
| -rw-r--r-- | object_test.go | 132 | ||||
| -rw-r--r-- | shared_test.go | 13 |
5 files changed, 181 insertions, 12 deletions
@@ -115,7 +115,7 @@ func (o *Object) Headers() (ObjectHeaders, error) { //Update updates the object's headers using a POST request. To add URL //parameters, pass a non-nil *RequestOptions. // -//If you are not sure whether the container exists, use Create() instead. +//This operation returns http.StatusNotFound if the object does not exist. // //A successful POST request implies Invalidate() since it may change metadata. func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error { @@ -125,7 +125,7 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error { ObjectName: o.name, Headers: headersToHTTP(headers), Options: opts, - ExpectStatusCodes: []int{204}, + ExpectStatusCodes: []int{202}, }.Do(o.c.a.client) if err == nil { o.Invalidate() @@ -146,6 +146,10 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error { // var buffer string // o.Upload(bytes.NewReader([]byte(buffer)), headers, opts) // +//If you have neither an io.Reader nor a []byte or string, but you have a +//function that generates the object's content into an io.Writer, use +//UploadWithWriter instead. +// //If content is a *bytes.Reader or a *bytes.Buffer instance, the Content-Length //and Etag request headers will be computed automatically. Otherwise, it is //highly recommended that the caller set these headers (if possible) to allow @@ -235,6 +239,37 @@ func tryComputeEtag(content io.Reader, headers ObjectHeaders) { } } +//UploadWithWriter is a variant of Upload that can be used when the object's +//content is generated by some function or package that takes an io.Writer +//instead of supplying an io.Reader. For example: +// +// func greeting(target io.Writer, name string) error { +// _, err := fmt.Fprintf(target, "Hello %s!\n", name) +// return err +// } +// +// obj := container.Object("greeting-for-susan-and-jeffrey") +// err := obj.UploadWithWriter(nil, nil, func(w io.Writer) error { +// err := greeting(w, "Susan") +// if err == nil { +// err = greeting(w, "Jeffrey") +// } +// return err +// }) +// +//If you do not need an io.Writer, always use Upload instead. +func (o *Object) UploadWithWriter(headers ObjectHeaders, opts *RequestOptions, callback func(io.Writer) error) error { + reader, writer := io.Pipe() + errChan := make(chan error) + go func() { + err := o.Upload(reader, headers, opts) + reader.CloseWithError(err) //stop the writer if it is still writing + errChan <- err + }() + writer.CloseWithError(callback(writer)) //stop the reader if it is still reading + return <-errChan +} + //Delete deletes the object using a DELETE request. To add URL parameters, //pass a non-nil *RequestOptions. // diff --git a/object_iterator.go b/object_iterator.go index 67f3138..6d93ccf 100644 --- a/object_iterator.go +++ b/object_iterator.go @@ -70,6 +70,8 @@ type ObjectIterator struct { //Options may contain additional query parameters for the GET request. Options *RequestOptions + //TODO: Delimter field (and check if other stuff is missing) + base *iteratorBase } diff --git a/object_iterator_test.go b/object_iterator_test.go index 64cb6a2..92fe4b1 100644 --- a/object_iterator_test.go +++ b/object_iterator_test.go @@ -20,8 +20,6 @@ package schwift import ( "bytes" - "crypto/md5" - "encoding/hex" "fmt" "testing" ) @@ -29,11 +27,6 @@ import ( var objectExampleContent = []byte(`{"message":"Hello World!"}`) var objectExampleContentEtag = etagOf(objectExampleContent) -func etagOf(buf []byte) string { - hash := md5.Sum(buf) - return hex.EncodeToString(hash[:]) -} - func TestObjectIterator(t *testing.T) { testWithContainer(t, func(c *Container) { oname := func(idx int) string { diff --git a/object_test.go b/object_test.go index 841d177..73463ad 100644 --- a/object_test.go +++ b/object_test.go @@ -20,6 +20,8 @@ package schwift import ( "bytes" + "io" + "io/ioutil" "net/http" "testing" ) @@ -30,6 +32,7 @@ func TestObjectLifecycle(t *testing.T) { o := c.Object(objectName) expectString(t, o.Name(), objectName) + expectString(t, o.FullName(), c.Name()+"/"+objectName) if o.Container() != c { t.Errorf("expected o.Container() = %#v, got %#v instead\n", c, o.Container()) } @@ -59,3 +62,132 @@ func TestObjectLifecycle(t *testing.T) { expectSuccess(t, err) }) } + +func TestObjectUpload(t *testing.T) { + testWithContainer(t, func(c *Container) { + validateUploadedFile := func(obj *Object, expected []byte) { + str, err := obj.Download(nil, nil).AsString() + expectSuccess(t, err) + expectString(t, str, string(expected)) + obj.Invalidate() + hdr, err := obj.Headers() + expectSuccess(t, err) + expectString(t, hdr.Etag().Get(), etagOf(expected)) + } + + //test upload with bytes.Reader + obj := c.Object("upload1") + err := obj.Upload(bytes.NewReader(objectExampleContent), nil, nil) + expectSuccess(t, err) + validateUploadedFile(obj, objectExampleContent) + + //test upload with bytes.Buffer + obj = c.Object("upload2") + err = obj.Upload(bytes.NewBuffer(objectExampleContent), nil, nil) + expectSuccess(t, err) + validateUploadedFile(obj, objectExampleContent) + + //test upload with opaque io.Reader + obj = c.Object("upload3") + err = obj.Upload(opaqueReader{bytes.NewReader(objectExampleContent)}, nil, nil) + expectSuccess(t, err) + validateUploadedFile(obj, objectExampleContent) + + //test upload with io.Writer + obj = c.Object("upload4") + err = obj.UploadWithWriter(nil, nil, func(w io.Writer) error { + _, err := w.Write(objectExampleContent) + return err + }) + expectSuccess(t, err) + validateUploadedFile(obj, objectExampleContent) + + //test upload with empty reader (should create zero-byte-sized object) + obj = c.Object("upload5") + err = obj.Upload(eofReader{}, nil, nil) + expectSuccess(t, err) + validateUploadedFile(obj, nil) + + //test upload without reader (should create zero-byte-sized object) + obj = c.Object("upload6") + err = obj.Upload(nil, nil, nil) + expectSuccess(t, err) + validateUploadedFile(obj, nil) + }) +} + +type eofReader struct{} + +func (r eofReader) Read([]byte) (int, error) { + return 0, io.EOF +} + +type opaqueReader struct { + b *bytes.Reader +} + +func (r opaqueReader) Read(buf []byte) (int, error) { + return r.b.Read(buf) +} + +func TestObjectDownload(t *testing.T) { + testWithContainer(t, func(c *Container) { + //upload example object + obj := c.Object("example") + err := obj.Upload(bytes.NewReader(objectExampleContent), nil, nil) + expectSuccess(t, err) + + //test download as string + str, err := obj.Download(nil, nil).AsString() + expectSuccess(t, err) + expectString(t, str, string(objectExampleContent)) + + //test download as byte slice + buf, err := obj.Download(nil, nil).AsByteSlice() + expectSuccess(t, err) + expectString(t, string(buf), string(objectExampleContent)) + + //test download as io.ReadCloser slice + reader, err := obj.Download(nil, nil).AsReadCloser() + expectSuccess(t, err) + buf = make([]byte, 4) + _, err = reader.Read(buf) + expectSuccess(t, err) + expectString(t, string(buf), string(objectExampleContent[0:4])) + _, err = reader.Read(buf) + expectSuccess(t, err) + expectString(t, string(buf), string(objectExampleContent[4:8])) + buf, err = ioutil.ReadAll(reader) + expectSuccess(t, err) + expectString(t, string(buf), string(objectExampleContent[8:])) + }) +} + +func TestObjectUpdate(t *testing.T) { + testWithContainer(t, func(c *Container) { + obj := c.Object("example") + + //test that metadata update fails for non-existing object + newHeaders := make(ObjectHeaders) + newHeaders.ContentType().Set("application/json") + err := obj.Update(newHeaders, nil) + expectBool(t, Is(err, http.StatusNotFound), true) + expectError(t, err, "expected 202 response, got 404 instead: <html><h1>Not Found</h1><p>The resource could not be found.</p></html>") + + //create object + err = obj.Upload(nil, nil, nil) + expectSuccess(t, err) + + hdr, err := obj.Headers() + expectSuccess(t, err) + expectString(t, hdr.ContentType().Get(), "application/octet-stream") + + //now the metadata update should work + err = obj.Update(newHeaders, nil) + expectSuccess(t, err) + obj.Invalidate() + hdr, err = obj.Headers() + expectSuccess(t, err) + expectString(t, hdr.ContentType().Get(), "application/json") + }) +} diff --git a/shared_test.go b/shared_test.go index be29557..2c1d204 100644 --- a/shared_test.go +++ b/shared_test.go @@ -19,6 +19,7 @@ package schwift import ( + "crypto/md5" "crypto/rand" "encoding/hex" "math" @@ -121,6 +122,11 @@ func testWithContainer(t *testing.T, testCode func(c *Container)) { //////////////////////////////////////////////////////////////////////////////// +func etagOf(buf []byte) string { + hash := md5.Sum(buf) + return hex.EncodeToString(hash[:]) +} + func getRandomName() string { var buf [16]byte _, err := rand.Read(buf[:]) @@ -132,14 +138,14 @@ func getRandomName() string { //////////////////////////////////////////////////////////////////////////////// -func expectBool(t *testing.T, actual bool, expected bool) { +func expectBool(t *testing.T, actual, expected bool) { t.Helper() if actual != expected { t.Errorf("expected value %#v, got %#v instead\n", expected, actual) } } -func expectFloat64(t *testing.T, actual float64, expected float64) { +func expectFloat64(t *testing.T, actual, expected float64) { t.Helper() if math.Abs((actual-expected)/expected) > 1e-8 { t.Errorf("expected value %g, got %g instead\n", expected, actual) @@ -167,7 +173,7 @@ func expectUint64(t *testing.T, actual, expected uint64) { } } -func expectString(t *testing.T, actual string, expected string) { +func expectString(t *testing.T, actual, expected string) { t.Helper() if actual != expected { t.Errorf("expected value %q, got %q instead\n", expected, actual) @@ -188,6 +194,7 @@ func expectError(t *testing.T, actual error, expected string) (ok bool) { } func expectSuccess(t *testing.T, actual error) (ok bool) { + t.Helper() if actual != nil { t.Errorf("expected success, got error %q instead\n", actual.Error()) return false |
