Skip to content
quarry Artifact [2dc529feb8]
Login

Artifact [2dc529feb8]

Artifact 2dc529feb855283b64a267004ccec8890e68145ebd862b357eb22ff976013cac:


package s3

import (
	"context"
	"errors"
	"strings"
	"testing"
	"time"
)

// Copy replaces an object's metadata in place, by CopyObject from the
// key to itself: the bytes never pass through the forge, and the type,
// the disposition and the metadata are the ones given now.
func TestCopyReplacesMetadata(t *testing.T) {
	srv, m := newMetaBucket(t)
	b := Bucket{Endpoint: srv.URL, Name: "files", Region: "auto", AccessKey: "AK", Secret: testSecret}
	ctx := context.Background()
	key := "unversioned/releases/a b+c.txt"
	if err := b.PutWith(ctx, nil, key, []byte("12345"), PutOptions{ContentType: "text/plain",
		Meta: map[string]string{"mtime": "1", "hash": "c0ffee"}}, time.Now()); err != nil {
		t.Fatal(err)
	}
	err := b.Copy(ctx, nil, key, key, PutOptions{ContentType: "text/plain; charset=utf-8",
		Meta: map[string]string{"mtime": "2", "hash": "c0ffee"}}, time.Now())
	if err != nil {
		t.Fatalf("FAIL: copy: %v", err)
	}
	o := m.objects[key]
	if string(o.body) != "12345" || o.meta["mtime"] != "2" || o.meta["hash"] != "c0ffee" || o.ctype != "text/plain; charset=utf-8" || m.copies != 1 {
		t.Errorf("FAIL: after copy %+v copies=%d", o, m.copies)
	} else {
		t.Log("ok: the metadata is replaced and the bytes stay where they are")
	}
	if err := b.Copy(ctx, nil, "unversioned/none", "unversioned/none", PutOptions{}, time.Now()); err == nil {
		t.Error("FAIL: copying an absent object succeeded")
	}
	// CopyObject can answer 200 and then report the failure in the
	// body; that is a failure, and the object is unchanged.
	m.copyFails = true
	err = b.Copy(ctx, nil, key, key, PutOptions{ContentType: "text/plain", Meta: map[string]string{"mtime": "3"}}, time.Now())
	var pe *ProbeError
	if !errors.As(err, &pe) || pe.Code != "InternalError" || m.objects[key].meta["mtime"] != "2" {
		t.Errorf("FAIL: a 200 carrying an error: %v, mtime %s", err, m.objects[key].meta["mtime"])
	} else {
		t.Log("ok: a copy that answers 200 with an error body is a failure")
	}
}

// GetMax reads an object whole when it fits and refuses one that does
// not, without reading past the bound.
func TestGetMaxIsBounded(t *testing.T) {
	srv, m := newMetaBucket(t)
	m.objects["unversioned/a"] = metaObject{body: []byte("0123456789")}
	b := Bucket{Endpoint: srv.URL, Name: "files", Region: "auto", AccessKey: "AK", Secret: testSecret}
	ctx := context.Background()
	got, err := b.GetMax(ctx, nil, "unversioned/a", 10, time.Now())
	if err != nil || string(got) != "0123456789" {
		t.Errorf("FAIL: at the bound: %q %v", got, err)
	}
	if _, err := b.GetMax(ctx, nil, "unversioned/a", 9, time.Now()); err == nil || !strings.Contains(err.Error(), "larger than 9") {
		t.Errorf("FAIL: over the bound: %v", err)
	}
	if _, err := b.GetMax(ctx, nil, "unversioned/none", 9, time.Now()); err == nil {
		t.Error("FAIL: an absent object read as empty")
	}
	t.Log("ok: a bounded read takes what fits and refuses the rest")
}