Files
telegram-exporter/internal/naming/naming_test.go
T
tiennm99 1393abdae2 feat: index a remote and verify a chat against it in-process
Third slice: verify-export.sh, without the subprocess or the python.

One rclone listing builds an in-memory index, and the report is computed from
it. missing-ids.txt and gap.json are gone; so is every python3 heredoc.

Presence is answered from a whole name and never from a message id. The
id-keyed map exists only to tell "absent" apart from "absent, but a stale copy
under an older name is sitting there", and it stays unexported so nothing can
reach for it as an answer. That distinction is the bug this rewrite exists to
remove, so it is enforced by structure rather than by comment.

Names are checked for path containment before use. Storing them verbatim means
a filename chosen by whoever uploaded the file can contain a separator or a
parent reference, and tdl never had to care because its template rewrote those
away. Over-long names are refused for the same reason: the filesystem would
reject them at create time, and a file that can never be written would be
reported absent on every pass forever.

Objects are addressed by the path rclone knows them by, not by the basename
used for matching. The two differ once a remote has directory structure, and
deleting by basename would miss the object or remove a same-named one from the
root. Basenames appearing at more than one path make the snapshot ambiguous, so
they are reported rather than silently resolved.

Indexing runs at full depth with filters cleared. Inheriting RCLONE_MAX_DEPTH
or RCLONE_EXCLUDE would not fail, it would quietly report archived files as
absent and fetch them all again.

Deleting stale copies stays opt-in and confirmed; a non-interactive stdin
declines rather than proceeding. Filenames are quoted wherever they are
printed, so an embedded escape cannot redraw the list an operator approves.

Verified against the live remote: identical to verify-export.sh on the same
state — 18155 expected, 15548 present, 2607 absent, ids 9857-18013, exit 1.
2026-09-06 18:50:53 +07:00

93 lines
2.7 KiB
Go

package naming
import (
"testing"
"github.com/iyear/tdl/core/tmedia"
)
// The format is a compatibility contract, not a style choice: ~15k files are
// already stored under it. Changing it makes every one of them look absent and
// re-downloads the entire archive.
func TestForMatchesStoredLayout(t *testing.T) {
tests := []struct {
name string
dialogID int64
msgID int
file string
want string
}{
{
name: "document with a filename attribute",
dialogID: 1234567890,
msgID: 14726,
file: "298.mp4",
want: "1234567890_14726_298.mp4",
},
{
// The message that started this rewrite. Its name carries a doubled
// '!' which tdl's default template collapsed to one, via filenamify,
// while the export JSON kept both — the two derivations that never
// agreed. This package keeps the name as Telegram reports it, so the
// doubled '!' must survive.
name: "punctuation is preserved verbatim",
dialogID: 1234567890,
msgID: 4242,
file: "Pipe her!! And by her, we mean pipeperr! 1080p.mp4",
want: "1234567890_4242_Pipe her!! And by her, we mean pipeperr! 1080p.mp4",
},
{
name: "photo gets tmedia's generated name",
dialogID: 1234567890,
msgID: 42,
file: "5901234567890123456.jpg",
want: "1234567890_42_5901234567890123456.jpg",
},
{
name: "spaces and separators inside the filename are untouched",
dialogID: 1234567890,
msgID: 7,
file: "a_b c-d.e.mp4",
want: "1234567890_7_a_b c-d.e.mp4",
},
{
name: "non-ascii is untouched",
dialogID: 1234567890,
msgID: 8,
file: "ünïcödé näme 🍓.mp4",
want: "1234567890_8_ünïcödé näme 🍓.mp4",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := For(tt.dialogID, tt.msgID, &tmedia.Media{Name: tt.file})
if got != tt.want {
t.Errorf("For() = %q, want %q", got, tt.want)
}
})
}
}
// Guards the reason the field layout is what it is. If this ever starts failing,
// tdl changed its default template and the compatibility note on For is stale.
func TestForKeepsCharactersTdlWouldRewrite(t *testing.T) {
// filenamify (tdl's default template applies it) collapses runs of '!' and
// replaces reserved characters. None of that may happen here.
for _, file := range []string{
"double!!bang.mp4",
"a?b:c.mp4",
".leading-dot.mp4",
"trailing!.mp4",
} {
got := For(1, 2, &tmedia.Media{Name: file})
want := "1_2_" + file
if got != want {
t.Errorf("For(%q) = %q, want %q — a sanitiser crept in", file, got, want)
}
}
}
// mediaNamed builds the only part of tmedia.Media these tests care about.
func mediaNamed(name string) *tmedia.Media { return &tmedia.Media{Name: name} }