refactor: 迁移至 bashly CLI 与功能域模块结构

- CLI 定义迁移至 src/bashly.yml(bashly 1.4 生成参数解析),入口收敛为 src/root_command.sh
- 源码按功能域重组:config/storage/integrate/media/pipeline,消除扁平散落
- 新增零依赖测试框架 tests/(123 用例)与 CI 流水线(lint + test + build + 产物一致性)
- build.sh 支持版本单一来源注入与 --check 产物校验;新增 lint.sh(bash -n + shellcheck 零容忍)
- 删除旧单文件 media_organizer.sh 与旧扁平模块
This commit is contained in:
Shuery committed 2026-08-14 22:41:28 +08:00
1 parent d978a13a83
commit 0df07ad0ae
48 files changed
+5813 -6812

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
# ============================================================================
# 识别登记与并发池模块测试(src/lib/registry.sh)
# ============================================================================
setup() {
MEDIA_DESTINATION_MAP=()
MEDIA_OUTCOME_MAP=()
PENDING_AI_SEARCH=()
PENDING_AI_SPECIAL=()
PENDING_AI_ARTIST=()
PENDING_AI_MATCH=()
PENDING_SEARCH_COUNT=0
PENDING_SPECIAL_COUNT=0
PENDING_ARTIST_COUNT=0
PENDING_MATCH_COUNT=0
LINK_SUCCESS_COUNT=0
LINK_FAIL_COUNT=0
}
# register_identified:登记目的与结局账本
test_register_identified() {
register_identified "/m/a.mkv" "Movie/A (2020)" "A (2020).mkv"
assert_eq "Movie/A (2020)|A (2020).mkv" "${MEDIA_DESTINATION_MAP[/m/a.mkv]}"
assert_eq "identified" "${MEDIA_OUTCOME_MAP[/m/a.mkv]}"
}
# register_skip:跳过不进入目的映射
test_register_skip() {
register_skip "/m/a.mkv" "skip_unidentified"
assert_eq "skip_unidentified" "${MEDIA_OUTCOME_MAP[/m/a.mkv]}"
assert_empty "${MEDIA_DESTINATION_MAP[/m/a.mkv]:-}"
}
# register_request_failed:请求失败结局
test_register_request_failed() {
register_request_failed "/m/a.mkv"
assert_eq "request_failed" "${MEDIA_OUTCOME_MAP[/m/a.mkv]}"
}
# register_pending:待 AI 搜索并计入计数
test_register_pending() {
register_pending "/m/a.mkv" "unknown dir"
assert_eq "unknown dir" "${PENDING_AI_SEARCH[/m/a.mkv]}"
assert_eq "1" "$PENDING_SEARCH_COUNT"
}
# emit:DEST 协议行写入 POOL_EMIT_FILE(识别池 worker 模式;父进程 merge 后登记)
test_emit_dest_protocol() {
local f
f="$(mktemp)"
POOL_EMIT_FILE="$f"
emit "/m/a.mkv" DEST "Movie/A (2020)|A (2020).mkv"
assert_eq $'/m/a.mkv DEST Movie/A (2020)|A (2020).mkv' "$(cat "$f")"
assert_empty "${MEDIA_OUTCOME_MAP[/m/a.mkv]:-}" "emit 本身不登记,merge 才登记"
rm -f "$f"
unset POOL_EMIT_FILE
}
# emit:SPECIAL_PENDING 直接登记待学习特典
test_emit_special_pending() {
emit "/m/a.mkv" SPECIAL_PENDING "123|Menu01|Show|01:Menu; 02:CM"
assert_eq "Show|01:Menu; 02:CM" "${PENDING_AI_SPECIAL[123|Menu01]}"
assert_eq "1" "$PENDING_SPECIAL_COUNT"
}
# emit:MATCH 直接登记待甄别
test_emit_match_protocol() {
emit "/m/a.mkv" MATCH "tv 1 /search/tv key123"
assert_eq "1" "$PENDING_MATCH_COUNT"
assert_not_empty "${PENDING_AI_MATCH[/m/a.mkv]:-}"
}
# merge_emit_lines:合并子进程协议行(临时文件)
test_merge_emit_lines() {
local f
f="$(mktemp)"
printf '/m/a.mkv\tDEST\tMovie/A|A.mkv\n' >"$f"
printf '/m/b.mkv\tSKIP\tskip_type\n' >>"$f"
merge_emit_lines "$f"
rm -f "$f"
assert_eq "identified" "${MEDIA_OUTCOME_MAP[/m/a.mkv]}"
assert_eq "skip_type" "${MEDIA_OUTCOME_MAP[/m/b.mkv]}"
}
# dir_chain:相对源根的目录链(最多 3 层)
test_dir_chain() {
SOURCE_DIR="/media/src"
assert_eq "Show/Season 1" "$(dir_chain "/media/src/Show/Season 1/a.mkv")"
# 契约:最多 3 层,保留最靠近文件的 3 层(含文件所在目录)
assert_eq "B/C/D" "$(dir_chain "/media/src/A/B/C/D/a.mkv")"
assert_eq "src" "$(dir_chain "/media/src/a.mkv")"
}