mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
bc374fe8cd
* refactor(attachment): multimodal attachment refactor with snapshot schema and storage layer - Add snapshot schema migration (0008) and update init/versions/snapshots - Add internal/attachment and internal/channel normalize for unified attachment handling - Move containerfs provider from internal/media to internal/storage - Update agent types, channel adapters (Telegram/Feishu), inbound and handlers - Add containerd snapshot lineage and local_channel tests - Regenerate sqlc, swagger and SDK * refactor(media): content-addressed asset system with unified naming - Replace asset_id foreign key with content_hash as sole identifier for bot_history_message_assets (pure soft-link model) - Remove mime, size_bytes, storage_key from DB; derive at read time via media.Resolve from actual storage - Merge migrations 0008/0009 into single 0008; keep 0001 as canonical schema - Add Docker initdb script for deterministic migration execution order - Fix cross-channel real-time image display (Telegram → WebUI SSE) - Fix message disappearing on refresh (null assets fallback) - Fix file icon instead of image preview (mime derivation from storage) - Unify AssetID → ContentHash naming across Go, Agent, and Frontend - Change storage key prefix from 4-char to 2-char for directory sharding - Add server-entrypoint.sh for Docker deployment migration handling * refactor(infra): embedded migrations, Docker simplification, and config consolidation - Embed SQL migrations into Go binary, removing shell-based migration scripts - Consolidate config files into conf/ directory (app.example.toml, app.docker.toml, app.dev.toml) - Simplify Docker setup: remove initdb.d scripts, streamline nginx config and entrypoint - Remove legacy CLI, feishu-echo commands, and obsolete incremental migration files - Update install script and docs to require sudo for one-click install - Add mise tasks for dev environment orchestration * chore: recover migrations --------- Co-authored-by: Acbox <acbox0328@gmail.com>
98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package channel
|
|
|
|
import "testing"
|
|
|
|
func TestInferAttachmentType(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
inType AttachmentType
|
|
mime string
|
|
file string
|
|
want AttachmentType
|
|
}{
|
|
{
|
|
name: "keep explicit image",
|
|
inType: AttachmentImage,
|
|
mime: "",
|
|
file: "",
|
|
want: AttachmentImage,
|
|
},
|
|
{
|
|
name: "infer from image mime",
|
|
inType: AttachmentFile,
|
|
mime: "image/jpeg",
|
|
file: "a.bin",
|
|
want: AttachmentImage,
|
|
},
|
|
{
|
|
name: "infer gif from mime",
|
|
inType: AttachmentFile,
|
|
mime: "image/gif",
|
|
file: "a.bin",
|
|
want: AttachmentGIF,
|
|
},
|
|
{
|
|
name: "infer from audio extension",
|
|
inType: AttachmentFile,
|
|
mime: "",
|
|
file: "a.mp3",
|
|
want: AttachmentAudio,
|
|
},
|
|
{
|
|
name: "infer from video extension",
|
|
inType: AttachmentFile,
|
|
mime: "",
|
|
file: "a.mp4",
|
|
want: AttachmentVideo,
|
|
},
|
|
{
|
|
name: "fallback file",
|
|
inType: AttachmentFile,
|
|
mime: "",
|
|
file: "a.unknown",
|
|
want: AttachmentFile,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := InferAttachmentType(tc.inType, tc.mime, tc.file)
|
|
if got != tc.want {
|
|
t.Fatalf("InferAttachmentType got %q want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizeInboundChannelAttachment(t *testing.T) {
|
|
normalized := NormalizeInboundChannelAttachment(Attachment{
|
|
Type: AttachmentFile,
|
|
Mime: "IMAGE/JPEG; charset=utf-8",
|
|
Name: " photo.jpg ",
|
|
URL: " https://example.com/x ",
|
|
PlatformKey: " file_key_1 ",
|
|
SourcePlatform: " feishu ",
|
|
Caption: " hello ",
|
|
})
|
|
if normalized.Type != AttachmentImage {
|
|
t.Fatalf("expected inferred image type, got %q", normalized.Type)
|
|
}
|
|
if normalized.Mime != "image/jpeg" {
|
|
t.Fatalf("expected normalized mime image/jpeg, got %q", normalized.Mime)
|
|
}
|
|
if normalized.Name != "photo.jpg" {
|
|
t.Fatalf("expected trimmed name, got %q", normalized.Name)
|
|
}
|
|
if normalized.URL != "https://example.com/x" {
|
|
t.Fatalf("expected trimmed url, got %q", normalized.URL)
|
|
}
|
|
if normalized.PlatformKey != "file_key_1" {
|
|
t.Fatalf("expected trimmed platform key, got %q", normalized.PlatformKey)
|
|
}
|
|
if normalized.SourcePlatform != "feishu" {
|
|
t.Fatalf("expected trimmed source platform, got %q", normalized.SourcePlatform)
|
|
}
|
|
if normalized.Caption != "hello" {
|
|
t.Fatalf("expected trimmed caption, got %q", normalized.Caption)
|
|
}
|
|
}
|