mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
181 lines
5.6 KiB
Go
181 lines
5.6 KiB
Go
package wecom
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
basechannels "github.com/sipeed/picoclaw/pkg/channels"
|
|
"github.com/sipeed/picoclaw/pkg/media"
|
|
)
|
|
|
|
func TestStoreRemoteMedia_DetectsJPEGContentTypeFromBody(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const jpegBase64 = "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////" +
|
|
"//////////////////////////////////////////////////////////////////////////////////////////////2wBDAf//////////////////////////////////////////////////////////////////////////////////////" +
|
|
"//////////////////////////////////////////////////////////////////////////////////////////////wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAVEQEBAAAAAAAAAAAAAAAAAAAABf/aAAwDAQACEAMQAAAB6A//xAAVEAEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwF//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJf/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABPyFf/9k="
|
|
|
|
jpegData := decodeTestBase64(t, jpegBase64)
|
|
store := media.NewFileMediaStore()
|
|
ch := &WeComChannel{
|
|
BaseChannel: basechannels.NewBaseChannel("wecom", nil, nil, nil),
|
|
mediaClient: &http.Client{
|
|
Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
return &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Header: http.Header{"Content-Type": []string{"application/octet-stream"}},
|
|
Body: io.NopCloser(bytes.NewReader(jpegData)),
|
|
}, nil
|
|
}),
|
|
},
|
|
}
|
|
ch.SetMediaStore(store)
|
|
|
|
ref, err := ch.storeRemoteMedia(context.Background(), "test-scope", "msg-1", "https://wecom.example/media", "", "")
|
|
if err != nil {
|
|
t.Fatalf("storeRemoteMedia returned error: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = store.ReleaseAll("test-scope")
|
|
})
|
|
|
|
_, meta, err := store.ResolveWithMeta(ref)
|
|
if err != nil {
|
|
t.Fatalf("resolve media ref: %v", err)
|
|
}
|
|
if meta.ContentType != "image/jpeg" {
|
|
t.Fatalf("expected image/jpeg content type, got %q", meta.ContentType)
|
|
}
|
|
if !strings.HasSuffix(meta.Filename, ".jpg") && !strings.HasSuffix(meta.Filename, ".jpeg") {
|
|
t.Fatalf("expected jpeg filename, got %q", meta.Filename)
|
|
}
|
|
}
|
|
|
|
func TestDetectWeComMediaMetadata_UsesFallbackExtensionWhenBodyUnknown(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
filename, contentType := detectWeComMediaMetadata([]byte("not a real image"), "msg-2.pdf", "", "", "")
|
|
if filename != "msg-2.pdf" {
|
|
t.Fatalf("expected fallback filename to be preserved, got %q", filename)
|
|
}
|
|
if contentType != "application/pdf" {
|
|
t.Fatalf("expected application/pdf from fallback extension, got %q", contentType)
|
|
}
|
|
}
|
|
|
|
func TestStoreRemoteMedia_PreservesSuffixFromURL(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
docxLikeData := []byte("PK\x03\x04fake office payload")
|
|
store := media.NewFileMediaStore()
|
|
ch := &WeComChannel{
|
|
BaseChannel: basechannels.NewBaseChannel("wecom", nil, nil, nil),
|
|
mediaClient: &http.Client{
|
|
Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
return &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Header: http.Header{"Content-Type": []string{"application/octet-stream"}},
|
|
Body: io.NopCloser(bytes.NewReader(docxLikeData)),
|
|
}, nil
|
|
}),
|
|
},
|
|
}
|
|
ch.SetMediaStore(store)
|
|
|
|
ref, err := ch.storeRemoteMedia(
|
|
context.Background(),
|
|
"test-scope",
|
|
"msg-docx",
|
|
"https://wecom.example/media/report.docx?signature=1",
|
|
"",
|
|
".bin",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("storeRemoteMedia returned error: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = store.ReleaseAll("test-scope")
|
|
})
|
|
|
|
localPath, meta, err := store.ResolveWithMeta(ref)
|
|
if err != nil {
|
|
t.Fatalf("resolve media ref: %v", err)
|
|
}
|
|
if !strings.HasSuffix(meta.Filename, ".docx") {
|
|
t.Fatalf("expected docx filename, got %q", meta.Filename)
|
|
}
|
|
if !strings.HasSuffix(strings.ToLower(localPath), ".docx") {
|
|
t.Fatalf("expected docx temp path, got %q", localPath)
|
|
}
|
|
}
|
|
|
|
func TestStoreRemoteMedia_PreservesSuffixFromContentDisposition(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
pptxLikeData := []byte("PK\x03\x04fake office payload")
|
|
store := media.NewFileMediaStore()
|
|
ch := &WeComChannel{
|
|
BaseChannel: basechannels.NewBaseChannel("wecom", nil, nil, nil),
|
|
mediaClient: &http.Client{
|
|
Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
return &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Header: http.Header{
|
|
"Content-Type": []string{"application/octet-stream"},
|
|
"Content-Disposition": []string{`attachment; filename="slides.pptx"`},
|
|
},
|
|
Body: io.NopCloser(bytes.NewReader(pptxLikeData)),
|
|
}, nil
|
|
}),
|
|
},
|
|
}
|
|
ch.SetMediaStore(store)
|
|
|
|
ref, err := ch.storeRemoteMedia(
|
|
context.Background(),
|
|
"test-scope",
|
|
"msg-pptx",
|
|
"https://wecom.example/media/download",
|
|
"",
|
|
".bin",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("storeRemoteMedia returned error: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = store.ReleaseAll("test-scope")
|
|
})
|
|
|
|
localPath, meta, err := store.ResolveWithMeta(ref)
|
|
if err != nil {
|
|
t.Fatalf("resolve media ref: %v", err)
|
|
}
|
|
if !strings.HasSuffix(meta.Filename, ".pptx") {
|
|
t.Fatalf("expected pptx filename, got %q", meta.Filename)
|
|
}
|
|
if !strings.HasSuffix(strings.ToLower(localPath), ".pptx") {
|
|
t.Fatalf("expected pptx temp path, got %q", localPath)
|
|
}
|
|
}
|
|
|
|
func decodeTestBase64(t *testing.T, value string) []byte {
|
|
t.Helper()
|
|
|
|
data, err := io.ReadAll(base64.NewDecoder(base64.StdEncoding, strings.NewReader(value)))
|
|
if err != nil {
|
|
t.Fatalf("decode base64 fixture: %v", err)
|
|
}
|
|
return data
|
|
}
|
|
|
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
return f(req)
|
|
}
|