SpotiFLAC-Mobile/go_backend/filename_test.go
zarzet d24435dbc2 fix: truncate SAF filenames and directory segments safely at UTF-8 boundaries
Long track names (especially CJK/emoji) could exceed filesystem limits
when used as SAF document display names, causing write failures.

- Add truncateUtf8Bytes in Go, Kotlin (MainActivity + SafDownloadHandler),
  and Dart to truncate strings at valid UTF-8 codepoint boundaries
- Limit SAF filenames to 180 UTF-8 bytes (preserving file extension)
- Limit SAF directory segments to 120 UTF-8 bytes
- Fix Go sanitizeFilename to use UTF-8 aware truncation instead of
  byte slice which could split multi-byte characters
- Add Go test for multi-byte truncation correctness
- Sanitize SAF relative directory in Dart native worker and regular
  download paths via _sanitizeSafRelativeDir
2026-05-06 01:18:49 +07:00

114 lines
2.9 KiB
Go

package gobackend
import (
"strings"
"testing"
"unicode/utf8"
)
func TestBuildFilenameFromTemplate_WithRawTrackAndDisc(t *testing.T) {
metadata := map[string]interface{}{
"title": "Song Name",
"artist": "Artist Name",
"album": "Album Name",
"track": 1,
"disc": 2,
"year": "2025",
}
formatted := buildFilenameFromTemplate(
"{artist} - {track} - {track_raw} - d{disc} - d{disc_raw} - {title}",
metadata,
)
expected := "Artist Name - 01 - 1 - d2 - d2 - Song Name"
if formatted != expected {
t.Fatalf("expected %q, got %q", expected, formatted)
}
}
func TestBuildFilenameFromTemplate_RawPlaceholdersEmptyWhenZero(t *testing.T) {
metadata := map[string]interface{}{
"title": "Song Name",
"artist": "Artist Name",
"track": 0,
"disc": 0,
}
formatted := buildFilenameFromTemplate("{track_raw}-{disc_raw}-{title}", metadata)
expected := "--Song Name"
if formatted != expected {
t.Fatalf("expected %q, got %q", expected, formatted)
}
}
func TestBuildFilenameFromTemplate_InlineNumberFormatting(t *testing.T) {
metadata := map[string]interface{}{
"track": 3,
"disc": 2,
}
formatted := buildFilenameFromTemplate("{track:1}-{track:02}-{disc:03}", metadata)
expected := "3-03-002"
if formatted != expected {
t.Fatalf("expected %q, got %q", expected, formatted)
}
}
func TestBuildFilenameFromTemplate_DateStrftimeFormatting(t *testing.T) {
metadata := map[string]interface{}{
"artist": "Artist Name",
"title": "Song Name",
"release_date": "2024-03-09",
"track_number": 7,
"disc_number": 1,
}
formatted := buildFilenameFromTemplate(
"{artist} - {track:02} - {title} - {date:%Y-%m-%d} - {year}",
metadata,
)
expected := "Artist Name - 07 - Song Name - 2024-03-09 - 2024"
if formatted != expected {
t.Fatalf("expected %q, got %q", expected, formatted)
}
}
func TestBuildFilenameFromTemplate_DateStrftimeFormattingWithYearOnly(t *testing.T) {
metadata := map[string]interface{}{
"artist": "Artist Name",
"title": "Song Name",
"date": "2019",
}
formatted := buildFilenameFromTemplate("{date:%Y}-{date:%m}-{date:%d}", metadata)
expected := "2019-01-01"
if formatted != expected {
t.Fatalf("expected %q, got %q", expected, formatted)
}
}
func TestSanitizeFilenameMatchesDesktopSpacingBehavior(t *testing.T) {
got := sanitizeFilename(` "Text In Quotes"?%* / Demo `)
want := "Text In Quotes % Demo"
if got != want {
t.Fatalf("expected %q, got %q", want, got)
}
}
func TestSanitizeFilenameFallsBackToUnknownWhenEmpty(t *testing.T) {
got := sanitizeFilename(`<>:"/\|?*`)
if got != "Unknown" {
t.Fatalf("expected %q, got %q", "Unknown", got)
}
}
func TestSanitizeFilenameTruncatesWithoutSplittingUTF8(t *testing.T) {
got := sanitizeFilename(strings.Repeat("あ", 80))
if !utf8.ValidString(got) {
t.Fatalf("sanitizeFilename returned invalid UTF-8: %q", got)
}
if len(got) > 200 {
t.Fatalf("sanitizeFilename length = %d, want <= 200", len(got))
}
}