Files
Memoh/internal/channel/normalize_test.go
BBQ bc374fe8cd refactor: content-addressed assets, cross-channel multimodal, infra simplification (#63)
* 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>
2026-02-19 00:20:27 +08:00

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)
}
}