77e69b9cf3
Signed-off-by: Olivier Gambier <olivier@docker.com>
1593 lines
37 KiB
Go
1593 lines
37 KiB
Go
// 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")
|
|
}
|
|
}
|