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

+79
View File
@@ -0,0 +1,79 @@
# ============================================================================
# 用户自定义规则测试(src/lib/rules.sh):命名模板渲染 + 匹配规则键
# ============================================================================
# render_naming_template:普通占位符 + 补零
test_render_plain_and_padding() {
assert_eq "S01E02" "$(render_naming_template 'S{season:02}E{episode:02}' season=1 episode=2)"
assert_eq "Season 12" "$(render_naming_template 'Season {season}' season=12)"
}
# render_naming_template:条件段(year 非空渲染,空则不渲染)
test_render_conditional_year() {
assert_eq "Movie (2020)" "$(render_naming_template '{title}{?year: ({year})}' title=Movie year=2020)"
assert_eq "Movie" "$(render_naming_template '{title}{?year: ({year})}' title=Movie year=)"
}
# render_naming_template:条件段正文含嵌套占位符
test_render_conditional_nested_placeholder() {
assert_eq "Show [2020]" "$(render_naming_template '{title}{?year: [{year}]}' title=Show year=2020)"
}
# render_naming_template:默认剧集模板全链路(含空集名条件段)
test_render_episode_template_full() {
assert_eq "S01E02 - Name" \
"$(render_naming_template 'S{season:02}E{episode:02}{range}{?episode_name: - {episode_name}}' season=01 episode=02 range= 'episode_name=Name')"
assert_eq "S01E02" \
"$(render_naming_template 'S{season:02}E{episode:02}{range}{?episode_name: - {episode_name}}' season=01 episode=02 range= 'episode_name=')"
assert_eq "S01E02-E03 - A - B" \
"$(render_naming_template 'S{season:02}E{episode:02}{range}{?episode_name: - {episode_name}}' season=01 episode=02 range=-E03 'episode_name=A - B')"
}
# render_naming_template:值含 & 不被替换语义破坏(${var//pat/repl} 会把 & 当匹配整体)
test_render_ampersand_safe() {
assert_eq "A & B" "$(render_naming_template '{episode_name}' 'episode_name=A & B')"
assert_eq "Artist & Band - Album" \
"$(render_naming_template '{artist} - {album}' 'artist=Artist & Band' album=Album)"
}
# render_naming_template:值内花括号被剔除(防破坏模板解析)
test_render_braces_in_value_stripped() {
assert_eq "AB" "$(render_naming_template '{title}' 'title=A{B')"
}
# render_naming_template:未知占位符渲染为空
test_render_unknown_placeholder_empty() {
assert_eq "X " "$(render_naming_template '{title} {foo}' title=X)"
}
# render_naming_template:补零对非数字值原样保留
test_render_padding_non_numeric() {
assert_eq "abc" "$(render_naming_template '{n:03}' n=abc)"
}
# render_naming_template:无配对右花括号按字面保留
test_render_unclosed_brace_literal() {
assert_eq "X {tail" "$(render_naming_template 'X {tail' title=X)"
}
# render_naming:配置键未设置时用内置默认(测试环境不经过 load_config)
test_render_naming_default_fallback() {
assert_eq "Season 03" "$(render_naming NAMING_SEASON season=3)"
assert_eq "Movie (2020)" "$(render_naming NAMING_MOVIE title=Movie year=2020)"
assert_eq "S00Menu - Menu01" "$(render_naming NAMING_SPECIAL tag=Menu fragment=Menu01)"
assert_eq "Artist/Album" "$(render_naming NAMING_MUSIC artist=Artist album=Album)"
}
# template_token_names:提取占位符名(含条件段正文内的;重复名由调用方去重)
test_template_token_names() {
assert_eq "$(printf 'title\nyear' | sort)" "$(template_token_names '{title}{?year: ({year})}' | sort -u)"
assert_eq "$(printf 'season\nepisode\nrange\nepisode_name' | sort)" \
"$(template_token_names 'S{season:02}E{episode:02}{range}{?episode_name: - {episode_name}}' | sort -u)"
}
# rule_key:小写 + 空白折叠 + 去首尾空白
test_rule_key_normalization() {
assert_eq "sword art online" "$(rule_key ' Sword Art ONLINE ')"
assert_eq "sword art online" "$(rule_key 'Sword Art Online')"
assert_eq "俺妹" "$(rule_key '俺妹')"
}