// 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/rand" "crypto/tls" "encoding/json" "encoding/xml" "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "os" "reflect" "strconv" "strings" "sync" "testing" "time" "github.com/ncw/swift" "github.com/ncw/swift/swifttest" ) var ( srv *swifttest.SwiftServer m1 = swift.Metadata{"Hello": "1", "potato-Salad": "2"} m2 = swift.Metadata{"hello": "", "potato-salad": ""} skipVersionTests = false ) const ( CONTAINER = "GoSwiftUnitTest" SEGMENTS_CONTAINER = "GoSwiftUnitTest_segments" 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(t *testing.T) (*swift.Connection, func()) { 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") EndpointType := os.Getenv("SWIFT_ENDPOINT_TYPE") Insecure := os.Getenv("SWIFT_AUTH_INSECURE") ConnectionChannelTimeout := os.Getenv("SWIFT_CONNECTION_CHANNEL_TIMEOUT") DataChannelTimeout := os.Getenv("SWIFT_DATA_CHANNEL_TIMEOUT") if UserName == "" || ApiKey == "" || AuthUrl == "" { srv, err = swifttest.NewSwiftServer("localhost") if err != nil && t != nil { t.Fatal("Failed to create server", 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, EndpointType: swift.EndpointType(EndpointType), } 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, func() { if srv != nil { srv.Close() } } } func makeConnectionAuth(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnection(t) err := c.Authenticate() if err != nil { t.Fatal("Auth failed", err) } return c, rollback } func makeConnectionWithContainer(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionAuth(t) err := c.ContainerCreate(CONTAINER, m1.ContainerHeaders()) if err != nil { t.Fatal(err) } return c, func() { c.ContainerDelete(CONTAINER) rollback() } } func makeConnectionWithObject(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionWithContainer(t) err := c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "") if err != nil { t.Fatal(err) } return c, func() { c.ObjectDelete(CONTAINER, OBJECT) rollback() } } func makeConnectionWithObjectHeaders(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionWithObject(t) err := c.ObjectUpdate(CONTAINER, OBJECT, m1.ObjectHeaders()) if err != nil { t.Fatal(err) } return c, rollback } func makeConnectionWithVersionsContainer(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionAuth(t) err := c.VersionContainerCreate(CURRENT_CONTAINER, VERSIONS_CONTAINER) newRollback := func() { c.ContainerDelete(CURRENT_CONTAINER) c.ContainerDelete(VERSIONS_CONTAINER) rollback() } if err != nil { if err == swift.Forbidden { skipVersionTests = true return c, newRollback } t.Fatal(err) } return c, newRollback } func makeConnectionWithVersionsObject(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionWithVersionsContainer(t) if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS, ""); err != nil { t.Fatal(err) } // Version 2 if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { t.Fatal(err) } // Version 3 if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { t.Fatal(err) } return c, func() { for i := 0; i < 3; i++ { c.ObjectDelete(CURRENT_CONTAINER, OBJECT) } rollback() } } func makeConnectionWithSegmentsContainer(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionWithContainer(t) err := c.ContainerCreate(SEGMENTS_CONTAINER, swift.Headers{}) if err != nil { t.Fatal(err) } return c, func() { err = c.ContainerDelete(SEGMENTS_CONTAINER) if err != nil { t.Fatal(err) } rollback() } } func makeConnectionWithDLO(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionWithSegmentsContainer(t) opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } for i := 0; i < 2; i++ { _, err = fmt.Fprintf(out, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } return c, func() { c.DynamicLargeObjectDelete(CONTAINER, OBJECT) rollback() } } func makeConnectionWithSLO(t *testing.T) (*swift.Connection, func()) { c, rollback := makeConnectionWithSegmentsContainer(t) opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.StaticLargeObjectCreate(&opts) if err != nil { if err == swift.SLONotSupported { t.Skip("SLO not supported") return c, rollback } t.Fatal(err) } for i := 0; i < 2; i++ { _, err = fmt.Fprintf(out, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } return c, func() { c.StaticLargeObjectDelete(CONTAINER, OBJECT) rollback() } } func isV3Api() bool { AuthUrl := os.Getenv("SWIFT_AUTH_URL") return strings.Contains(AuthUrl, "v3") } func TestTransport(t *testing.T) { c, rollback := makeConnection(t) defer rollback() 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") } } // The following Test functions are run in order - this one must come before the others! func TestV1V2Authenticate(t *testing.T) { if isV3Api() { return } c, rollback := makeConnection(t) defer rollback() 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) { if !isV3Api() { return } c, rollback := makeConnection(t) defer rollback() 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) { if !isV3Api() { return } c, rollback := makeConnection(t) defer rollback() 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) { if !isV3Api() { return } c, rollback := makeConnection(t) defer rollback() 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) { if !isV3Api() { return } c, rollback := makeConnection(t) defer rollback() 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) { if !isV3Api() { return } c, rollback := makeConnection(t) defer rollback() 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) { c, rollback := makeConnection(t) defer rollback() 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) { c, rollback := makeConnectionAuth(t) defer rollback() 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) { c, rollback := makeConnectionAuth(t) defer rollback() 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) { c, rollback := makeConnectionAuth(t) defer rollback() c.UnAuthenticate() _, _, err := c.Account() if err != nil { t.Fatalf("Failed to reauthenticate: %v", err) } } func TestAccount(t *testing.T) { c, rollback := makeConnectionAuth(t) defer rollback() 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") } } 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) { c, rollback := makeConnectionAuth(t) defer rollback() 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{}) } func TestContainerCreate(t *testing.T) { c, rollback := makeConnectionAuth(t) defer rollback() err := c.ContainerCreate(CONTAINER, m1.ContainerHeaders()) if err != nil { t.Fatal(err) } err = c.ContainerDelete(CONTAINER) if err != nil { t.Fatal(err) } } func TestContainer(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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") } } func TestContainersAll(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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{}) } func TestContainerNames(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() containers, err := c.ContainerNames(nil) if err != nil { t.Fatal(err) } 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) } } func TestContainerNamesAll(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() err := c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "") if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT) 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT) 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() err := c.ObjectPutString(CONTAINER, EMPTYOBJECT, "", "") if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, EMPTYOBJECT) if err != nil { t.Error(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) } } func TestObjectPutBytes(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() err := c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "") if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT) if err != nil { t.Error(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) { c, rollback := makeConnectionWithContainer(t) defer rollback() err := c.ObjectPutString(CONTAINER, "test.jpg", CONTENTS, "") if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, "test.jpg") if err != nil { t.Error(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) } } func TestObjectCreate(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() out, err := c.ObjectCreate(CONTAINER, OBJECT2, true, "", "", nil) if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT2) if err != nil { t.Error(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) } // Ensure Headers fails if called prematurely _, err = out.Headers() if err == nil { t.Error("Headers should fail if called before Close()") } 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.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } // Ensure Headers succeeds when called after a good upload headers, err := out.Headers() if err != nil { t.Error(err) } if len(headers) < 1 { t.Error("The Headers returned by Headers() should not be empty") } // 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.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } // 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) } } func TestObjectGetString(t *testing.T) { c, rollback := makeConnectionWithObject(t) defer rollback() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } if contents != CONTENTS { t.Error("Contents wrong") } } func TestObjectGetBytes(t *testing.T) { c, rollback := makeConnectionWithObject(t) defer rollback() contents, err := c.ObjectGetBytes(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } if string(contents) != CONTENTS { t.Error("Contents wrong") } } func TestObjectOpen(t *testing.T) { c, rollback := makeConnectionWithObject(t) defer rollback() 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) { c, rollback := makeConnectionWithObject(t) defer rollback() 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) { c, rollback := makeConnectionWithObject(t) defer rollback() 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) { c, rollback := makeConnectionWithObject(t) defer rollback() 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 { var result int64 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 var n int64 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) } } // Test seeking to the end to find the file size func TestObjectOpenSeekEnd(t *testing.T) { c, rollback := makeConnectionWithObject(t) defer rollback() file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil) if err != nil { t.Fatal(err) } n, err := file.Seek(0, 2) // seek to end if err != nil { t.Fatal(err) } if n != CONTENT_SIZE { t.Fatal("Wrong offset", n) } // Now check reading returns EOF buf := make([]byte, 16) nn, err := io.ReadFull(file, buf) if err != io.EOF { t.Fatal(err) } if nn != 0 { t.Fatal("wrong length", n) } // Now seek back to start and check we can read the file n, err = file.Seek(0, 0) // seek to start if err != nil { t.Fatal(err) } if n != 0 { t.Fatal("Wrong offset", n) } // read file and check contents buf, err = ioutil.ReadAll(file) if err != nil { t.Fatal(err) } if string(buf) != CONTENTS { t.Fatal("wrong contents", string(buf)) } } func TestObjectUpdate(t *testing.T) { c, rollback := makeConnectionWithObject(t) defer rollback() 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) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "", "potato-salad": ""}) } func TestContainers(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } } func TestObjectNames(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() objects, err := c.ObjectNames(CONTAINER, nil) if err != nil { t.Fatal(err) } if len(objects) != 1 || objects[0] != OBJECT { t.Error("Incorrect listing", objects) } } func TestObjectNamesAll(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() objects, err := c.ObjectNamesAll(CONTAINER, nil) if err != nil { t.Fatal(err) } if len(objects) != 1 || objects[0] != OBJECT { t.Error("Incorrect listing", objects) } } func TestObjectNamesAllWithLimit(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } } func TestObjectsWalk(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } } func TestObjects(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } func TestObjectsDirectory(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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") } } func TestObjectsPseudoDirectory(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } func TestObjectsAll(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } } func TestObjectsAllWithLimit(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } } func TestObjectNamesWithPath(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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) } } func TestObjectCopy(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() _, 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) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() m := swift.Metadata{} m["copy-special-metadata"] = "hello" m["hello"] = "9" h := m.ObjectHeaders() h["Content-Type"] = "image/jpeg" _, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, OBJECT2, h) if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT2) 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": "9", "potato-salad": "2", "copy-special-metadata": "hello"}) } func TestObjectMove(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() err := c.ObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } testExistenceAfterDelete(t, c, 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, c, CONTAINER, OBJECT2) _, headers, err := c.Object(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } compareMaps(t, headers.ObjectMetadata(), map[string]string{"hello": "1", "potato-salad": "2"}) } func TestObjectUpdateContentType(t *testing.T) { c, rollback := makeConnectionWithObjectHeaders(t) defer rollback() 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": "1", "potato-salad": "2"}) } func TestVersionContainerCreate(t *testing.T) { c, rollback := makeConnectionAuth(t) defer rollback() err := c.VersionContainerCreate(CURRENT_CONTAINER, VERSIONS_CONTAINER) defer func() { c.ContainerDelete(CURRENT_CONTAINER) c.ContainerDelete(VERSIONS_CONTAINER) }() if err != nil { if err == swift.Forbidden { t.Log("Server doesn't support Versions - skipping test") return } t.Fatal(err) } } func TestVersionObjectAdd(t *testing.T) { c, rollback := makeConnectionWithVersionsContainer(t) defer rollback() 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) } defer func() { err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT) if 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) } defer func() { err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT) if 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) } defer func() { err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT) if err != nil { t.Fatal(err) } }() } func TestVersionObjectList(t *testing.T) { c, rollback := makeConnectionWithVersionsObject(t) defer rollback() 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") } } func TestVersionObjectDelete(t *testing.T) { c, rollback := makeConnectionWithVersionsObject(t) defer rollback() 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") } } func TestVersionDeleteContent(t *testing.T) { c, rollback := makeConnectionWithVersionsObject(t) defer rollback() 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) } // Delete Version 1 if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil { t.Fatal(err) } if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != swift.ObjectNotFound { t.Fatalf("Expecting Object not found error, got: %v", err) } } // 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, c *swift.Connection, 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) { c, rollback := makeConnectionWithObject(t) defer rollback() err := c.ObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } testExistenceAfterDelete(t, c, CONTAINER, OBJECT) err = c.ObjectDelete(CONTAINER, OBJECT) if err != swift.ObjectNotFound { t.Fatal("Expecting Object not found", err) } } func TestBulkDelete(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } err = c.ObjectDelete(CONTAINER, OBJECT2) 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") } } func TestObjectDifficultName(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() const name = `hello? sausage/êé/Hello, 世界/ " ' @ < > & ?/` err := c.ObjectPutString(CONTAINER, name, CONTENTS, "") if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, name) 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) } } func TestTempUrl(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() err := c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "") if err != nil { t.Fatal(err) } defer func() { err = c.ObjectDelete(CONTAINER, OBJECT) 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") } defer resp.Body.Close() 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 { var content []byte 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") } defer resp.Body.Close() if resp.StatusCode != 401 { t.Fatal("Expecting server to forbid access to object") } } } func TestQueryInfo(t *testing.T) { c, rollback := makeConnectionAuth(t) defer rollback() 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 TestDLOCreate(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } defer func() { err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } }() buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } info, _, err := c.Object(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } if info.ObjectType != swift.DynamicLargeObjectType { t.Errorf("Wrong ObjectType, expected %d, got: %d", swift.DynamicLargeObjectType, info.ObjectType) } if info.Bytes != int64(len(expected)) { t.Errorf("Wrong Bytes size, expected %d, got: %d", len(expected), info.Bytes) } } func TestDLOInsert(t *testing.T) { c, rollback := makeConnectionWithDLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, CheckHash: true, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreateFile(&opts) if err != nil { t.Fatal(err) } buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) _, err = fmt.Fprintf(multi, "%d%s\n", 0, CONTENTS) if err != nil { t.Fatal(err) } fmt.Fprintf(buf, "\n%d %s\n", 1, CONTENTS) err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestDLOAppend(t *testing.T) { c, rollback := makeConnectionWithDLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, Flags: os.O_APPEND, CheckHash: true, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreateFile(&opts) if err != nil { t.Fatal(err) } contents, err := c.ObjectGetString(CONTAINER, OBJECT) buf := bytes.NewBuffer([]byte(contents)) multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i+10, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err = c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestDLOTruncate(t *testing.T) { c, rollback := makeConnectionWithDLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, Flags: os.O_TRUNC, CheckHash: true, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreateFile(&opts) if err != nil { t.Fatal(err) } buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) _, err = fmt.Fprintf(multi, "%s", CONTENTS) if err != nil { t.Fatal(err) } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestDLOMove(t *testing.T) { c, rollback := makeConnectionWithDLO(t) defer rollback() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } err = c.DynamicLargeObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } defer func() { err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } }() contents2, err := c.ObjectGetString(CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } if contents2 != contents { t.Error("Contents wrong") } } func TestDLONoSegmentContainer(t *testing.T) { c, rollback := makeConnectionWithDLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", SegmentContainer: CONTAINER, } out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestDLOCreateMissingSegmentsInList(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() if srv == nil { t.Skipf("This test only runs with the fake swift server as it's needed to simulate eventual consistency problems.") return } listURL := "/v1/AUTH_" + swifttest.TEST_ACCOUNT + "/" + SEGMENTS_CONTAINER srv.SetOverride(listURL, func(w http.ResponseWriter, r *http.Request, recorder *httptest.ResponseRecorder) { for k, v := range recorder.HeaderMap { w.Header().Set(k, v[0]) } w.WriteHeader(recorder.Code) w.Write([]byte("null\n")) }) defer srv.UnsetOverride(listURL) headers := swift.Headers{} err := c.ContainerCreate(SEGMENTS_CONTAINER, headers) if err != nil { t.Fatal(err) } defer func() { err = c.ContainerDelete(SEGMENTS_CONTAINER) if err != nil { t.Fatal(err) } }() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } defer func() { err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } }() buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestDLOCreateIncorrectSize(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() if srv == nil { t.Skipf("This test only runs with the fake swift server as it's needed to simulate eventual consistency problems.") return } listURL := "/v1/AUTH_" + swifttest.TEST_ACCOUNT + "/" + CONTAINER + "/" + OBJECT headCount := 0 expectedHeadCount := 5 srv.SetOverride(listURL, func(w http.ResponseWriter, r *http.Request, recorder *httptest.ResponseRecorder) { for k, v := range recorder.HeaderMap { w.Header().Set(k, v[0]) } if r.Method == "HEAD" { headCount++ if headCount < expectedHeadCount { w.Header().Set("Content-Length", "7") } } w.WriteHeader(recorder.Code) w.Write(recorder.Body.Bytes()) }) defer srv.UnsetOverride(listURL) headers := swift.Headers{} err := c.ContainerCreate(SEGMENTS_CONTAINER, headers) if err != nil { t.Fatal(err) } defer func() { err = c.ContainerDelete(SEGMENTS_CONTAINER) if err != nil { t.Fatal(err) } }() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } defer func() { err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } }() buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } if headCount != expectedHeadCount { t.Errorf("Unexpected HEAD requests count, expected %d, got: %d", expectedHeadCount, headCount) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestDLOConcurrentWrite(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() nConcurrency := 5 nChunks := 100 var chunkSize int64 = 1024 writeFn := func(i int) { objName := fmt.Sprintf("%s_concurrent_dlo_%d", OBJECT, i) opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: objName, ContentType: "image/jpeg", } out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } defer func() { err = c.DynamicLargeObjectDelete(CONTAINER, objName) if err != nil { t.Fatal(err) } }() buf := &bytes.Buffer{} for j := 0; j < nChunks; j++ { var data []byte var n int data, err = ioutil.ReadAll(io.LimitReader(rand.Reader, chunkSize)) if err != nil { t.Fatal(err) } multi := io.MultiWriter(buf, out) n, err = multi.Write(data) if err != nil { t.Fatal(err) } if int64(n) != chunkSize { t.Fatalf("expected to write %d, got: %d", chunkSize, n) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, objName) if err != nil { t.Error(err) } if contents != expected { t.Error("Contents wrong") } } wg := sync.WaitGroup{} for i := 0; i < nConcurrency; i++ { wg.Add(1) go func(i int) { defer wg.Done() writeFn(i) }(i) } wg.Wait() } func TestDLOSegmentation(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", ChunkSize: 6, NoBuffer: true, } testSegmentation(t, c, func() swift.LargeObjectFile { out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } return out }, []segmentTest{ { writes: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8"}, expectedSegs: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8"}, expectedValue: "012345678", }, { writes: []string{"012345", "012345"}, expectedSegs: []string{"012345", "012345"}, expectedValue: "012345012345", }, { writes: []string{"0123456", "0123456"}, expectedSegs: []string{"012345", "6", "012345", "6"}, expectedValue: "01234560123456", }, { writes: []string{"0123456", "0123456"}, seeks: []int{-4, 0}, expectedSegs: []string{"012012", "3456"}, expectedValue: "0120123456", }, { writes: []string{"0123456", "0123456", "abcde"}, seeks: []int{0, -11, 0}, expectedSegs: []string{"012abc", "d", "e12345", "6"}, expectedValue: "012abcde123456", }, { writes: []string{"0123456", "ab"}, seeks: []int{-4, 0}, expectedSegs: []string{"012ab5", "6"}, expectedValue: "012ab56", }, }) } func TestDLOSegmentationBuffered(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", ChunkSize: 6, } testSegmentation(t, c, func() swift.LargeObjectFile { out, err := c.DynamicLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } return out }, []segmentTest{ { writes: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8"}, expectedSegs: []string{"012345", "678"}, expectedValue: "012345678", }, { writes: []string{"012345", "012345"}, expectedSegs: []string{"012345", "012345"}, expectedValue: "012345012345", }, { writes: []string{"0123456", "0123456"}, expectedSegs: []string{"012345", "6", "012345", "6"}, expectedValue: "01234560123456", }, { writes: []string{"0123456", "0123456"}, seeks: []int{-4, 0}, expectedSegs: []string{"012012", "3456"}, expectedValue: "0120123456", }, { writes: []string{"0123456", "0123456", "abcde"}, seeks: []int{0, -11, 0}, expectedSegs: []string{"012abc", "d", "e12345", "6"}, expectedValue: "012abcde123456", }, { writes: []string{"0123456", "ab"}, seeks: []int{-4, 0}, expectedSegs: []string{"012ab5", "6"}, expectedValue: "012ab56", }, }) } func TestSLOCreate(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.StaticLargeObjectCreate(&opts) if err != nil { if err == swift.SLONotSupported { t.Skip("SLO not supported") return } t.Fatal(err) } defer func() { err = c.StaticLargeObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } }() buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } info, _, err := c.Object(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } if info.ObjectType != swift.StaticLargeObjectType { t.Errorf("Wrong ObjectType, expected %d, got: %d", swift.StaticLargeObjectType, info.ObjectType) } if info.Bytes != int64(len(expected)) { t.Errorf("Wrong Bytes size, expected %d, got: %d", len(expected), info.Bytes) } } func TestSLOInsert(t *testing.T) { c, rollback := makeConnectionWithSLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", } out, err := c.StaticLargeObjectCreateFile(&opts) if err != nil { t.Fatal(err) } buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) _, err = fmt.Fprintf(multi, "%d%s\n", 0, CONTENTS) if err != nil { t.Fatal(err) } fmt.Fprintf(buf, "\n%d %s\n", 1, CONTENTS) err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestSLOAppend(t *testing.T) { c, rollback := makeConnectionWithSLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, Flags: os.O_APPEND, CheckHash: true, ContentType: "image/jpeg", } out, err := c.StaticLargeObjectCreateFile(&opts) if err != nil { t.Fatal(err) } contents, err := c.ObjectGetString(CONTAINER, OBJECT) buf := bytes.NewBuffer([]byte(contents)) multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i+10, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err = c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } } func TestSLOMove(t *testing.T) { c, rollback := makeConnectionWithSLO(t) defer rollback() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } err = c.StaticLargeObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } defer func() { err = c.StaticLargeObjectDelete(CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } }() contents2, err := c.ObjectGetString(CONTAINER, OBJECT2) if err != nil { t.Fatal(err) } if contents2 != contents { t.Error("Contents wrong") } } func TestSLONoSegmentContainer(t *testing.T) { c, rollback := makeConnectionWithSLO(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", SegmentContainer: CONTAINER, } out, err := c.StaticLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } buf := &bytes.Buffer{} multi := io.MultiWriter(buf, out) for i := 0; i < 2; i++ { _, err = fmt.Fprintf(multi, "%d %s\n", i, CONTENTS) if err != nil { t.Fatal(err) } } err = out.Close() if err != nil { t.Error(err) } expected := buf.String() contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != expected { t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) } err = c.StaticLargeObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } } func TestSLOMinChunkSize(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() if srv == nil { t.Skipf("This test only runs with the fake swift server as it's needed to simulate min segment size.") return } srv.SetOverride("/info", func(w http.ResponseWriter, r *http.Request, recorder *httptest.ResponseRecorder) { w.Write([]byte(`{"slo": {"min_segment_size": 4}}`)) }) defer srv.UnsetOverride("/info") c.QueryInfo() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", ChunkSize: 6, MinChunkSize: 0, NoBuffer: true, } testSLOSegmentation(t, c, func() swift.LargeObjectFile { out, err := c.StaticLargeObjectCreate(&opts) if err != nil { t.Fatal(err) } return out }) } func TestSLOSegmentation(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", ChunkSize: 6, MinChunkSize: 4, NoBuffer: true, } testSLOSegmentation(t, c, func() swift.LargeObjectFile { out, err := c.StaticLargeObjectCreate(&opts) if err != nil { if err == swift.SLONotSupported { t.Skip("SLO not supported") } t.Fatal(err) } return out }) } func TestSLOSegmentationBuffered(t *testing.T) { c, rollback := makeConnectionWithSegmentsContainer(t) defer rollback() opts := swift.LargeObjectOpts{ Container: CONTAINER, ObjectName: OBJECT, ContentType: "image/jpeg", ChunkSize: 6, MinChunkSize: 4, } testSegmentation(t, c, func() swift.LargeObjectFile { out, err := c.StaticLargeObjectCreate(&opts) if err != nil { if err == swift.SLONotSupported { t.Skip("SLO not supported") } t.Fatal(err) } return out }, []segmentTest{ { writes: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8"}, expectedSegs: []string{"012345", "678"}, expectedValue: "012345678", }, { writes: []string{"012345", "012345"}, expectedSegs: []string{"012345", "012345"}, expectedValue: "012345012345", }, { writes: []string{"0123456", "0123456"}, expectedSegs: []string{"012345", "601234", "56"}, expectedValue: "01234560123456", }, { writes: []string{"0123456", "0123456"}, seeks: []int{-4, 0}, expectedSegs: []string{"012012", "3456"}, expectedValue: "0120123456", }, { writes: []string{"0123456", "0123456", "abcde"}, seeks: []int{0, -11, 0}, expectedSegs: []string{"012abc", "de1234", "56"}, expectedValue: "012abcde123456", }, { writes: []string{"0123456", "ab"}, seeks: []int{-4, 0}, expectedSegs: []string{"012ab5", "6"}, expectedValue: "012ab56", }, }) } func testSLOSegmentation(t *testing.T, c *swift.Connection, createObj func() swift.LargeObjectFile) { testCases := []segmentTest{ { writes: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8"}, expectedSegs: []string{"0123", "4567", "8"}, expectedValue: "012345678", }, { writes: []string{"012345", "012345"}, expectedSegs: []string{"012345", "012345"}, expectedValue: "012345012345", }, { writes: []string{"0123456", "0123456"}, expectedSegs: []string{"012345", "601234", "56"}, expectedValue: "01234560123456", }, { writes: []string{"0123456", "0123456"}, seeks: []int{-4, 0}, expectedSegs: []string{"012012", "3456"}, expectedValue: "0120123456", }, { writes: []string{"0123456", "0123456", "abcde"}, seeks: []int{0, -11, 0}, expectedSegs: []string{"012abc", "de1234", "56"}, expectedValue: "012abcde123456", }, { writes: []string{"0123456", "ab"}, seeks: []int{-4, 0}, expectedSegs: []string{"012ab5", "6"}, expectedValue: "012ab56", }, } testSegmentation(t, c, createObj, testCases) } type segmentTest struct { writes []string seeks []int expectedSegs []string expectedValue string } func testSegmentation(t *testing.T, c *swift.Connection, createObj func() swift.LargeObjectFile, testCases []segmentTest) { var err error runTestCase := func(tCase segmentTest) { out := createObj() defer func() { err = c.LargeObjectDelete(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } }() for i, data := range tCase.writes { _, err = fmt.Fprint(out, data) if err != nil { t.Error(err) } if i < len(tCase.seeks)-1 { _, err = out.Seek(int64(tCase.seeks[i]), os.SEEK_CUR) if err != nil { t.Error(err) } } } err = out.Close() if err != nil { t.Error(err) } contents, err := c.ObjectGetString(CONTAINER, OBJECT) if err != nil { t.Error(err) } if contents != tCase.expectedValue { t.Errorf("Contents wrong, expected %q, got: %q", tCase.expectedValue, contents) } container, objects, err := c.LargeObjectGetSegments(CONTAINER, OBJECT) if err != nil { t.Error(err) } if container != SEGMENTS_CONTAINER { t.Errorf("Segments container wrong, expected %q, got: %q", SEGMENTS_CONTAINER, container) } _, headers, err := c.Object(CONTAINER, OBJECT) if err != nil { t.Fatal(err) } if headers.IsLargeObjectSLO() { var info swift.SwiftInfo info, err = c.QueryInfo() if err != nil { t.Fatal(err) } if info.SLOMinSegmentSize() > 4 { t.Log("Skipping checking segments because SLO min segment size imposed by server is larger than wanted for tests.") return } } var segContents []string for _, obj := range objects { var value string value, err = c.ObjectGetString(SEGMENTS_CONTAINER, obj.Name) if err != nil { t.Error(err) } segContents = append(segContents, value) } if !reflect.DeepEqual(segContents, tCase.expectedSegs) { t.Errorf("Segments wrong, expected %#v, got: %#v", tCase.expectedSegs, segContents) } } for _, tCase := range testCases { runTestCase(tCase) } } func TestContainerDelete(t *testing.T) { c, rollback := makeConnectionWithContainer(t) defer rollback() 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, rollback := makeConnectionAuth(t) defer rollback() 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") } }