feat: artifacts client
All checks were successful
default / ensure tests work (push) Successful in 27s
All checks were successful
default / ensure tests work (push) Successful in 27s
This commit is contained in:
parent
24ad00274d
commit
d77ef9f9c6
1 changed files with 1364 additions and 6 deletions
173
artifact/storage_test.go
Normal file
173
artifact/storage_test.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package artifact
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func sha256Hex(data string) string {
|
||||
sum := sha256.Sum256([]byte(data))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func TestPushBlob(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const data = "helloworld" // 10 bytes, chunked into 4+4+2
|
||||
|
||||
var mu sync.Mutex
|
||||
received := map[string][]byte{}
|
||||
var blockList []string
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Query().Get("comp") {
|
||||
case "block":
|
||||
id := r.URL.Query().Get("blockid")
|
||||
// Delay the first block so it completes after later ones; the
|
||||
// committed block list must still be in ascending index order.
|
||||
raw, _ := base64.StdEncoding.DecodeString(id)
|
||||
if string(raw) == "000000" {
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
mu.Lock()
|
||||
received[id] = body
|
||||
mu.Unlock()
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
case "blocklist":
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
var bl struct {
|
||||
Latest []struct {
|
||||
Value string `xml:",chardata"`
|
||||
} `xml:"Latest"`
|
||||
}
|
||||
if err := xml.Unmarshal(body, &bl); err != nil {
|
||||
t.Errorf("invalid block list xml: %v", err)
|
||||
}
|
||||
for _, l := range bl.Latest {
|
||||
blockList = append(blockList, l.Value)
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
default:
|
||||
http.Error(w, "unexpected request", http.StatusBadRequest)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
res, err := PushBlob(context.Background(), nil, strings.NewReader(data), server.URL+"?sv=test", 4, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Size != int64(len(data)) {
|
||||
t.Errorf("expected size %d, got %d", len(data), res.Size)
|
||||
}
|
||||
if res.SHA256Sum != sha256Hex(data) {
|
||||
t.Errorf("expected sha %q, got %q", sha256Hex(data), res.SHA256Sum)
|
||||
}
|
||||
|
||||
if len(received) != 3 {
|
||||
t.Fatalf("expected 3 blocks, got %d", len(received))
|
||||
}
|
||||
|
||||
wantChunks := []string{"hell", "owor", "ld"}
|
||||
for i, want := range wantChunks {
|
||||
id := base64.StdEncoding.EncodeToString([]byte(chunkID(i)))
|
||||
if got := string(received[id]); got != want {
|
||||
t.Errorf("block %d: expected %q, got %q", i, want, got)
|
||||
}
|
||||
}
|
||||
|
||||
if len(blockList) != 3 {
|
||||
t.Fatalf("expected 3 block list entries, got %d", len(blockList))
|
||||
}
|
||||
for i, id := range blockList {
|
||||
raw, err := base64.StdEncoding.DecodeString(id)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid block id %q: %v", id, err)
|
||||
}
|
||||
if got := string(raw); got != chunkID(i) {
|
||||
t.Errorf("block list position %d: expected %q, got %q", i, chunkID(i), got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushBlob_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
if _, err := PushBlob(context.Background(), nil, strings.NewReader(""), server.URL+"?sv=test", 4, 3); err == nil {
|
||||
t.Fatal("expected error for empty input")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushBlob_Error(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "boom", http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
_, err := PushBlob(context.Background(), nil, strings.NewReader("helloworld"), server.URL+"?sv=test", 4, 3)
|
||||
if err == nil || !strings.Contains(err.Error(), "upload failed") {
|
||||
t.Fatalf("expected upload error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullBlob(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const data = "helloworld"
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = io.WriteString(w, data)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
var buf bytes.Buffer
|
||||
res, err := PullBlob(context.Background(), nil, &buf, server.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := buf.String(); got != data {
|
||||
t.Errorf("expected %q, got %q", data, got)
|
||||
}
|
||||
if res.Size != int64(len(data)) {
|
||||
t.Errorf("expected size %d, got %d", len(data), res.Size)
|
||||
}
|
||||
if res.SHA256Sum != sha256Hex(data) {
|
||||
t.Errorf("expected sha %q, got %q", sha256Hex(data), res.SHA256Sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullBlob_Error(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "nope", http.StatusNotFound)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
if _, err := PullBlob(context.Background(), nil, io.Discard, server.URL); err == nil {
|
||||
t.Fatal("expected error for non-2xx response")
|
||||
}
|
||||
}
|
||||
|
||||
func chunkID(i int) string {
|
||||
return fmt.Sprintf("%06d", i)
|
||||
}
|
||||
Loading…
Reference in a new issue