aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2018-02-19 19:19:53 +0100
committerStefan Majewsky <majewsky@gmx.net>2018-02-19 19:19:53 +0100
commit46081e2068aeea62783863515ab116bb6af20661 (patch)
treed4dcd5e849909b08597209781dba6fba526ad2ae
parentd1dec3782fb5f9aa5775dafb0ea1225af6279ed2 (diff)
downloadgo-schwift-46081e2068aeea62783863515ab116bb6af20661.tar.gz
test coverage for object upload, download, metadata update
-rw-r--r--object.go39
-rw-r--r--object_iterator.go2
-rw-r--r--object_iterator_test.go7
-rw-r--r--object_test.go132
-rw-r--r--shared_test.go13
5 files changed, 181 insertions, 12 deletions
diff --git a/object.go b/object.go
index 83ada1d..9da08c5 100644
--- a/object.go
+++ b/object.go
@@ -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