aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2018-02-10 10:24:02 +0100
committerStefan Majewsky <majewsky@gmx.net>2018-02-10 10:30:26 +0100
commit8dfe7249e015e1a03a911d441cab8c4bb0c7d4c9 (patch)
tree709a12ce0c9b5236073b328c56ccf226640372fc
parent49a15959b15dcaefaff48146a7afabf640934f06 (diff)
downloadgo-schwift-8dfe7249e015e1a03a911d441cab8c4bb0c7d4c9.tar.gz
add tests for container iterator
-rw-r--r--container_iterator.go49
-rw-r--r--container_iterator_test.go169
-rw-r--r--headers.go6
-rw-r--r--shared_test.go11
4 files changed, 208 insertions, 27 deletions
diff --git a/container_iterator.go b/container_iterator.go
index 465a837..71c17be 100644
--- a/container_iterator.go
+++ b/container_iterator.go
@@ -76,12 +76,11 @@ type ContainerInfo struct {
LastModified time.Time
}
-func (i ContainerIterator) request(limit *uint, detailed bool) Request {
+func (i ContainerIterator) request(limit int, detailed bool) Request {
r := Request{
- Method: "GET",
- Headers: headersToHTTP(i.Headers),
- Options: cloneRequestOptions(i.Options),
- ExpectStatusCodes: []int{200},
+ Method: "GET",
+ Headers: headersToHTTP(i.Headers),
+ Options: cloneRequestOptions(i.Options),
}
if i.Prefix != "" {
@@ -94,25 +93,27 @@ func (i ContainerIterator) request(limit *uint, detailed bool) Request {
r.Options.Values.Set("marker", i.marker)
}
- if limit == nil {
+ if limit < 0 {
r.Options.Values.Del("limit")
} else {
- r.Options.Values.Set("limit", strconv.FormatUint(uint64(*limit), 10))
+ r.Options.Values.Set("limit", strconv.FormatUint(uint64(limit), 10))
}
if detailed {
r.Headers.Set("Accept", "application/json")
r.Options.Values.Set("format", "json")
+ r.ExpectStatusCodes = []int{200}
} else {
r.Headers.Set("Accept", "text/plain")
r.Options.Values.Set("format", "plain")
+ r.ExpectStatusCodes = []int{200, 204}
}
return r
}
//NextPage queries Swift for the next page of container names. If limit is
-//given, not more than that container names will be returned at once. Note
+//>= 0, not more than that container names will be returned at once. Note
//that the server also has a limit for how many containers to list in one
//request; the lower limit wins.
//
@@ -120,7 +121,7 @@ func (i ContainerIterator) request(limit *uint, detailed bool) Request {
//
//This method offers maximal flexibility, but most users will prefer the
//simpler interfaces offered by Collect() and Foreach().
-func (i *ContainerIterator) NextPage(limit *uint) ([]*Container, error) {
+func (i *ContainerIterator) NextPage(limit int) ([]*Container, error) {
if i.eof {
return nil, nil
}
@@ -128,14 +129,19 @@ func (i *ContainerIterator) NextPage(limit *uint) ([]*Container, error) {
if err != nil {
return nil, err
}
+
buf, err := collectResponseBody(resp)
if err != nil {
return nil, err
}
- names := strings.Split(string(buf), "\n")
- result := make([]*Container, len(names))
- for idx, name := range names {
- result[idx] = i.Account.Container(name)
+ bufStr := strings.TrimSuffix(string(buf), "\n")
+ var result []*Container
+ if bufStr != "" {
+ names := strings.Split(bufStr, "\n")
+ result = make([]*Container, len(names))
+ for idx, name := range names {
+ result[idx] = i.Account.Container(name)
+ }
}
if len(result) == 0 {
@@ -149,11 +155,11 @@ func (i *ContainerIterator) NextPage(limit *uint) ([]*Container, error) {
}
//NextPageDetailed is like NextPage, but includes basic metadata.
-func (i *ContainerIterator) NextPageDetailed(limit *uint) ([]ContainerInfo, error) {
+func (i *ContainerIterator) NextPageDetailed(limit int) ([]ContainerInfo, error) {
if i.eof {
return nil, nil
}
- resp, err := i.request(limit, false).Do(i.Account.client)
+ resp, err := i.request(limit, true).Do(i.Account.client)
if err != nil {
return nil, err
}
@@ -165,8 +171,9 @@ func (i *ContainerIterator) NextPageDetailed(limit *uint) ([]ContainerInfo, erro
Name string `json:"name"`
}
err = json.NewDecoder(resp.Body).Decode(&document)
+ closeErr := resp.Body.Close()
if err == nil {
- err = resp.Body.Close()
+ err = closeErr
}
if err != nil {
return nil, err
@@ -177,7 +184,7 @@ func (i *ContainerIterator) NextPageDetailed(limit *uint) ([]ContainerInfo, erro
result[idx].Container = i.Account.Container(data.Name)
result[idx].BytesUsed = data.BytesUsed
result[idx].ObjectCount = data.ObjectCount
- result[idx].LastModified, err = time.Parse(time.RFC3339Nano, data.LastModifiedStr)
+ result[idx].LastModified, err = time.Parse(time.RFC3339Nano, data.LastModifiedStr+"Z")
if err != nil {
//this error is sufficiently obscure that we don't need to expose a type for it
return nil, fmt.Errorf("Bad field containers[%d].last_modified: %s", idx, err.Error())
@@ -199,7 +206,7 @@ func (i *ContainerIterator) NextPageDetailed(limit *uint) ([]ContainerInfo, erro
//or when the callback returns a non-nil error.
func (i *ContainerIterator) Foreach(callback func(*Container) error) error {
for {
- containers, err := i.NextPage(nil)
+ containers, err := i.NextPage(-1)
if err != nil {
return err
}
@@ -218,7 +225,7 @@ func (i *ContainerIterator) Foreach(callback func(*Container) error) error {
//ForeachDetailed is like Foreach, but includes basic metadata.
func (i *ContainerIterator) ForeachDetailed(callback func(ContainerInfo) error) error {
for {
- infos, err := i.NextPageDetailed(nil)
+ infos, err := i.NextPageDetailed(-1)
if err != nil {
return err
}
@@ -240,7 +247,7 @@ func (i *ContainerIterator) ForeachDetailed(callback func(ContainerInfo) error)
func (i *ContainerIterator) Collect() ([]*Container, error) {
var result []*Container
for {
- containers, err := i.NextPage(nil)
+ containers, err := i.NextPage(-1)
if err != nil {
return nil, err
}
@@ -255,7 +262,7 @@ func (i *ContainerIterator) Collect() ([]*Container, error) {
func (i *ContainerIterator) CollectDetailed() ([]ContainerInfo, error) {
var result []ContainerInfo
for {
- infos, err := i.NextPageDetailed(nil)
+ infos, err := i.NextPageDetailed(-1)
if err != nil {
return nil, err
}
diff --git a/container_iterator_test.go b/container_iterator_test.go
new file mode 100644
index 0000000..df4ab80
--- /dev/null
+++ b/container_iterator_test.go
@@ -0,0 +1,169 @@
+/******************************************************************************
+*
+* Copyright 2018 Stefan Majewsky <majewsky@gmx.net>
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************************/
+
+package schwift
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestContainerIterator(t *testing.T) {
+ testWithAccount(t, func(a *Account) {
+ cname := func(idx int) string {
+ return fmt.Sprintf("schwift-test-listing%d", idx)
+ }
+
+ //create test containers that can be listed
+ for idx := 1; idx <= 4; idx++ {
+ _, err := a.Container(cname(idx)).EnsureExists()
+ expectSuccess(t, err)
+ }
+
+ //test iteration with empty last page
+ iter := a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ cs, err := iter.NextPage(2)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs, cname(1), cname(2))
+ cs, err = iter.NextPage(2)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs, cname(3), cname(4))
+ cs, err = iter.NextPage(2)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs)
+ cs, err = iter.NextPage(2)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs)
+
+ //test iteration with partial last page
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ cs, err = iter.NextPage(3)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs, cname(1), cname(2), cname(3))
+ cs, err = iter.NextPage(3)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs, cname(4))
+ cs, err = iter.NextPage(4)
+ expectSuccess(t, err)
+ expectContainerNames(t, cs)
+
+ //test detailed iteration
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ cis, err := iter.NextPageDetailed(2)
+ expectSuccess(t, err)
+ expectContainerInfos(t, cis, cname(1), cname(2))
+ cis, err = iter.NextPageDetailed(3)
+ expectSuccess(t, err)
+ expectContainerInfos(t, cis, cname(3), cname(4))
+ cis, err = iter.NextPageDetailed(3)
+ expectSuccess(t, err)
+ expectContainerInfos(t, cis)
+ cis, err = iter.NextPageDetailed(3)
+ expectSuccess(t, err)
+ expectContainerInfos(t, cis)
+
+ //test Foreach
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ idx := 0
+ expectSuccess(t, iter.Foreach(func(c *Container) error {
+ idx++
+ expectString(t, c.Name(), cname(idx))
+ return nil
+ }))
+ expectInt(t, idx, 4)
+
+ //test ForeachDetailed
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ idx = 0
+ expectSuccess(t, iter.ForeachDetailed(func(info ContainerInfo) error {
+ idx++
+ expectString(t, info.Container.Name(), cname(idx))
+ return nil
+ }))
+ expectInt(t, idx, 4)
+
+ //test Collect
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ cs, err = iter.Collect()
+ expectSuccess(t, err)
+ expectContainerNames(t, cs, cname(1), cname(2), cname(3), cname(4))
+
+ //test CollectDetailed
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ cis, err = iter.CollectDetailed()
+ expectSuccess(t, err)
+ expectContainerInfos(t, cis, cname(1), cname(2), cname(3), cname(4))
+
+ //cleanup
+ iter = a.Containers()
+ iter.Prefix = "schwift-test-listing"
+ expectSuccess(t, iter.Foreach(func(c *Container) error {
+ return c.Delete(nil, nil)
+ }))
+ })
+}
+
+func expectContainerNames(t *testing.T, actualContainers []*Container, expectedNames ...string) {
+ t.Helper()
+ if len(actualContainers) != len(expectedNames) {
+ t.Errorf("expected %d containers, got %d containers",
+ len(expectedNames), len(actualContainers))
+ return
+ }
+ for idx, c := range actualContainers {
+ if c.Name() != expectedNames[idx] {
+ t.Errorf("expected containers[%d].Name() == %q, got %q",
+ idx, expectedNames[idx], c.Name())
+ }
+ }
+}
+
+func expectContainerInfos(t *testing.T, actualInfos []ContainerInfo, expectedNames ...string) {
+ t.Helper()
+ if len(actualInfos) != len(expectedNames) {
+ t.Errorf("expected %d containers, got %d containers",
+ len(expectedNames), len(actualInfos))
+ return
+ }
+ for idx, info := range actualInfos {
+ if info.Container.Name() != expectedNames[idx] {
+ t.Errorf("expected containers[%d].Name() == %q, got %q",
+ idx, expectedNames[idx], info.Container.Name())
+ }
+ //TODO: upload test object of defined size to the listed containers to
+ //check if this zero is not just the default value
+ if info.BytesUsed != 0 {
+ t.Errorf("expected containers[%d] bytesUsed == 0, got %d",
+ idx, info.BytesUsed)
+ }
+ if info.ObjectCount != 0 {
+ t.Errorf("expected containers[%d] objectCount == 0, got %d",
+ idx, info.ObjectCount)
+ }
+ if info.LastModified.IsZero() {
+ t.Errorf("containers[%d].LastModified is zero", idx)
+ }
+ }
+}
diff --git a/headers.go b/headers.go
index 1bc206f..7a6ec16 100644
--- a/headers.go
+++ b/headers.go
@@ -24,9 +24,6 @@ import (
)
func headersToHTTP(h map[string]string) http.Header {
- if h == nil {
- return nil
- }
dest := make(http.Header, len(h))
for k, v := range h {
dest.Set(k, v)
@@ -35,9 +32,6 @@ func headersToHTTP(h map[string]string) http.Header {
}
func headersFromHTTP(src http.Header) map[string]string {
- if src == nil {
- return nil
- }
h := make(map[string]string, len(src))
for k, v := range src {
if len(v) > 0 {
diff --git a/shared_test.go b/shared_test.go
index 04481d4..1e7130e 100644
--- a/shared_test.go
+++ b/shared_test.go
@@ -121,6 +121,13 @@ func expectFloat64(t *testing.T, actual float64, expected float64) {
}
}
+func expectInt(t *testing.T, actual int, expected int) {
+ t.Helper()
+ if actual != expected {
+ t.Errorf("expected value %d, got %d instead\n", expected, actual)
+ }
+}
+
func expectUint64(t *testing.T, actual uint64, expected uint64) {
t.Helper()
if actual != expected {
@@ -180,3 +187,7 @@ func expectHeaders(t *testing.T, actual map[string]string, expected map[string]s
}
}
}
+
+func expectSuccess(t *testing.T, actual error) (ok bool) {
+ return expectError(t, actual, "")
+}