Files
im_server/server/media/oss/minio_test.go
T

130 lines
4.1 KiB
Go

package oss
import (
"bytes"
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/tinode/chat/server/media"
"github.com/tinode/chat/server/store"
"github.com/tinode/chat/server/store/types"
)
func TestMinIOUploadDownloadDelete(t *testing.T) {
const body = "hello minio"
var stored []byte
var deleted bool
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.URL.Path, "/xgjs/public/") {
t.Errorf("unexpected request path: %s", r.URL.Path)
http.Error(w, "wrong path", http.StatusNotFound)
return
}
switch r.Method {
case http.MethodPut:
stored, _ = io.ReadAll(r.Body)
if strings.Contains(r.Header.Get("Content-Encoding"), "aws-chunked") {
separator := bytes.Index(stored, []byte("\r\n"))
if separator < 0 || len(stored) < separator+2+len(body) {
t.Errorf("invalid signed chunked request")
http.Error(w, "invalid body", http.StatusBadRequest)
return
}
stored = stored[separator+2 : separator+2+len(body)]
}
w.Header().Set("ETag", `"test-etag"`)
case http.MethodGet:
w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
w.Header().Set("ETag", `"test-etag"`)
if r.Header.Get("Range") == "bytes=6-10" {
w.WriteHeader(http.StatusPartialContent)
_, _ = w.Write(stored[6:])
return
}
_, _ = w.Write(stored)
case http.MethodDelete:
deleted = true
w.WriteHeader(http.StatusNoContent)
default:
t.Errorf("unexpected request method: %s", r.Method)
http.Error(w, "wrong method", http.StatusMethodNotAllowed)
}
}))
defer server.Close()
previous := store.ThirdPartyConfig
store.ThirdPartyConfig = fakeThirdPartyConfig{
"oss_enabled": "0",
"minio_endpoint": server.URL,
"minio_bucket": "xgjs",
"minio_region": "us-east-1",
"minio_access_key_id": "test-key",
"minio_secret_access_key": "test-secret",
}
defer func() { store.ThirdPartyConfig = previous }()
handler := &ossHandler{}
if err := handler.Init(`{"provider":"minio","use_sys_config":true}`); err != nil {
t.Fatal(err)
}
if handler.bucket != nil || handler.minio == nil {
t.Fatal("MinIO provider should initialize without OSS credentials")
}
file := &media.PublicFileDef{ID: "file_test", Purpose: "avatar", MimeType: "text/plain", CreatedAt: time.Now()}
location, size, err := handler.UploadPublic(file, bytes.NewReader([]byte(body)))
if err != nil {
t.Fatal(err)
}
if size != int64(len(body)) || string(stored) != body || !strings.HasPrefix(location, "minio://xgjs/public/avatar/") {
t.Fatalf("upload: location=%q size=%d data=%q", location, size, stored)
}
fd := &types.FileDef{Location: location, Size: size}
_, reader, err := handler.downloadMinIO(fd)
if err != nil {
t.Fatal(err)
}
defer reader.Close()
buf := make([]byte, len(body))
if _, err := io.ReadFull(reader, buf); err != nil || string(buf) != body {
t.Fatalf("download: %q, %v", buf, err)
}
if _, err := reader.Seek(6, io.SeekStart); err != nil {
t.Fatal(err)
}
if got, err := io.ReadAll(reader); err != nil || string(got) != "minio" {
t.Fatalf("range download: %q, %v", got, err)
}
if err := handler.Delete([]string{location}); err != nil || !deleted {
t.Fatalf("delete: deleted=%v err=%v", deleted, err)
}
}
func TestMinIOLocationRejectsDifferentBucket(t *testing.T) {
handler := &ossHandler{minio: &minioStorage{bucket: "xgjs"}}
if _, _, err := handler.minioObject("minio://other/public/file"); err != types.ErrMalformed {
t.Fatalf("wrong bucket should be rejected, got %v", err)
}
}
func TestMinIOPublicEndpointUsesHTTPSPathStyle(t *testing.T) {
storage, err := newMinIOStorage(minioConfig{
Endpoint: "https://minio.118im.com", Bucket: "xgjs", Region: "us-east-1",
AccessKeyID: "test-key", SecretAccessKey: "test-secret",
})
if err != nil {
t.Fatal(err)
}
url, err := storage.client.PresignedGetObject(context.Background(), storage.bucket, "images/test.jpg", time.Minute, nil)
if err != nil {
t.Fatal(err)
}
if url.Scheme != "https" || url.Host != "minio.118im.com" || url.Path != "/xgjs/images/test.jpg" {
t.Fatalf("unexpected MinIO URL: %s", url)
}
}