1 Star 0 Fork 0

xianghan228/swift

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
swift_test.go 73.09 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142
// 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"
SYMLINK_OBJECT = "test_symlink"
SYMLINK_OBJECT2 = "test_symlink2"
EMPTYOBJECT = "empty_test_object"
CONTENTS = "12345"
CONTENTS2 = "54321"
CONTENT_SIZE = int64(len(CONTENTS))
CONTENT_MD5 = "827ccb0eea8a706c4c34a16891f84e7b"
CONTENT2_MD5 = "01cfcd4f6b8770febfb40cb906715822"
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")
internalServer := false
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
internalServer = true
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
MaxIdleConnsPerHost: 2048,
}
if Insecure == "1" {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
swift.SetExpectContinueTimeout(transport, 5*time.Second)
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),
}
if !internalServer {
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")
}
}
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 getSwinftInfo(t *testing.T) (info swift.SwiftInfo, err error) {
c, rollback := makeConnectionAuth(t)
defer rollback()
return c.QueryInfo()
}
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
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()
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.Tenant = ""
c.Domain = os.Getenv("SWIFT_API_DOMAIN")
c.TenantId = os.Getenv("SWIFT_TENANT_ID")
c.DomainId = ""
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.Tenant = ""
c.Domain = ""
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")
c.TenantId = ""
c.DomainId = ""
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.Domain = ""
c.TenantId = ""
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 TestObjectPutWithReauth(t *testing.T) {
if !swift.IS_AT_LEAST_GO_16 {
return
}
c, rollback := makeConnectionWithContainer(t)
defer rollback()
// Simulate that our auth token expired
c.AuthToken = "expiredtoken"
r := strings.NewReader(CONTENTS)
_, err := c.ObjectPut(CONTAINER, OBJECT, r, true, "", "text/plain", nil)
if err != nil {
t.Fatal(err)
}
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 TestObjectPutStringWithReauth(t *testing.T) {
if !swift.IS_AT_LEAST_GO_16 {
return
}
c, rollback := makeConnectionWithContainer(t)
defer rollback()
// Simulate that our auth token expired
c.AuthToken = "expiredtoken"
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 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 TestSymlinkObject(t *testing.T) {
info, err := getSwinftInfo(t)
if err != nil {
t.Fatal(err)
}
if _, ok := info["symlink"]; !ok {
// skip, symlink not supported
t.Skip("skip, symlink not supported")
return
}
c, rollback := makeConnectionWithContainer(t)
defer rollback()
// write target objects
err = c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "text/potato")
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, OBJECT)
if err != nil {
t.Error(err)
}
}()
// test dynamic link
_, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT, "", CONTAINER, OBJECT, "")
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, SYMLINK_OBJECT)
if err != nil {
t.Error(err)
}
}()
md, _, err := c.Object(CONTAINER, SYMLINK_OBJECT)
if err != nil {
t.Error(err)
}
if md.ContentType != "text/potato" {
t.Error("Bad content type", md.ContentType)
}
if md.Bytes != CONTENT_SIZE {
t.Errorf("Bad length want 5 got %v", md.Bytes)
}
if md.Hash != CONTENT_MD5 {
t.Errorf("Bad MD5 want %v got %v", CONTENT_MD5, md.Hash)
}
}
func TestStaticSymlinkObject(t *testing.T) {
info, err := getSwinftInfo(t)
if err != nil {
t.Fatal(err)
}
if sym, ok := info["symlink"].(map[string]interface{}); ok {
if _, ok := sym["static_links"]; !ok {
t.Skip("skip, static symlink not supported")
return
}
} else {
t.Skip("skip, symlink not supported")
return
}
c, rollback := makeConnectionWithContainer(t)
defer rollback()
// write target objects
err = c.ObjectPutBytes(CONTAINER, OBJECT2, []byte(CONTENTS2), "text/tomato")
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, OBJECT2)
if err != nil {
t.Error(err)
}
}()
// test static link
// first with the wrong target etag
_, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT_MD5)
if err == nil {
t.Error("Symlink with wrong target etag should have failed")
}
_, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT2_MD5)
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, SYMLINK_OBJECT2)
if err != nil {
t.Error(err)
}
}()
md, _, err := c.Object(CONTAINER, SYMLINK_OBJECT2)
if err != nil {
t.Error(err)
}
if md.ContentType != "text/tomato" {
t.Error("Bad content type", md.ContentType)
}
if md.Bytes != CONTENT_SIZE {
t.Errorf("Bad length want 5 got %v", md.Bytes)
}
if md.Hash != CONTENT2_MD5 {
t.Errorf("Bad MD5 want %v got %v", CONTENT2_MD5, md.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 TestObjectCreateAbort(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() {
_ = c.ObjectDelete(CONTAINER, OBJECT2) // Ignore error
}()
expectedContents := "foo"
_, err = out.Write([]byte(expectedContents))
if err != nil {
t.Error(err)
}
errAbort := fmt.Errorf("abort")
err = out.CloseWithError(errAbort)
if err != nil {
t.Errorf("Unexpected error %#v", err)
}
_, err = c.ObjectGetString(CONTAINER, OBJECT2)
if err != swift.ObjectNotFound {
t.Errorf("Unexpected error: %#v", 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 TestObjectOpenNotModified(t *testing.T) {
c, rollback := makeConnectionWithObject(t)
defer rollback()
_, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, swift.Headers{
"If-None-Match": CONTENT_MD5,
})
if err != swift.NotModified {
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 TestObjectCopyDifficultName(t *testing.T) {
c, rollback := makeConnectionWithObjectHeaders(t)
defer rollback()
const dest = OBJECT + "?param %30%31%32 £100"
_, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, dest, nil)
if err != nil {
t.Fatal(err)
}
err = c.ObjectDelete(CONTAINER, dest)
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")
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/xianghan228/swift.git
git@gitee.com:xianghan228/swift.git
xianghan228
swift
swift
master

搜索帮助