mirror of
https://github.com/tiennm99/telegram-exporter.git
synced 2026-10-11 03:13:49 +00:00
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.
93 lines
2.7 KiB
Go
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} }
|