distribution/vendor/github.com/ncw/swift/swift_test.go

1594 lines
37 KiB
Go
Raw Normal View History

// This tests the swift packagae
//
// It can be used with a real swift server which should be set up in
// the environment variables SWIFT_API_USER, SWIFT_API_KEY and
// SWIFT_AUTH_URL
// In case those variables are not defined, a fake Swift server
// is used instead - see Testing in README.md for more info
//
// The functions are designed to run in order and create things the
// next function tests. This means that if it goes wrong it is likely
// errors will propagate. You may need to tidy up the CONTAINER to
// get it to run cleanly.
package swift_test
import (
"archive/tar"
"bytes"
"crypto/md5"
"crypto/tls"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/ncw/swift"
"github.com/ncw/swift/swifttest"
)
var (
c *swift.Connection
srv *swifttest.SwiftServer
m1 = swift.Metadata{"Hello": "1", "potato-Salad": "2"}
m2 = swift.Metadata{"hello": "", "potato-salad": ""}
skipVersionTests = false
)
const (
CONTAINER = "GoSwiftUnitTest"
VERSIONS_CONTAINER = "GoSwiftUnitTestVersions"
CURRENT_CONTAINER = "GoSwiftUnitTestCurrent"
OBJECT = "test_object"
OBJECT2 = "test_object2"
EMPTYOBJECT = "empty_test_object"
CONTENTS = "12345"
CONTENTS2 = "54321"
CONTENT_SIZE = int64(len(CONTENTS))
CONTENT_MD5 = "827ccb0eea8a706c4c34a16891f84e7b"
EMPTY_MD5 = "d41d8cd98f00b204e9800998ecf8427e"
SECRET_KEY = "b3968d0207b54ece87cccc06515a89d4"
)
type someTransport struct{ http.Transport }
func makeConnection() (*swift.Connection, error) {
var err error
UserName := os.Getenv("SWIFT_API_USER")
ApiKey := os.Getenv("SWIFT_API_KEY")
AuthUrl := os.Getenv("SWIFT_AUTH_URL")
Region := os.Getenv("SWIFT_REGION_NAME")
Insecure := os.Getenv("SWIFT_AUTH_INSECURE")
ConnectionChannelTimeout := os.Getenv("SWIFT_CONNECTION_CHANNEL_TIMEOUT")
DataChannelTimeout := os.Getenv("SWIFT_DATA_CHANNEL_TIMEOUT")
if UserName == "" || ApiKey == "" || AuthUrl == "" {
if srv != nil {
srv.Close()
}
srv, err = swifttest.NewSwiftServer("localhost")
if err != nil {
return nil, err
}
UserName = "swifttest"
ApiKey = "swifttest"
AuthUrl = srv.AuthURL
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
MaxIdleConnsPerHost: 2048,
}
if Insecure == "1" {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
c := swift.Connection{
UserName: UserName,
ApiKey: ApiKey,
AuthUrl: AuthUrl,
Region: Region,
Transport: transport,
ConnectTimeout: 60 * time.Second,
Timeout: 60 * time.Second,
}
var timeout int64
if ConnectionChannelTimeout != "" {
timeout, err = strconv.ParseInt(ConnectionChannelTimeout, 10, 32)
if err == nil {
c.ConnectTimeout = time.Duration(timeout) * time.Second
}
}
if DataChannelTimeout != "" {
timeout, err = strconv.ParseInt(DataChannelTimeout, 10, 32)
if err == nil {
c.Timeout = time.Duration(timeout) * time.Second
}
}
return &c, nil
}
func isV3Api() bool {
AuthUrl := os.Getenv("SWIFT_AUTH_URL")
return strings.Contains(AuthUrl, "v3")
}
func TestTransport(t *testing.T) {
var err error
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
tr := &someTransport{
Transport: http.Transport{
MaxIdleConnsPerHost: 2048,
},
}
Insecure := os.Getenv("SWIFT_AUTH_INSECURE")
if Insecure == "1" {
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
c.Transport = tr
if isV3Api() {
c.Tenant = os.Getenv("SWIFT_TENANT")
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
} else {
c.Tenant = os.Getenv("SWIFT_TENANT")
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
}
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
if srv != nil {
srv.Close()
}
}
// The following Test functions are run in order - this one must come before the others!
func TestV1V2Authenticate(t *testing.T) {
var err error
if isV3Api() {
return
}
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
c.Tenant = os.Getenv("SWIFT_TENANT")
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}
func TestV3AuthenticateWithDomainNameAndTenantId(t *testing.T) {
var err error
if !isV3Api() {
return
}
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}
func TestV3TrustWithTrustId(t *testing.T) {
var err error
if !isV3Api() {
return
}
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
c.TrustId = os.Getenv("SWIFT_TRUST_ID")
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}
func TestV3AuthenticateWithDomainIdAndTenantId(t *testing.T) {
var err error
if !isV3Api() {
return
}
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
c.DomainId = os.Getenv("SWIFT_API_DOMAIN_ID")
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}
func TestV3AuthenticateWithDomainNameAndTenantName(t *testing.T) {
var err error
if !isV3Api() {
return
}
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
c.Tenant = os.Getenv("SWIFT_TENANT")
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}
func TestV3AuthenticateWithDomainIdAndTenantName(t *testing.T) {
var err error
if !isV3Api() {
return
}
c, err = makeConnection()
if err != nil {
t.Fatal("Failed to create server", err)
}
c.Tenant = os.Getenv("SWIFT_TENANT")
c.DomainId = os.Getenv("SWIFT_API_DOMAIN_ID")
err = c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}
// Attempt to trigger a race in authenticate
//
// Run with -race to test
func TestAuthenticateRace(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
err := c.Authenticate()
if err != nil {
t.Fatal("Auth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}()
}
wg.Wait()
}
// Test a connection can be serialized and unserialized with JSON
func TestSerializeConnectionJson(t *testing.T) {
serializedConnection, err := json.Marshal(c)
if err != nil {
t.Fatalf("Failed to serialize connection: %v", err)
}
c2 := new(swift.Connection)
err = json.Unmarshal(serializedConnection, &c2)
if err != nil {
t.Fatalf("Failed to unserialize connection: %v", err)
}
if !c2.Authenticated() {
t.Fatal("Should be authenticated")
}
_, _, err = c2.Account()
if err != nil {
t.Fatalf("Failed to use unserialized connection: %v", err)
}
}
// Test a connection can be serialized and unserialized with XML
func TestSerializeConnectionXml(t *testing.T) {
serializedConnection, err := xml.Marshal(c)
if err != nil {
t.Fatalf("Failed to serialize connection: %v", err)
}
c2 := new(swift.Connection)
err = xml.Unmarshal(serializedConnection, &c2)
if err != nil {
t.Fatalf("Failed to unserialize connection: %v", err)
}
if !c2.Authenticated() {
t.Fatal("Should be authenticated")
}
_, _, err = c2.Account()
if err != nil {
t.Fatalf("Failed to use unserialized connection: %v", err)
}
}
// Test the reauthentication logic
func TestOnReAuth(t *testing.T) {
c2 := c
c2.UnAuthenticate()
_, _, err := c2.Account()
if err != nil {
t.Fatalf("Failed to reauthenticate: %v", err)
}
}
func TestAccount(t *testing.T) {
info, headers, err := c.Account()
if err != nil {
t.Fatal(err)
}
if headers["X-Account-Container-Count"] != fmt.Sprintf("%d", info.Containers) {
t.Error("Bad container count")
}
if headers["X-Account-Bytes-Used"] != fmt.Sprintf("%d", info.BytesUsed) {
t.Error("Bad bytes count")
}
if headers["X-Account-Object-Count"] != fmt.Sprintf("%d", info.Objects) {
t.Error("Bad objects count")
}
//fmt.Println(info)
//fmt.Println(headers)
}
func compareMaps(t *testing.T, a, b map[string]string) {
if len(a) != len(b) {
t.Error("Maps different sizes", a, b)
}
for ka, va := range a {
if vb, ok := b[ka]; !ok || va != vb {
t.Error("Difference in key", ka, va, b[ka])
}
}
for kb, vb := range b {
if va, ok := a[kb]; !ok || vb != va {
t.Error("Difference in key", kb, vb, a[kb])
}
}
}
func TestAccountUpdate(t *testing.T) {
err := c.AccountUpdate(m1.AccountHeaders())
if err != nil {
t.Fatal(err)
}
_, headers, err := c.Account()
if err != nil {
t.Fatal(err)
}
m := headers.AccountMetadata()
delete(m, "temp-url-key") // remove X-Account-Meta-Temp-URL-Key if set
compareMaps(t, m, map[string]string{"hello": "1", "potato-salad": "2"})
err = c.AccountUpdate(m2.AccountHeaders())
if err != nil {
t.Fatal(err)
}
_, headers, err = c.Account()
if err != nil {
t.Fatal(err)
}
m = headers.AccountMetadata()
delete(m, "temp-url-key") // remove X-Account-Meta-Temp-URL-Key if set
compareMaps(t, m, map[string]string{})
//fmt.Println(c.Account())
//fmt.Println(headers)
//fmt.Println(headers.AccountMetadata())
//fmt.Println(c.AccountUpdate(m2.AccountHeaders()))
//fmt.Println(c.Account())
}
func TestContainerCreate(t *testing.T) {
err := c.ContainerCreate(CONTAINER, m1.ContainerHeaders())
if err != nil {
t.Fatal(err)
}
}
func TestContainer(t *testing.T) {
info, headers, err := c.Container(CONTAINER)
if err != nil {
t.Fatal(err)
}
compareMaps(t, headers.ContainerMetadata(), map[string]string{"hello": "1", "potato-salad": "2"})
if CONTAINER != info.Name {
t.Error("Bad container count")
}
if headers["X-Container-Bytes-Used"] != fmt.Sprintf("%d", info.Bytes) {
t.Error("Bad bytes count")
}
if headers["X-Container-Object-Count"] != fmt.Sprintf("%d", info.Count) {
t.Error("Bad objects count")
}
//fmt.Println(info)
//fmt.Println(headers)
}
func TestContainersAll(t *testing.T) {
containers1, err := c.ContainersAll(nil)
if err != nil {
t.Fatal(err)
}
containers2, err := c.Containers(nil)
if err != nil {
t.Fatal(err)
}
if len(containers1) != len(containers2) {
t.Fatal("Wrong length")
}
for i := range containers1 {
if containers1[i] != containers2[i] {
t.Fatal("Not the same")
}
}
}
func TestContainersAllWithLimit(t *testing.T) {
containers1, err := c.ContainersAll(&swift.ContainersOpts{Limit: 1})
if err != nil {
t.Fatal(err)
}
containers2, err := c.Containers(nil)
if err != nil {
t.Fatal(err)
}
if len(containers1) != len(containers2) {
t.Fatal("Wrong length")
}
for i := range containers1 {
if containers1[i] != containers2[i] {
t.Fatal("Not the same")
}
}
}
func TestContainerUpdate(t *testing.T) {
err := c.ContainerUpdate(CONTAINER, m2.ContainerHeaders())
if err != nil {
t.Fatal(err)
}
_, headers, err := c.Container(CONTAINER)
if err != nil {
t.Fatal(err)
}
compareMaps(t, headers.ContainerMetadata(), map[string]string{})
//fmt.Println(headers)
}
func TestContainerNames(t *testing.T) {
containers, err := c.ContainerNames(nil)
if err != nil {
t.Fatal(err)
}
// fmt.Printf("container %q\n", CONTAINER)
ok := false
for _, container := range containers {
if container == CONTAINER {
ok = true
break
}
}
if !ok {
t.Errorf("Didn't find container %q in listing %q", CONTAINER, containers)
}
// fmt.Println(containers)
}
func TestContainerNamesAll(t *testing.T) {
containers1, err := c.ContainerNamesAll(nil)
if err != nil {
t.Fatal(err)
}
containers2, err := c.ContainerNames(nil)
if err != nil {
t.Fatal(err)
}
if len(containers1) != len(containers2) {
t.Fatal("Wrong length")
}
for i := range containers1 {
if containers1[i] != containers2[i] {
t.Fatal("Not the same")
}
}
}
func TestContainerNamesAllWithLimit(t *testing.T) {
containers1, err := c.ContainerNamesAll(&swift.ContainersOpts{Limit: 1})
if err != nil {
t.Fatal(err)
}
containers2, err := c.ContainerNames(nil)
if err != nil {
t.Fatal(err)
}
if len(containers1) != len(containers2) {
t.Fatal("Wrong length")
}
for i := range containers1 {
if containers1[i] != containers2[i] {
t.Fatal("Not the same")
}
}
}
func TestObjectPutString(t *testing.T) {
err := c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "")
if err != nil {
t.Fatal(err)
}
info, _, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Error(err)
}
if info.ContentType != "application/octet-stream" {
t.Error("Bad content type", info.ContentType)
}
if info.Bytes != CONTENT_SIZE {
t.Error("Bad length")
}
if info.Hash != CONTENT_MD5 {
t.Error("Bad length")
}
}
func TestObjectPut(t *testing.T) {
headers := swift.Headers{}
// Set content size incorrectly - should produce an error
headers["Content-Length"] = strconv.FormatInt(CONTENT_SIZE-1, 10)
contents := bytes.NewBufferString(CONTENTS)
h, err := c.ObjectPut(CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers)
if err == nil {
t.Fatal("Expecting error but didn't get one")
}
// Now set content size correctly
contents = bytes.NewBufferString(CONTENTS)
headers["Content-Length"] = strconv.FormatInt(CONTENT_SIZE, 10)
h, err = c.ObjectPut(CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers)
if err != nil {
t.Fatal(err)
}
if h["Etag"] != CONTENT_MD5 {
t.Errorf("Bad Etag want %q got %q", CONTENT_MD5, h["Etag"])
}
// Fetch object info and compare
info, _, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Error(err)
}
if info.ContentType != "text/plain" {
t.Error("Bad content type", info.ContentType)
}
if info.Bytes != CONTENT_SIZE {
t.Error("Bad length")
}
if info.Hash != CONTENT_MD5 {
t.Error("Bad length")
}
}
func TestObjectEmpty(t *testing.T) {
err := c.ObjectPutString(CONTAINER, EMPTYOBJECT, "", "")
if err != nil {
t.Fatal(err)
}
info, _, err := c.Object(CONTAINER, EMPTYOBJECT)
if err != nil {
t.Error(err)
}
if info.ContentType != "application/octet-stream" {
t.Error("Bad content type", info.ContentType)
}
if info.Bytes != 0 {
t.Errorf("Bad length want 0 got %v", info.Bytes)
}
if info.Hash != EMPTY_MD5 {
t.Errorf("Bad MD5 want %v got %v", EMPTY_MD5, info.Hash)
}
// Tidy up
err = c.ObjectDelete(CONTAINER, EMPTYOBJECT)
if err != nil {
t.Error(err)
}
}
func TestObjectPutBytes(t *testing.T) {
err := c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "")
if err != nil {
t.Fatal(err)
}
info, _, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Error(err)
}
if info.ContentType != "application/octet-stream" {
t.Error("Bad content type", info.ContentType)
}
if info.Bytes != CONTENT_SIZE {
t.Error("Bad length")
}
if info.Hash != CONTENT_MD5 {
t.Error("Bad length")
}
}
func TestObjectPutMimeType(t *testing.T) {
err := c.ObjectPutString(CONTAINER, "test.jpg", CONTENTS, "")
if err != nil {
t.Fatal(err)
}
info, _, err := c.Object(CONTAINER, "test.jpg")
if err != nil {
t.Error(err)
}
if info.ContentType != "image/jpeg" {
t.Error("Bad content type", info.ContentType)
}
// Tidy up
err = c.ObjectDelete(CONTAINER, "test.jpg")
if err != nil {
t.Error(err)
}
}
func TestObjectCreate(t *testing.T) {
out, err := c.ObjectCreate(CONTAINER, OBJECT2, true, "", "", nil)
if err != nil {
t.Fatal(err)
}
buf := &bytes.Buffer{}
hash := md5.New()
out2 := io.MultiWriter(out, buf, hash)
for i := 0; i < 100; i++ {
fmt.Fprintf(out2, "%d %s\n", i, CONTENTS)
}
err = out.Close()
if err != nil {
t.Error(err)
}
expected := buf.String()
contents, err := c.ObjectGetString(CONTAINER, OBJECT2)
if err != nil {
t.Error(err)
}
if contents != expected {
t.Error("Contents wrong")
}
// Test writing on closed file
n, err := out.Write([]byte{0})
if err == nil || n != 0 {
t.Error("Expecting error and n == 0 writing on closed file", err, n)
}
// Now with hash instead
out, err = c.ObjectCreate(CONTAINER, OBJECT2, false, fmt.Sprintf("%x", hash.Sum(nil)), "", nil)
if err != nil {
t.Fatal(err)
}
_, err = out.Write(buf.Bytes())
if err != nil {
t.Error(err)
}
err = out.Close()
if err != nil {
t.Error(err)
}
contents, err = c.ObjectGetString(CONTAINER, OBJECT2)
if err != nil {
t.Error(err)
}
if contents != expected {
t.Error("Contents wrong")
}
// Now with bad hash
out, err = c.ObjectCreate(CONTAINER, OBJECT2, false, CONTENT_MD5, "", nil)
if err != nil {
t.Fatal(err)
}
// FIXME: work around bug which produces 503 not 422 for empty corrupted files
fmt.Fprintf(out, "Sausage")
err = out.Close()
if err != swift.ObjectCorrupted {
t.Error("Expecting object corrupted not", err)
}
// Tidy up
err = c.ObjectDelete(CONTAINER, OBJECT2)
if err != nil {
t.Error(err)
}
}
func TestObjectGetString(t *testing.T) {
contents, err := c.ObjectGetString(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
if contents != CONTENTS {
t.Error("Contents wrong")
}
//fmt.Println(contents)
}
func TestObjectGetBytes(t *testing.T) {
contents, err := c.ObjectGetBytes(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
if string(contents) != CONTENTS {
t.Error("Contents wrong")
}
//fmt.Println(contents)
}
func TestObjectOpen(t *testing.T) {
file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil)
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
n, err := io.Copy(&buf, file)
if err != nil {
t.Fatal(err)
}
if n != CONTENT_SIZE {
t.Fatal("Wrong length", n, CONTENT_SIZE)
}
if buf.String() != CONTENTS {
t.Error("Contents wrong")
}
err = file.Close()
if err != nil {
t.Fatal(err)
}
}
func TestObjectOpenPartial(t *testing.T) {
file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil)
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
n, err := io.CopyN(&buf, file, 1)
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Fatal("Wrong length", n, CONTENT_SIZE)
}
if buf.String() != CONTENTS[:1] {
t.Error("Contents wrong")
}
err = file.Close()
if err != nil {
t.Fatal(err)
}
}
func TestObjectOpenLength(t *testing.T) {
file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil)
if err != nil {
t.Fatal(err)
}
// FIXME ideally this would check both branches of the Length() code
n, err := file.Length()
if err != nil {
t.Fatal(err)
}
if n != CONTENT_SIZE {
t.Fatal("Wrong length", n, CONTENT_SIZE)
}
err = file.Close()
if err != nil {
t.Fatal(err)
}
}
func TestObjectOpenSeek(t *testing.T) {
plan := []struct {
whence int
offset int64
result int64
}{
{-1, 0, 0},
{-1, 0, 1},
{-1, 0, 2},
{0, 0, 0},
{0, 0, 0},
{0, 1, 1},
{0, 2, 2},
{1, 0, 3},
{1, -2, 2},
{1, 1, 4},
{2, -1, 4},
{2, -3, 2},
{2, -2, 3},
{2, -5, 0},
{2, -4, 1},
}
file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil)
if err != nil {
t.Fatal(err)
}
for _, p := range plan {
if p.whence >= 0 {
result, err := file.Seek(p.offset, p.whence)
if err != nil {
t.Fatal(err, p)
}
if result != p.result {
t.Fatal("Seek result was", result, "expecting", p.result, p)
}
}
var buf bytes.Buffer
n, err := io.CopyN(&buf, file, 1)
if err != nil {
t.Fatal(err, p)
}
if n != 1 {
t.Fatal("Wrong length", n, p)
}
actual := buf.String()
expected := CONTENTS[p.result : p.result+1]
if actual != expected {
t.Error("Contents wrong, expecting", expected, "got", actual, p)
}
}
err = file.Close()
if err != nil {
t.Fatal(err)
}
}
func TestObjectUpdate(t *testing.T) {
err := c.ObjectUpdate(CONTAINER, OBJECT, m1.ObjectHeaders())
if err != nil {
t.Fatal(err)
}
}
func checkTime(t *testing.T, when time.Time, low, high int) {
dt := time.Now().Sub(when)
if dt < time.Duration(low)*time.Second || dt > time.Duration(high)*time.Second {
t.Errorf("Time is wrong: dt=%q, when=%q", dt, when)
}
}
func TestObject(t *testing.T) {
object, headers, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "1", "potato-salad": "2"})
if object.Name != OBJECT || object.Bytes != CONTENT_SIZE || object.ContentType != "application/octet-stream" || object.Hash != CONTENT_MD5 || object.PseudoDirectory != false || object.SubDir != "" {
t.Error("Bad object info", object)
}
checkTime(t, object.LastModified, -10, 10)
}
func TestObjectUpdate2(t *testing.T) {
err := c.ObjectUpdate(CONTAINER, OBJECT, m2.ObjectHeaders())
if err != nil {
t.Fatal(err)
}
_, headers, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
//fmt.Println(headers, headers.ObjectMetadata())
compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "", "potato-salad": ""})
}
func TestContainers(t *testing.T) {
containers, err := c.Containers(nil)
if err != nil {
t.Fatal(err)
}
ok := false
for _, container := range containers {
if container.Name == CONTAINER {
ok = true
// Container may or may not have the file contents in it
// Swift updates may be behind
if container.Count == 0 && container.Bytes == 0 {
break
}
if container.Count == 1 && container.Bytes == CONTENT_SIZE {
break
}
t.Errorf("Bad size of Container %q: %q", CONTAINER, container)
break
}
}
if !ok {
t.Errorf("Didn't find container %q in listing %q", CONTAINER, containers)
}
//fmt.Println(containers)
}
func TestObjectNames(t *testing.T) {
objects, err := c.ObjectNames(CONTAINER, nil)
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0] != OBJECT {
t.Error("Incorrect listing", objects)
}
//fmt.Println(objects)
}
func TestObjectNamesAll(t *testing.T) {
objects, err := c.ObjectNamesAll(CONTAINER, nil)
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0] != OBJECT {
t.Error("Incorrect listing", objects)
}
//fmt.Println(objects)
}
func TestObjectNamesAllWithLimit(t *testing.T) {
objects, err := c.ObjectNamesAll(CONTAINER, &swift.ObjectsOpts{Limit: 1})
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0] != OBJECT {
t.Error("Incorrect listing", objects)
}
//fmt.Println(objects)
}
func TestObjectsWalk(t *testing.T) {
objects := make([]string, 0)
err := c.ObjectsWalk(container, nil, func(opts *swift.ObjectsOpts) (interface{}, error) {
newObjects, err := c.ObjectNames(CONTAINER, opts)
if err == nil {
objects = append(objects, newObjects...)
}
return newObjects, err
})
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0] != OBJECT {
t.Error("Incorrect listing", objects)
}
//fmt.Println(objects)
}
func TestObjects(t *testing.T) {
objects, err := c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/'})
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 {
t.Fatal("Should only be 1 object")
}
object := objects[0]
if object.Name != OBJECT || object.Bytes != CONTENT_SIZE || object.ContentType != "application/octet-stream" || object.Hash != CONTENT_MD5 || object.PseudoDirectory != false || object.SubDir != "" {
t.Error("Bad object info", object)
}
checkTime(t, object.LastModified, -10, 10)
// fmt.Println(objects)
}
func TestObjectsDirectory(t *testing.T) {
err := c.ObjectPutString(CONTAINER, "directory", "", "application/directory")
if err != nil {
t.Fatal(err)
}
defer c.ObjectDelete(CONTAINER, "directory")
// Look for the directory object and check we aren't confusing
// it with a pseudo directory object
objects, err := c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/'})
if err != nil {
t.Fatal(err)
}
if len(objects) != 2 {
t.Fatal("Should only be 2 objects")
}
found := false
for i := range objects {
object := objects[i]
if object.Name == "directory" {
found = true
if object.Bytes != 0 || object.ContentType != "application/directory" || object.Hash != "d41d8cd98f00b204e9800998ecf8427e" || object.PseudoDirectory != false || object.SubDir != "" {
t.Error("Bad object info", object)
}
checkTime(t, object.LastModified, -10, 10)
}
}
if !found {
t.Error("Didn't find directory object")
}
// fmt.Println(objects)
}
func TestObjectsPseudoDirectory(t *testing.T) {
err := c.ObjectPutString(CONTAINER, "directory/puppy.jpg", "cute puppy", "")
if err != nil {
t.Fatal(err)
}
defer c.ObjectDelete(CONTAINER, "directory/puppy.jpg")
// Look for the pseudo directory
objects, err := c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/'})
if err != nil {
t.Fatal(err)
}
if len(objects) != 2 {
t.Fatal("Should only be 2 objects", objects)
}
found := false
for i := range objects {
object := objects[i]
if object.Name == "directory/" {
found = true
if object.Bytes != 0 || object.ContentType != "application/directory" || object.Hash != "" || object.PseudoDirectory != true || object.SubDir != "directory/" && object.LastModified.IsZero() {
t.Error("Bad object info", object)
}
}
}
if !found {
t.Error("Didn't find directory object", objects)
}
// Look in the pseudo directory now
objects, err = c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: "directory/"})
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 {
t.Fatal("Should only be 1 object", objects)
}
object := objects[0]
if object.Name != "directory/puppy.jpg" || object.Bytes != 10 || object.ContentType != "image/jpeg" || object.Hash != "87a12ea22fca7f54f0cefef1da535489" || object.PseudoDirectory != false || object.SubDir != "" {
t.Error("Bad object info", object)
}
checkTime(t, object.LastModified, -10, 10)
// fmt.Println(objects)
}
func TestObjectsAll(t *testing.T) {
objects, err := c.ObjectsAll(CONTAINER, nil)
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0].Name != OBJECT {
t.Error("Incorrect listing", objects)
}
//fmt.Println(objects)
}
func TestObjectsAllWithLimit(t *testing.T) {
objects, err := c.ObjectsAll(CONTAINER, &swift.ObjectsOpts{Limit: 1})
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0].Name != OBJECT {
t.Error("Incorrect listing", objects)
}
//fmt.Println(objects)
}
func TestObjectNamesWithPath(t *testing.T) {
objects, err := c.ObjectNames(CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: ""})
if err != nil {
t.Fatal(err)
}
if len(objects) != 1 || objects[0] != OBJECT {
t.Error("Bad listing with path", objects)
}
// fmt.Println(objects)
objects, err = c.ObjectNames(CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: "Downloads/"})
if err != nil {
t.Fatal(err)
}
if len(objects) != 0 {
t.Error("Bad listing with path", objects)
}
// fmt.Println(objects)
}
func TestObjectCopy(t *testing.T) {
_, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, OBJECT2, nil)
if err != nil {
t.Fatal(err)
}
err = c.ObjectDelete(CONTAINER, OBJECT2)
if err != nil {
t.Fatal(err)
}
}
func TestObjectCopyWithMetadata(t *testing.T) {
m := swift.Metadata{}
m["copy-special-metadata"] = "hello"
m["hello"] = "3"
h := m.ObjectHeaders()
h["Content-Type"] = "image/jpeg"
_, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, OBJECT2, h)
if err != nil {
t.Fatal(err)
}
// Re-read the metadata to see if it is correct
_, headers, err := c.Object(CONTAINER, OBJECT2)
if err != nil {
t.Fatal(err)
}
if headers["Content-Type"] != "image/jpeg" {
t.Error("Didn't change content type")
}
compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "3", "potato-salad": "", "copy-special-metadata": "hello"})
err = c.ObjectDelete(CONTAINER, OBJECT2)
if err != nil {
t.Fatal(err)
}
}
func TestObjectMove(t *testing.T) {
err := c.ObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2)
if err != nil {
t.Fatal(err)
}
testExistenceAfterDelete(t, CONTAINER, OBJECT)
_, _, err = c.Object(CONTAINER, OBJECT2)
if err != nil {
t.Fatal(err)
}
err = c.ObjectMove(CONTAINER, OBJECT2, CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
testExistenceAfterDelete(t, CONTAINER, OBJECT2)
_, headers, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "", "potato-salad": ""})
}
func TestObjectUpdateContentType(t *testing.T) {
err := c.ObjectUpdateContentType(CONTAINER, OBJECT, "text/potato")
if err != nil {
t.Fatal(err)
}
// Re-read the metadata to see if it is correct
_, headers, err := c.Object(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
if headers["Content-Type"] != "text/potato" {
t.Error("Didn't change content type")
}
compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "", "potato-salad": ""})
}
func TestVersionContainerCreate(t *testing.T) {
if err := c.VersionContainerCreate(CURRENT_CONTAINER, VERSIONS_CONTAINER); err != nil {
if err == swift.Forbidden {
t.Log("Server doesn't support Versions - skipping test")
skipVersionTests = true
return
}
t.Fatal(err)
}
}
func TestVersionObjectAdd(t *testing.T) {
if skipVersionTests {
t.Log("Server doesn't support Versions - skipping test")
return
}
// Version 1
if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS, ""); err != nil {
t.Fatal(err)
}
if contents, err := c.ObjectGetString(CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
} else if contents != CONTENTS {
t.Error("Contents wrong")
}
// Version 2
if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil {
t.Fatal(err)
}
if contents, err := c.ObjectGetString(CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
} else if contents != CONTENTS2 {
t.Error("Contents wrong")
}
// Version 3
if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil {
t.Fatal(err)
}
}
func TestVersionObjectList(t *testing.T) {
if skipVersionTests {
t.Log("Server doesn't support Versions - skipping test")
return
}
list, err := c.VersionObjectList(VERSIONS_CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
if len(list) != 2 {
t.Error("Version list should return 2 objects")
}
//fmt.Print(list)
}
func TestVersionObjectDelete(t *testing.T) {
if skipVersionTests {
t.Log("Server doesn't support Versions - skipping test")
return
}
// Delete Version 3
if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
}
// Delete Version 2
if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
}
// Contents should be reverted to Version 1
if contents, err := c.ObjectGetString(CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
} else if contents != CONTENTS {
t.Error("Contents wrong")
}
}
// cleanUpContainer deletes everything in the container and then the
// container. It expects the container to be empty and if it wasn't
// it logs an error.
func cleanUpContainer(t *testing.T, container string) {
objects, err := c.Objects(container, nil)
if err != nil {
t.Error(err, container)
} else {
if len(objects) != 0 {
t.Error("Container not empty", container)
}
for _, object := range objects {
t.Log("Deleting spurious", object.Name)
err = c.ObjectDelete(container, object.Name)
if err != nil {
t.Error(err, container)
}
}
}
if err := c.ContainerDelete(container); err != nil {
t.Error(err, container)
}
}
func TestVersionDeleteContent(t *testing.T) {
if skipVersionTests {
t.Log("Server doesn't support Versions - skipping test")
} else {
// Delete Version 1
if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil {
t.Fatal(err)
}
}
cleanUpContainer(t, VERSIONS_CONTAINER)
cleanUpContainer(t, CURRENT_CONTAINER)
}
// Check for non existence after delete
// May have to do it a few times to wait for swift to be consistent.
func testExistenceAfterDelete(t *testing.T, container, object string) {
for i := 10; i <= 0; i-- {
_, _, err := c.Object(container, object)
if err == swift.ObjectNotFound {
break
}
if i == 0 {
t.Fatalf("Expecting object %q/%q not found not: err=%v", container, object, err)
}
time.Sleep(1 * time.Second)
}
}
func TestObjectDelete(t *testing.T) {
err := c.ObjectDelete(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
testExistenceAfterDelete(t, CONTAINER, OBJECT)
err = c.ObjectDelete(CONTAINER, OBJECT)
if err != swift.ObjectNotFound {
t.Fatal("Expecting Object not found", err)
}
}
func TestBulkDelete(t *testing.T) {
result, err := c.BulkDelete(CONTAINER, []string{OBJECT})
if err == swift.Forbidden {
t.Log("Server doesn't support BulkDelete - skipping test")
return
}
if err != nil {
t.Fatal(err)
}
if result.NumberNotFound != 1 {
t.Error("Expected 1, actual:", result.NumberNotFound)
}
if result.NumberDeleted != 0 {
t.Error("Expected 0, actual:", result.NumberDeleted)
}
err = c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "")
if err != nil {
t.Fatal(err)
}
result, err = c.BulkDelete(CONTAINER, []string{OBJECT2, OBJECT})
if err != nil {
t.Fatal(err)
}
if result.NumberNotFound != 1 {
t.Error("Expected 1, actual:", result.NumberNotFound)
}
if result.NumberDeleted != 1 {
t.Error("Expected 1, actual:", result.NumberDeleted)
}
t.Log("Errors:", result.Errors)
}
func TestBulkUpload(t *testing.T) {
buffer := new(bytes.Buffer)
ds := tar.NewWriter(buffer)
var files = []struct{ Name, Body string }{
{OBJECT, CONTENTS},
{OBJECT2, CONTENTS2},
}
for _, file := range files {
hdr := &tar.Header{
Name: file.Name,
Size: int64(len(file.Body)),
}
if err := ds.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if _, err := ds.Write([]byte(file.Body)); err != nil {
t.Fatal(err)
}
}
if err := ds.Close(); err != nil {
t.Fatal(err)
}
result, err := c.BulkUpload(CONTAINER, buffer, swift.UploadTar, nil)
if err == swift.Forbidden {
t.Log("Server doesn't support BulkUpload - skipping test")
return
}
if err != nil {
t.Fatal(err)
}
if result.NumberCreated != 2 {
t.Error("Expected 2, actual:", result.NumberCreated)
}
t.Log("Errors:", result.Errors)
_, _, err = c.Object(CONTAINER, OBJECT)
if err != nil {
t.Error("Expecting object to be found")
}
_, _, err = c.Object(CONTAINER, OBJECT2)
if err != nil {
t.Error("Expecting object to be found")
}
c.ObjectDelete(CONTAINER, OBJECT)
c.ObjectDelete(CONTAINER, OBJECT2)
}
func TestObjectDifficultName(t *testing.T) {
const name = `hello? sausage/êé/Hello, 世界/ " ' @ < > & ?/`
err := c.ObjectPutString(CONTAINER, name, CONTENTS, "")
if err != nil {
t.Fatal(err)
}
objects, err := c.ObjectNamesAll(CONTAINER, nil)
if err != nil {
t.Error(err)
}
found := false
for _, object := range objects {
if object == name {
found = true
break
}
}
if !found {
t.Errorf("Couldn't find %q in listing %q", name, objects)
}
err = c.ObjectDelete(CONTAINER, name)
if err != nil {
t.Fatal(err)
}
}
func TestTempUrl(t *testing.T) {
err := c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "")
if err != nil {
t.Fatal(err)
}
m := swift.Metadata{}
m["temp-url-key"] = SECRET_KEY
err = c.AccountUpdate(m.AccountHeaders())
if err != nil {
t.Fatal(err)
}
expiresTime := time.Now().Add(20 * time.Minute)
tempUrl := c.ObjectTempUrl(CONTAINER, OBJECT, SECRET_KEY, "GET", expiresTime)
resp, err := http.Get(tempUrl)
if err != nil {
t.Fatal("Failed to retrieve file from temporary url")
}
if resp.StatusCode == 401 {
t.Log("Server doesn't support tempurl")
} else if resp.StatusCode != 200 {
t.Fatal("HTTP Error retrieving file from temporary url", resp.StatusCode)
} else {
if content, err := ioutil.ReadAll(resp.Body); err != nil || string(content) != CONTENTS {
t.Error("Bad content", err)
}
resp, err := http.Post(tempUrl, "image/jpeg", bytes.NewReader([]byte(CONTENTS)))
if err != nil {
t.Fatal("Failed to retrieve file from temporary url")
}
if resp.StatusCode != 401 {
t.Fatal("Expecting server to forbid access to object")
}
}
resp.Body.Close()
err = c.ObjectDelete(CONTAINER, OBJECT)
if err != nil {
t.Fatal(err)
}
}
func TestQueryInfo(t *testing.T) {
infos, err := c.QueryInfo()
if err != nil {
t.Log("Server doesn't support querying info")
return
}
if _, ok := infos["swift"]; !ok {
t.Fatal("No 'swift' section found in configuration")
}
}
func TestContainerDelete(t *testing.T) {
err := c.ContainerDelete(CONTAINER)
if err != nil {
t.Fatal(err)
}
err = c.ContainerDelete(CONTAINER)
if err != swift.ContainerNotFound {
t.Fatal("Expecting container not found", err)
}
_, _, err = c.Container(CONTAINER)
if err != swift.ContainerNotFound {
t.Fatal("Expecting container not found", err)
}
}
func TestUnAuthenticate(t *testing.T) {
c.UnAuthenticate()
if c.Authenticated() {
t.Fatal("Shouldn't be authenticated")
}
// Test re-authenticate
err := c.Authenticate()
if err != nil {
t.Fatal("ReAuth failed", err)
}
if !c.Authenticated() {
t.Fatal("Not authenticated")
}
}