From 70d9fc2a699624f8fc87f1c5837241c536087373 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 7 Feb 2018 20:45:42 +0100 Subject: increase test coverage --- container_test.go | 48 ++++++++++++++----- field_test.go | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ field_time.go | 7 +-- generated.go | 6 +-- generated.go.in | 2 +- headers_test.go | 43 +----------------- shared_test.go | 108 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 285 insertions(+), 63 deletions(-) create mode 100644 field_test.go diff --git a/container_test.go b/container_test.go index c6e20f1..76ff037 100644 --- a/container_test.go +++ b/container_test.go @@ -19,15 +19,19 @@ package schwift import ( - "crypto/rand" - "encoding/hex" "net/http" "testing" ) -func TestContainerExistence(t *testing.T) { +func TestContainerLifecycle(t *testing.T) { testWithAccount(t, func(a *Account) { - c := a.Container(getRandomName()) + containerName := getRandomName() + c := a.Container(containerName) + + expectString(t, c.Name(), containerName) + if c.Account() != a { + t.Errorf("expected c.Account() = %#v, go %#v instead\n", a, c.Account()) + } exists, err := c.Exists() expectError(t, err, "") @@ -38,20 +42,42 @@ func TestContainerExistence(t *testing.T) { expectBool(t, Is(err, http.StatusNotFound), true) expectBool(t, Is(err, http.StatusNoContent), false) + //DELETE should be idempotent and not return success on non-existence, but + //OpenStack loves to be inconsistent with everything (including, notably, itself) + err = c.Delete(nil, nil) + expectError(t, err, "expected 204 response, got 404 instead:

Not Found

The resource could not be found.

") + err = c.Create(nil, nil) expectError(t, err, "") exists, err = c.Exists() expectError(t, err, "") expectBool(t, exists, true) + + err = c.Delete(nil, nil) + expectError(t, err, "") }) } -func getRandomName() string { - var buf [16]byte - _, err := rand.Read(buf[:]) - if err != nil { - panic(err.Error()) - } - return hex.EncodeToString(buf[:]) +func TestContainerUpdate(t *testing.T) { + testWithContainer(t, func(c *Container) { + + hdr, err := c.Headers() + expectError(t, err, "") + expectBool(t, hdr.ObjectCount().Exists(), true) + expectUint64(t, hdr.ObjectCount().Get(), 0) + + hdr = make(ContainerHeaders) + hdr.ObjectCountQuota().Set(23) + hdr.BytesUsedQuota().Set(42) + + err = c.Update(hdr, nil) + expectError(t, err, "") + + hdr, err = c.Headers() + expectError(t, err, "") + expectUint64(t, hdr.BytesUsedQuota().Get(), 42) + expectUint64(t, hdr.ObjectCountQuota().Get(), 23) + + }) } diff --git a/field_test.go b/field_test.go new file mode 100644 index 0000000..bea85ca --- /dev/null +++ b/field_test.go @@ -0,0 +1,134 @@ +/****************************************************************************** +* +* Copyright 2018 Stefan Majewsky +* +* 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 ( + "strconv" + "testing" +) + +func TestFieldString(t *testing.T) { + hdr := make(AccountHeaders) + expectBool(t, hdr.TempURLKey().Exists(), false) + expectString(t, hdr.TempURLKey().Get(), "") + expectError(t, hdr.Validate(), "") + + hdr["X-Account-Meta-Temp-Url-Key"] = "" + expectBool(t, hdr.TempURLKey().Exists(), false) + expectString(t, hdr.TempURLKey().Get(), "") + expectError(t, hdr.Validate(), "") + + hdr["X-Account-Meta-Temp-Url-Key"] = "foo" + expectBool(t, hdr.TempURLKey().Exists(), true) + expectString(t, hdr.TempURLKey().Get(), "foo") + expectError(t, hdr.Validate(), "") + + hdr.TempURLKey().Set("bar") + expectHeaders(t, hdr, map[string]string{ + "X-Account-Meta-Temp-Url-Key": "bar", + }) + hdr.TempURLKey().Clear() + expectHeaders(t, hdr, map[string]string{ + "X-Account-Meta-Temp-Url-Key": "", + }) + hdr.TempURLKey().Del() + expectHeaders(t, hdr, nil) + hdr.TempURLKey().Clear() + expectHeaders(t, hdr, map[string]string{ + "X-Account-Meta-Temp-Url-Key": "", + }) +} + +//////////////////////////////////////////////////////////////////////////////// + +func TestFieldTimestamp(t *testing.T) { + testWithAccount(t, func(a *Account) { + hdr, err := a.Headers() + if !expectError(t, err, "") { + return + } + + expectBool(t, hdr.Timestamp().Exists(), true) + + actual := float64(hdr.Timestamp().Get().UnixNano()) / 1e9 + expected, _ := strconv.ParseFloat(hdr["X-Timestamp"], 64) + expectFloat64(t, actual, expected) + }) + + hdr := make(AccountHeaders) + expectBool(t, hdr.Timestamp().Exists(), false) + expectBool(t, hdr.Timestamp().Get().IsZero(), true) + expectError(t, hdr.Validate(), "") + + hdr["X-Timestamp"] = "wtf" + expectBool(t, hdr.Timestamp().Exists(), true) + expectBool(t, hdr.Timestamp().Get().IsZero(), true) + expectError(t, hdr.Validate(), `Bad header X-Timestamp: strconv.ParseFloat: parsing "wtf": invalid syntax`) +} + +//////////////////////////////////////////////////////////////////////////////// + +func TestFieldUint64(t *testing.T) { + hdr := make(AccountHeaders) + expectBool(t, hdr.BytesUsedQuota().Exists(), false) + expectUint64(t, hdr.BytesUsedQuota().Get(), 0) + expectError(t, hdr.Validate(), "") + + hdr["X-Account-Meta-Quota-Bytes"] = "23" + expectBool(t, hdr.BytesUsedQuota().Exists(), true) + expectUint64(t, hdr.BytesUsedQuota().Get(), 23) + expectError(t, hdr.Validate(), "") + + hdr["X-Account-Meta-Quota-Bytes"] = "-23" + expectBool(t, hdr.BytesUsedQuota().Exists(), true) + expectUint64(t, hdr.BytesUsedQuota().Get(), 0) + expectError(t, hdr.Validate(), `Bad header X-Account-Meta-Quota-Bytes: strconv.ParseUint: parsing "-23": invalid syntax`) + + hdr.BytesUsedQuota().Set(9001) + expectHeaders(t, hdr, map[string]string{ + "X-Account-Meta-Quota-Bytes": "9001", + }) + hdr.BytesUsedQuota().Clear() + expectHeaders(t, hdr, map[string]string{ + "X-Account-Meta-Quota-Bytes": "", + }) + hdr.BytesUsedQuota().Del() + expectHeaders(t, hdr, nil) + hdr.BytesUsedQuota().Clear() + expectHeaders(t, hdr, map[string]string{ + "X-Account-Meta-Quota-Bytes": "", + }) +} + +func TestFieldUint64Readonly(t *testing.T) { + hdr := make(AccountHeaders) + expectBool(t, hdr.BytesUsed().Exists(), false) + expectUint64(t, hdr.BytesUsed().Get(), 0) + expectError(t, hdr.Validate(), "") + + hdr["X-Account-Bytes-Used"] = "23" + expectBool(t, hdr.BytesUsed().Exists(), true) + expectUint64(t, hdr.BytesUsed().Get(), 23) + expectError(t, hdr.Validate(), "") + + hdr["X-Account-Bytes-Used"] = "-23" + expectBool(t, hdr.BytesUsed().Exists(), true) + expectUint64(t, hdr.BytesUsed().Get(), 0) + expectError(t, hdr.Validate(), `Bad header X-Account-Bytes-Used: strconv.ParseUint: parsing "-23": invalid syntax`) +} diff --git a/field_time.go b/field_time.go index d8a1776..25a2c1e 100644 --- a/field_time.go +++ b/field_time.go @@ -19,7 +19,6 @@ package schwift import ( - "math" "strconv" "time" ) @@ -58,11 +57,7 @@ func (f FieldUnixTimeReadonly) Get() time.Time { if err != nil { return time.Time{} } - seconds := math.Floor(v) - return time.Unix( - int64(seconds), - int64(1e9*(v-seconds)), - ) + return time.Unix(0, int64(1e9*v)) } func (f FieldUnixTimeReadonly) validate() error { diff --git a/generated.go b/generated.go index 60b5a46..31233ea 100644 --- a/generated.go +++ b/generated.go @@ -61,7 +61,7 @@ func (h AccountHeaders) Validate() error { if err := h.Metadata().validate(); err != nil { return err } - if err := h.QuotaBytes().validate(); err != nil { + if err := h.BytesUsedQuota().validate(); err != nil { return err } if err := h.TempURLKey2().validate(); err != nil { @@ -94,8 +94,8 @@ func (h AccountHeaders) Metadata() FieldMetadata { return FieldMetadata{h, "X-Account-Meta-"} } -//QuotaBytes provides type-safe access to X-Account-Meta-Quota-Bytes headers. -func (h AccountHeaders) QuotaBytes() FieldUint64 { +//BytesUsedQuota provides type-safe access to X-Account-Meta-Quota-Bytes headers. +func (h AccountHeaders) BytesUsedQuota() FieldUint64 { return FieldUint64{h, "X-Account-Meta-Quota-Bytes"} } diff --git a/generated.go.in b/generated.go.in index 808c950..680b551 100644 --- a/generated.go.in +++ b/generated.go.in @@ -4,7 +4,7 @@ { "Header": "X-Account-Bytes-Used", "Attribute": "BytesUsed", "Type": "Uint64Readonly" }, { "Header": "X-Account-Container-Count", "Attribute": "ContainerCount", "Type": "Uint64Readonly" }, { "Header": "X-Account-Meta-", "Attribute": "Metadata", "Type": "Metadata" }, - { "Header": "X-Account-Meta-Quota-Bytes", "Attribute": "QuotaBytes", "Type": "Uint64" }, + { "Header": "X-Account-Meta-Quota-Bytes", "Attribute": "BytesUsedQuota", "Type": "Uint64" }, { "Header": "X-Account-Meta-Temp-URL-Key-2", "Attribute": "TempURLKey2", "Type": "String" }, { "Header": "X-Account-Meta-Temp-URL-Key", "Attribute": "TempURLKey", "Type": "String" }, { "Header": "X-Account-Object-Count", "Attribute": "ObjectCount", "Type": "Uint64Readonly" }, diff --git a/headers_test.go b/headers_test.go index f8b604c..d068623 100644 --- a/headers_test.go +++ b/headers_test.go @@ -36,7 +36,7 @@ func TestParseAccountHeadersSuccess(t *testing.T) { expectUint64(t, headers.BytesUsed().Get(), 1234) expectUint64(t, headers.ContainerCount().Get(), 23) expectUint64(t, headers.ObjectCount().Get(), 42) - expectUint64(t, headers.QuotaBytes().Get(), 1048576) + expectUint64(t, headers.BytesUsedQuota().Get(), 1048576) expectString(t, headers.Metadata().Get("foo"), "bar") expectString(t, headers.Metadata().Get("Foo"), "bar") @@ -44,44 +44,3 @@ func TestParseAccountHeadersSuccess(t *testing.T) { } //TODO TestParseAccountHeadersError - -func expectBool(t *testing.T, actual bool, expected bool) { - t.Helper() - if actual != expected { - t.Errorf("expected value %#v, got %#v instead\n", expected, actual) - } -} - -func expectUint64(t *testing.T, actual uint64, expected uint64) { - t.Helper() - if actual != expected { - t.Errorf("expected value %d, got %d instead\n", expected, actual) - } -} - -func expectString(t *testing.T, actual string, expected string) { - t.Helper() - if actual != expected { - t.Errorf("expected value %q, got %q instead\n", expected, actual) - } -} - -func expectError(t *testing.T, actual error, expected string) (ok bool) { - t.Helper() - if actual == nil { - if expected != "" { - t.Errorf("expected error %q, got no error\n", expected) - return false - } - } else { - if expected == "" { - t.Errorf("expected no error, got %q\n", actual.Error()) - return false - } else if expected != actual.Error() { - t.Errorf("expected error %q, got %q instead\n", expected, actual.Error()) - return false - } - } - - return true -} diff --git a/shared_test.go b/shared_test.go index bc67320..04481d4 100644 --- a/shared_test.go +++ b/shared_test.go @@ -19,6 +19,9 @@ package schwift import ( + "crypto/rand" + "encoding/hex" + "math" "os" "testing" @@ -72,3 +75,108 @@ func testWithAccount(t *testing.T, testCode func(a *Account)) { } testCode(account) } + +func testWithContainer(t *testing.T, testCode func(c *Container)) { + testWithAccount(t, func(a *Account) { + containerName := getRandomName() + container, err := a.Container(containerName).EnsureExists() + expectError(t, err, "") + + testCode(container) + + //cleanup + exists, err := container.Exists() + expectError(t, err, "") + if exists { + err = container.Delete(nil, nil) + expectError(t, err, "") + } + }) +} + +//////////////////////////////////////////////////////////////////////////////// + +func getRandomName() string { + var buf [16]byte + _, err := rand.Read(buf[:]) + if err != nil { + panic(err.Error()) + } + return hex.EncodeToString(buf[:]) +} + +//////////////////////////////////////////////////////////////////////////////// + +func expectBool(t *testing.T, actual bool, 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) { + t.Helper() + if math.Abs((actual-expected)/expected) > 1e-8 { + t.Errorf("expected value %g, got %g instead\n", expected, actual) + } +} + +func expectUint64(t *testing.T, actual uint64, expected uint64) { + t.Helper() + if actual != expected { + t.Errorf("expected value %d, got %d instead\n", expected, actual) + } +} + +func expectString(t *testing.T, actual string, expected string) { + t.Helper() + if actual != expected { + t.Errorf("expected value %q, got %q instead\n", expected, actual) + } +} + +func expectError(t *testing.T, actual error, expected string) (ok bool) { + t.Helper() + if actual == nil { + if expected != "" { + t.Errorf("expected error %q, got no error\n", expected) + return false + } + } else { + if expected == "" { + t.Errorf("expected no error, got %q\n", actual.Error()) + return false + } else if expected != actual.Error() { + t.Errorf("expected error %q, got %q instead\n", expected, actual.Error()) + return false + } + } + + return true +} + +func expectHeaders(t *testing.T, actual map[string]string, expected map[string]string) { + t.Helper() + reported := make(map[string]bool) + + for k, av := range actual { + ev, exists := expected[k] + if !exists { + ev = "" + } + if av != ev { + t.Errorf(`expected "%s: %s", got "%s: %s" instead`, k, ev, k, av) + reported[k] = true + } + } + + for k, ev := range expected { + av, exists := actual[k] + if !exists { + av = "" + } + if av != ev && !reported[k] { + t.Errorf(`expected "%s: %s", got "%s: %s" instead`, k, ev, k, av) + } + } +} -- cgit v1.2.3