cc-devflow 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +83 -0
- package/.claude/agents/architecture-designer.md +443 -0
- package/.claude/agents/bug-analyzer.md +382 -0
- package/.claude/agents/checklist-agent.md +175 -0
- package/.claude/agents/clarify-analyst.md +50 -0
- package/.claude/agents/code-reviewer.md +71 -0
- package/.claude/agents/codex-analyzer.md +39 -0
- package/.claude/agents/compatibility-checker.md +580 -0
- package/.claude/agents/consistency-checker.md +532 -0
- package/.claude/agents/impact-analyzer.md +441 -0
- package/.claude/agents/planner.md +230 -0
- package/.claude/agents/prd-writer.md +320 -0
- package/.claude/agents/project-guidelines-generator.md +1329 -0
- package/.claude/agents/qa-tester.md +313 -0
- package/.claude/agents/release-manager.md +295 -0
- package/.claude/agents/security-reviewer.md +314 -0
- package/.claude/agents/style-guide-generator.md +458 -0
- package/.claude/agents/tech-architect.md +516 -0
- package/.claude/agents/ui-designer.md +485 -0
- package/.claude/commands/code-review-high.md +58 -0
- package/.claude/commands/core-architecture.md +429 -0
- package/.claude/commands/core-guidelines.md +486 -0
- package/.claude/commands/core-roadmap.md +439 -0
- package/.claude/commands/core-style.md +293 -0
- package/.claude/commands/flow-archive.md +245 -0
- package/.claude/commands/flow-checklist.md +260 -0
- package/.claude/commands/flow-clarify.md +136 -0
- package/.claude/commands/flow-constitution.md +82 -0
- package/.claude/commands/flow-dev.md +134 -0
- package/.claude/commands/flow-epic.md +150 -0
- package/.claude/commands/flow-fix.md +104 -0
- package/.claude/commands/flow-ideate.md +214 -0
- package/.claude/commands/flow-init.md +313 -0
- package/.claude/commands/flow-new.md +394 -0
- package/.claude/commands/flow-prd.md +131 -0
- package/.claude/commands/flow-qa.md +93 -0
- package/.claude/commands/flow-release.md +92 -0
- package/.claude/commands/flow-restart.md +98 -0
- package/.claude/commands/flow-status.md +64 -0
- package/.claude/commands/flow-tech.md +142 -0
- package/.claude/commands/flow-ui.md +189 -0
- package/.claude/commands/flow-update.md +111 -0
- package/.claude/commands/flow-upgrade.md +115 -0
- package/.claude/commands/flow-verify.md +96 -0
- package/.claude/commands/problem-analyzer.md +60 -0
- package/.claude/config/quality-rules.yml +161 -0
- package/.claude/docs/SPEC_KIT_CONSTITUTION_ANALYSIS.md +426 -0
- package/.claude/docs/design/consistency-conflict-detection-algorithms.md +658 -0
- package/.claude/docs/design/intent-driven-input-design.md +380 -0
- package/.claude/docs/design/prd-version-management-design.md +437 -0
- package/.claude/docs/guides/INIT_TROUBLESHOOTING.md +117 -0
- package/.claude/docs/guides/NEW_TROUBLESHOOTING.md +151 -0
- package/.claude/docs/guides/ROADMAP_TROUBLESHOOTING.md +188 -0
- package/.claude/docs/guides/TASK_COMPLETION_MARKING.md +338 -0
- package/.claude/docs/templates/ARCHITECTURE_TEMPLATE.md +633 -0
- package/.claude/docs/templates/BACKLOG_TEMPLATE.md +261 -0
- package/.claude/docs/templates/CHECKLIST_TEMPLATE.md +52 -0
- package/.claude/docs/templates/CLARIFICATION_REPORT_TEMPLATE.md +206 -0
- package/.claude/docs/templates/CODE_REVIEW_TEMPLATE.md +71 -0
- package/.claude/docs/templates/EPIC_TEMPLATE.md +805 -0
- package/.claude/docs/templates/INIT_FLOW_TEMPLATE.md +213 -0
- package/.claude/docs/templates/INTENT_CLARIFICATION_TEMPLATE.md +57 -0
- package/.claude/docs/templates/NEW_ORCHESTRATION_TEMPLATE.md +148 -0
- package/.claude/docs/templates/PRD_TEMPLATE.md +562 -0
- package/.claude/docs/templates/RESEARCH_TEMPLATE.md +276 -0
- package/.claude/docs/templates/REVIEW-HIGH.md +57 -0
- package/.claude/docs/templates/ROADMAP_DIALOGUE_TEMPLATE.md +198 -0
- package/.claude/docs/templates/ROADMAP_TEMPLATE.md +310 -0
- package/.claude/docs/templates/STYLE_TEMPLATE.md +1266 -0
- package/.claude/docs/templates/TASKS_TEMPLATE.md +523 -0
- package/.claude/docs/templates/TECH_DESIGN_TEMPLATE.md +1019 -0
- package/.claude/docs/templates/UI_PROTOTYPE_TEMPLATE.md +1436 -0
- package/.claude/guides/agent-guides/agent-coordination-guide.md +459 -0
- package/.claude/guides/project-guidelines-system.md +463 -0
- package/.claude/guides/technical-guides/datetime-handling-guide.md +563 -0
- package/.claude/guides/technical-guides/git-github-guide.md +642 -0
- package/.claude/guides/technical-guides/test-execution-guide.md +618 -0
- package/.claude/guides/workflow-guides/bug-fix-orchestrator.md +217 -0
- package/.claude/guides/workflow-guides/flow-orchestrator.md +282 -0
- package/.claude/hooks/checklist-gate.js +397 -0
- package/.claude/hooks/error-handling-reminder.sh +12 -0
- package/.claude/hooks/error-handling-reminder.ts +459 -0
- package/.claude/hooks/post-tool-use-tracker.sh +280 -0
- package/.claude/hooks/pre-tool-use-guardrail.sh +36 -0
- package/.claude/hooks/pre-tool-use-guardrail.ts +342 -0
- package/.claude/hooks/skill-activation-prompt.sh +36 -0
- package/.claude/hooks/skill-activation-prompt.ts +214 -0
- package/.claude/hooks/state/skills-used-test-guard.json +3 -0
- package/.claude/rules/devflow-conventions.md +305 -0
- package/.claude/rules/project-constitution.md +748 -0
- package/.claude/schemas/constitution.schema.json +43 -0
- package/.claude/scripts/analyze-upgrade-impact.sh +200 -0
- package/.claude/scripts/archive-requirement.sh +351 -0
- package/.claude/scripts/calculate-checklist-completion.sh +243 -0
- package/.claude/scripts/calculate-quarter.sh +206 -0
- package/.claude/scripts/check-dependencies.sh +409 -0
- package/.claude/scripts/check-prerequisites.sh +232 -0
- package/.claude/scripts/check-task-status.sh +264 -0
- package/.claude/scripts/checklist-errors.sh +131 -0
- package/.claude/scripts/common.sh +570 -0
- package/.claude/scripts/consolidate-research.sh +182 -0
- package/.claude/scripts/create-requirement.sh +426 -0
- package/.claude/scripts/export-contracts.sh +117 -0
- package/.claude/scripts/extract-data-model.sh +78 -0
- package/.claude/scripts/generate-clarification-questions.sh +377 -0
- package/.claude/scripts/generate-clarification-report.sh +463 -0
- package/.claude/scripts/generate-quickstart.sh +146 -0
- package/.claude/scripts/generate-research-tasks.sh +157 -0
- package/.claude/scripts/generate-status-report.sh +523 -0
- package/.claude/scripts/generate-tech-analysis.sh +46 -0
- package/.claude/scripts/locate-requirement-in-roadmap.sh +233 -0
- package/.claude/scripts/manage-constitution.sh +602 -0
- package/.claude/scripts/mark-task-complete.sh +198 -0
- package/.claude/scripts/populate-research-tasks.sh +259 -0
- package/.claude/scripts/recover-workflow.sh +460 -0
- package/.claude/scripts/run-clarify-scan.sh +601 -0
- package/.claude/scripts/run-high-review.sh +62 -0
- package/.claude/scripts/run-problem-analysis.sh +68 -0
- package/.claude/scripts/setup-epic.sh +173 -0
- package/.claude/scripts/sync-roadmap-progress.sh +300 -0
- package/.claude/scripts/sync-task-marks.sh +199 -0
- package/.claude/scripts/test-clarify-scan.sh +515 -0
- package/.claude/scripts/update-agent-context.sh +806 -0
- package/.claude/scripts/validate-constitution.sh +567 -0
- package/.claude/scripts/validate-hooks.sh +487 -0
- package/.claude/scripts/validate-research.sh +332 -0
- package/.claude/scripts/validate-scope-boundary.sh +493 -0
- package/.claude/scripts/verify-setup.sh +37 -0
- package/.claude/settings.json +76 -0
- package/.claude/skills/_reference-implementations/README.md +96 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/SKILL.md +302 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/architecture-overview.md +451 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/async-and-errors.md +307 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/complete-examples.md +638 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/configuration.md +275 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/database-patterns.md +224 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/middleware-guide.md +213 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/routing-and-controllers.md +756 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/sentry-and-monitoring.md +336 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/services-and-repositories.md +789 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/testing-guide.md +235 -0
- package/.claude/skills/_reference-implementations/backend-express-prisma/resources/validation-patterns.md +754 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/SKILL.md +399 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/common-patterns.md +331 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/complete-examples.md +872 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/component-patterns.md +502 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/data-fetching.md +767 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/file-organization.md +502 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/loading-and-error-states.md +501 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/performance.md +406 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/routing-guide.md +364 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/styling-guide.md +428 -0
- package/.claude/skills/_reference-implementations/frontend-react-mui/resources/typescript-standards.md +418 -0
- package/.claude/skills/cc-devflow-orchestrator/SKILL.md +229 -0
- package/.claude/skills/constitution-guardian/SKILL.md +306 -0
- package/.claude/skills/devflow-constitution-quick-ref/SKILL.md +374 -0
- package/.claude/skills/devflow-file-standards/SKILL.md +353 -0
- package/.claude/skills/devflow-tdd-enforcer/SKILL.md +192 -0
- package/.claude/skills/skill-developer/ADVANCED.md +197 -0
- package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +306 -0
- package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +152 -0
- package/.claude/skills/skill-developer/SKILL.md +426 -0
- package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +315 -0
- package/.claude/skills/skill-developer/TRIGGER_TYPES.md +305 -0
- package/.claude/skills/skill-developer/TROUBLESHOOTING.md +514 -0
- package/.claude/skills/skill-rules.json +213 -0
- package/.claude/tests/README.md +300 -0
- package/.claude/tests/TODO.md +69 -0
- package/.claude/tests/__pycache__/test_analyze_upgrade_impact.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_consolidate_research.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_export_contracts.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_extract_data_model.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_generate_quickstart.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/__pycache__/test_generate_research_tasks.cpython-311-pytest-7.2.2.pyc +0 -0
- package/.claude/tests/constitution/run_all_constitution_tests.sh +111 -0
- package/.claude/tests/constitution/test_agent_assignment.sh +207 -0
- package/.claude/tests/constitution/test_article_coverage.sh +201 -0
- package/.claude/tests/constitution/test_template_completeness.sh +150 -0
- package/.claude/tests/constitution/test_version_consistency.sh +120 -0
- package/.claude/tests/fixtures/spec_delta_full.md +16 -0
- package/.claude/tests/fixtures/tasks_progress_sample.md +5 -0
- package/.claude/tests/run-all-tests.sh +229 -0
- package/.claude/tests/scripts/run.sh +30 -0
- package/.claude/tests/scripts/test-framework.sh +128 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh +511 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak +504 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak2 +505 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak3 +506 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak4 +507 -0
- package/.claude/tests/scripts/test_check_prerequisites.sh.bak5 +508 -0
- package/.claude/tests/scripts/test_check_task_status.sh +499 -0
- package/.claude/tests/scripts/test_common.sh +244 -0
- package/.claude/tests/scripts/test_generate_status_report.sh +71 -0
- package/.claude/tests/scripts/test_mark_task_complete.sh +441 -0
- package/.claude/tests/scripts/test_mark_task_complete.sh.backup +410 -0
- package/.claude/tests/scripts/test_recover_workflow.sh +304 -0
- package/.claude/tests/scripts/test_setup_epic.sh +437 -0
- package/.claude/tests/scripts/test_sync_task_marks.sh +196 -0
- package/.claude/tests/scripts/test_validate_constitution.sh +74 -0
- package/.claude/tests/scripts/test_validate_research.sh +462 -0
- package/.claude/tests/slugify.bats +82 -0
- package/.claude/tests/test-framework.sh +732 -0
- package/.claude/tests/test_analyze_upgrade_impact.py +34 -0
- package/.claude/tests/test_consolidate_research.py +48 -0
- package/.claude/tests/test_export_contracts.py +43 -0
- package/.claude/tests/test_extract_data_model.py +33 -0
- package/.claude/tests/test_generate_quickstart.py +50 -0
- package/.claude/tests/test_generate_research_tasks.py +52 -0
- package/.claude/tsc-cache/6e64f818-6398-49ca-8623-581a9af85c44/edited-files.log +1 -0
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +1 -0
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/edited-files.log +1 -0
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +1 -0
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/edited-files.log +1 -0
- package/CHANGELOG.md +507 -0
- package/LICENSE +21 -0
- package/README.md +534 -0
- package/README.zh-CN.md +530 -0
- package/bin/adapt.js +240 -0
- package/bin/cc-devflow-cli.js +185 -0
- package/bin/cc-devflow.js +78 -0
- package/config/adapters.yml +5 -0
- package/config/schema/adapters.schema.json +44 -0
- package/docs/CLAUDE.md +26 -0
- package/docs/commands/README.md +61 -0
- package/docs/commands/README.zh-CN.md +55 -0
- package/docs/commands/core-roadmap.md +106 -0
- package/docs/commands/core-roadmap.zh-CN.md +102 -0
- package/docs/commands/core-style.md +405 -0
- package/docs/commands/core-style.zh-CN.md +405 -0
- package/docs/commands/flow-init.md +134 -0
- package/docs/commands/flow-init.zh-CN.md +163 -0
- package/docs/commands/flow-new.md +274 -0
- package/docs/commands/flow-new.zh-CN.md +270 -0
- package/docs/guides/getting-started.md +204 -0
- package/docs/guides/getting-started.zh-CN.md +152 -0
- package/lib/adapters/adapter-interface.js +57 -0
- package/lib/adapters/claude-adapter.js +74 -0
- package/lib/adapters/codex-adapter.js +40 -0
- package/lib/adapters/config-validator.js +68 -0
- package/lib/adapters/logger.js +42 -0
- package/lib/adapters/registry.js +153 -0
- package/lib/compiler/CLAUDE.md +92 -0
- package/lib/compiler/__tests__/drift.test.js +215 -0
- package/lib/compiler/__tests__/errors.test.js +184 -0
- package/lib/compiler/__tests__/incremental.test.js +174 -0
- package/lib/compiler/__tests__/integration.test.js +174 -0
- package/lib/compiler/__tests__/manifest.test.js +233 -0
- package/lib/compiler/__tests__/parser.test.js +456 -0
- package/lib/compiler/__tests__/schemas.test.js +301 -0
- package/lib/compiler/__tests__/skills-registry.test.js +125 -0
- package/lib/compiler/__tests__/transformer.test.js +286 -0
- package/lib/compiler/emitters/antigravity-emitter.js +171 -0
- package/lib/compiler/emitters/base-emitter.js +73 -0
- package/lib/compiler/emitters/codex-emitter.js +52 -0
- package/lib/compiler/emitters/cursor-emitter.js +31 -0
- package/lib/compiler/emitters/index.js +50 -0
- package/lib/compiler/emitters/qwen-emitter.js +39 -0
- package/lib/compiler/errors.js +119 -0
- package/lib/compiler/index.js +256 -0
- package/lib/compiler/manifest.js +242 -0
- package/lib/compiler/parser.js +258 -0
- package/lib/compiler/platforms.js +113 -0
- package/lib/compiler/resource-copier.js +320 -0
- package/lib/compiler/rules-emitters/__tests__/antigravity-rules-emitter.test.js +191 -0
- package/lib/compiler/rules-emitters/__tests__/codex-rules-emitter.test.js +109 -0
- package/lib/compiler/rules-emitters/__tests__/cursor-rules-emitter.test.js +123 -0
- package/lib/compiler/rules-emitters/__tests__/qwen-rules-emitter.test.js +123 -0
- package/lib/compiler/rules-emitters/antigravity-rules-emitter.js +253 -0
- package/lib/compiler/rules-emitters/base-rules-emitter.js +83 -0
- package/lib/compiler/rules-emitters/codex-rules-emitter.js +116 -0
- package/lib/compiler/rules-emitters/cursor-rules-emitter.js +98 -0
- package/lib/compiler/rules-emitters/index.js +71 -0
- package/lib/compiler/rules-emitters/qwen-rules-emitter.js +70 -0
- package/lib/compiler/schemas.js +144 -0
- package/lib/compiler/skills-registry.js +225 -0
- package/lib/compiler/transformer.js +236 -0
- package/package.json +50 -0
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test-framework.sh - 测试框架核心功能
|
|
3
|
+
# 提供断言、mock、测试隔离和报告功能
|
|
4
|
+
|
|
5
|
+
# 颜色定义
|
|
6
|
+
RED='\033[0;31m'
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
YELLOW='\033[0;33m'
|
|
9
|
+
BLUE='\033[0;34m'
|
|
10
|
+
BOLD='\033[1m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
# 测试统计
|
|
14
|
+
TESTS_RUN=0
|
|
15
|
+
TESTS_PASSED=0
|
|
16
|
+
TESTS_FAILED=0
|
|
17
|
+
CURRENT_TEST_NAME=""
|
|
18
|
+
|
|
19
|
+
# 测试输出控制
|
|
20
|
+
VERBOSE=${VERBOSE:-false}
|
|
21
|
+
QUIET=${QUIET:-false}
|
|
22
|
+
|
|
23
|
+
# 临时目录
|
|
24
|
+
TEST_TMP_DIR=""
|
|
25
|
+
|
|
26
|
+
# ============================================================================
|
|
27
|
+
# 测试生命周期管理
|
|
28
|
+
# ============================================================================
|
|
29
|
+
|
|
30
|
+
# 初始化测试环境
|
|
31
|
+
init_test_framework() {
|
|
32
|
+
TESTS_RUN=0
|
|
33
|
+
TESTS_PASSED=0
|
|
34
|
+
TESTS_FAILED=0
|
|
35
|
+
|
|
36
|
+
# 创建临时测试目录
|
|
37
|
+
TEST_TMP_DIR=$(mktemp -d -t cc-devflow-test.XXXXXX)
|
|
38
|
+
export TEST_ROOT="$TEST_TMP_DIR"
|
|
39
|
+
|
|
40
|
+
if [[ "$VERBOSE" == "true" ]]; then
|
|
41
|
+
echo -e "${BLUE}Test framework initialized${NC}"
|
|
42
|
+
echo -e "${BLUE}Temporary directory: $TEST_TMP_DIR${NC}"
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# 清理测试环境
|
|
47
|
+
cleanup_test_framework() {
|
|
48
|
+
if [[ -n "$TEST_TMP_DIR" && -d "$TEST_TMP_DIR" ]]; then
|
|
49
|
+
rm -rf "$TEST_TMP_DIR"
|
|
50
|
+
if [[ "$VERBOSE" == "true" ]]; then
|
|
51
|
+
echo -e "${BLUE}Cleaned up temporary directory${NC}"
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# 测试开始
|
|
57
|
+
test_start() {
|
|
58
|
+
local test_name="$1"
|
|
59
|
+
CURRENT_TEST_NAME="$test_name"
|
|
60
|
+
((TESTS_RUN++))
|
|
61
|
+
|
|
62
|
+
if [[ "$VERBOSE" == "true" ]]; then
|
|
63
|
+
echo -e "${BLUE}▶ Running: $test_name${NC}"
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# 测试成功
|
|
68
|
+
test_pass() {
|
|
69
|
+
((TESTS_PASSED++))
|
|
70
|
+
|
|
71
|
+
if [[ "$QUIET" != "true" ]]; then
|
|
72
|
+
echo -e "${GREEN}✓ PASS${NC}: $CURRENT_TEST_NAME"
|
|
73
|
+
fi
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# 测试失败
|
|
77
|
+
test_fail() {
|
|
78
|
+
local message="$1"
|
|
79
|
+
((TESTS_FAILED++))
|
|
80
|
+
|
|
81
|
+
echo -e "${RED}✗ FAIL${NC}: $CURRENT_TEST_NAME"
|
|
82
|
+
echo -e "${RED} Reason: $message${NC}"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# ============================================================================
|
|
86
|
+
# 断言函数
|
|
87
|
+
# ============================================================================
|
|
88
|
+
|
|
89
|
+
# 断言相等
|
|
90
|
+
assert_equals() {
|
|
91
|
+
local actual="$1"
|
|
92
|
+
local expected="$2"
|
|
93
|
+
local message="${3:-Values should be equal}"
|
|
94
|
+
|
|
95
|
+
if [[ "$actual" == "$expected" ]]; then
|
|
96
|
+
return 0
|
|
97
|
+
else
|
|
98
|
+
test_fail "$message"
|
|
99
|
+
echo -e "${RED} Expected: '$expected'${NC}"
|
|
100
|
+
echo -e "${RED} Actual: '$actual'${NC}"
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# 断言不相等
|
|
106
|
+
assert_not_equals() {
|
|
107
|
+
local actual="$1"
|
|
108
|
+
local unexpected="$2"
|
|
109
|
+
local message="${3:-Values should not be equal}"
|
|
110
|
+
|
|
111
|
+
if [[ "$actual" != "$unexpected" ]]; then
|
|
112
|
+
return 0
|
|
113
|
+
else
|
|
114
|
+
test_fail "$message"
|
|
115
|
+
echo -e "${RED} Unexpected value: '$unexpected'${NC}"
|
|
116
|
+
echo -e "${RED} Actual: '$actual'${NC}"
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# 断言包含
|
|
122
|
+
assert_contains() {
|
|
123
|
+
local haystack="$1"
|
|
124
|
+
local needle="$2"
|
|
125
|
+
local message="${3:-String should contain substring}"
|
|
126
|
+
|
|
127
|
+
if [[ "$haystack" == *"$needle"* ]]; then
|
|
128
|
+
return 0
|
|
129
|
+
else
|
|
130
|
+
test_fail "$message"
|
|
131
|
+
echo -e "${RED} Looking for: '$needle'${NC}"
|
|
132
|
+
echo -e "${RED} In string: '$haystack'${NC}"
|
|
133
|
+
return 1
|
|
134
|
+
fi
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# 断言不包含
|
|
138
|
+
assert_not_contains() {
|
|
139
|
+
local haystack="$1"
|
|
140
|
+
local needle="$2"
|
|
141
|
+
local message="${3:-String should not contain substring}"
|
|
142
|
+
|
|
143
|
+
if [[ "$haystack" != *"$needle"* ]]; then
|
|
144
|
+
return 0
|
|
145
|
+
else
|
|
146
|
+
test_fail "$message"
|
|
147
|
+
echo -e "${RED} Should not contain: '$needle'${NC}"
|
|
148
|
+
echo -e "${RED} But found in: '$haystack'${NC}"
|
|
149
|
+
return 1
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# 断言文件存在
|
|
154
|
+
assert_file_exists() {
|
|
155
|
+
local path="$1"
|
|
156
|
+
local message="${2:-File should exist}"
|
|
157
|
+
|
|
158
|
+
if [[ -f "$path" ]]; then
|
|
159
|
+
return 0
|
|
160
|
+
else
|
|
161
|
+
test_fail "$message"
|
|
162
|
+
echo -e "${RED} File not found: '$path'${NC}"
|
|
163
|
+
return 1
|
|
164
|
+
fi
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# 断言文件不存在
|
|
168
|
+
assert_file_not_exists() {
|
|
169
|
+
local path="$1"
|
|
170
|
+
local message="${2:-File should not exist}"
|
|
171
|
+
|
|
172
|
+
if [[ ! -f "$path" ]]; then
|
|
173
|
+
return 0
|
|
174
|
+
else
|
|
175
|
+
test_fail "$message"
|
|
176
|
+
echo -e "${RED} File exists: '$path'${NC}"
|
|
177
|
+
return 1
|
|
178
|
+
fi
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# 断言目录存在
|
|
182
|
+
assert_dir_exists() {
|
|
183
|
+
local path="$1"
|
|
184
|
+
local message="${2:-Directory should exist}"
|
|
185
|
+
|
|
186
|
+
if [[ -d "$path" ]]; then
|
|
187
|
+
return 0
|
|
188
|
+
else
|
|
189
|
+
test_fail "$message"
|
|
190
|
+
echo -e "${RED} Directory not found: '$path'${NC}"
|
|
191
|
+
return 1
|
|
192
|
+
fi
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# 断言目录不存在
|
|
196
|
+
assert_dir_not_exists() {
|
|
197
|
+
local path="$1"
|
|
198
|
+
local message="${2:-Directory should not exist}"
|
|
199
|
+
|
|
200
|
+
if [[ ! -d "$path" ]]; then
|
|
201
|
+
return 0
|
|
202
|
+
else
|
|
203
|
+
test_fail "$message"
|
|
204
|
+
echo -e "${RED} Directory exists: '$path'${NC}"
|
|
205
|
+
return 1
|
|
206
|
+
fi
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# 断言 JSON 有效
|
|
210
|
+
assert_json_valid() {
|
|
211
|
+
local json="$1"
|
|
212
|
+
local message="${2:-JSON should be valid}"
|
|
213
|
+
|
|
214
|
+
if echo "$json" | jq . >/dev/null 2>&1; then
|
|
215
|
+
return 0
|
|
216
|
+
else
|
|
217
|
+
test_fail "$message"
|
|
218
|
+
echo -e "${RED} Invalid JSON: '$json'${NC}"
|
|
219
|
+
return 1
|
|
220
|
+
fi
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# 断言 JSON 字段值
|
|
224
|
+
assert_json_field() {
|
|
225
|
+
local json="$1"
|
|
226
|
+
local field="$2"
|
|
227
|
+
local expected="$3"
|
|
228
|
+
local message="${4:-JSON field should match expected value}"
|
|
229
|
+
|
|
230
|
+
local actual=$(echo "$json" | jq -r ".$field" 2>/dev/null)
|
|
231
|
+
|
|
232
|
+
if [[ "$actual" == "$expected" ]]; then
|
|
233
|
+
return 0
|
|
234
|
+
else
|
|
235
|
+
test_fail "$message"
|
|
236
|
+
echo -e "${RED} Field: '$field'${NC}"
|
|
237
|
+
echo -e "${RED} Expected: '$expected'${NC}"
|
|
238
|
+
echo -e "${RED} Actual: '$actual'${NC}"
|
|
239
|
+
return 1
|
|
240
|
+
fi
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# 断言退出码
|
|
244
|
+
assert_exit_code() {
|
|
245
|
+
local expected_code="$1"
|
|
246
|
+
shift
|
|
247
|
+
local command="$@"
|
|
248
|
+
local message="Command should exit with code $expected_code"
|
|
249
|
+
|
|
250
|
+
# 执行命令并捕获退出码
|
|
251
|
+
local output
|
|
252
|
+
local actual_code
|
|
253
|
+
output=$($command 2>&1) || actual_code=$?
|
|
254
|
+
actual_code=${actual_code:-0}
|
|
255
|
+
|
|
256
|
+
if [[ "$actual_code" -eq "$expected_code" ]]; then
|
|
257
|
+
return 0
|
|
258
|
+
else
|
|
259
|
+
test_fail "$message"
|
|
260
|
+
echo -e "${RED} Command: $command${NC}"
|
|
261
|
+
echo -e "${RED} Expected exit code: $expected_code${NC}"
|
|
262
|
+
echo -e "${RED} Actual exit code: $actual_code${NC}"
|
|
263
|
+
if [[ -n "$output" ]]; then
|
|
264
|
+
echo -e "${RED} Output: $output${NC}"
|
|
265
|
+
fi
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# 断言命令成功
|
|
271
|
+
assert_success() {
|
|
272
|
+
local command="$@"
|
|
273
|
+
assert_exit_code 0 "$@"
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# 断言命令失败
|
|
277
|
+
assert_failure() {
|
|
278
|
+
local command="$@"
|
|
279
|
+
local message="Command should fail"
|
|
280
|
+
|
|
281
|
+
# 执行命令并捕获退出码
|
|
282
|
+
local output
|
|
283
|
+
local actual_code
|
|
284
|
+
output=$($command 2>&1) || actual_code=$?
|
|
285
|
+
actual_code=${actual_code:-0}
|
|
286
|
+
|
|
287
|
+
if [[ "$actual_code" -ne 0 ]]; then
|
|
288
|
+
return 0
|
|
289
|
+
else
|
|
290
|
+
test_fail "$message"
|
|
291
|
+
echo -e "${RED} Command: $command${NC}"
|
|
292
|
+
echo -e "${RED} Expected: failure (exit code != 0)${NC}"
|
|
293
|
+
echo -e "${RED} Actual: success (exit code 0)${NC}"
|
|
294
|
+
return 1
|
|
295
|
+
fi
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
# 断言非空
|
|
299
|
+
assert_not_empty() {
|
|
300
|
+
local value="$1"
|
|
301
|
+
local message="${2:-Value should not be empty}"
|
|
302
|
+
|
|
303
|
+
if [[ -n "$value" ]]; then
|
|
304
|
+
return 0
|
|
305
|
+
else
|
|
306
|
+
test_fail "$message"
|
|
307
|
+
echo -e "${RED} Value is empty${NC}"
|
|
308
|
+
return 1
|
|
309
|
+
fi
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# 断言匹配正则表达式
|
|
313
|
+
assert_matches() {
|
|
314
|
+
local value="$1"
|
|
315
|
+
local pattern="$2"
|
|
316
|
+
local message="${3:-Value should match pattern}"
|
|
317
|
+
|
|
318
|
+
if [[ "$value" =~ $pattern ]]; then
|
|
319
|
+
return 0
|
|
320
|
+
else
|
|
321
|
+
test_fail "$message"
|
|
322
|
+
echo -e "${RED} Value: '$value'${NC}"
|
|
323
|
+
echo -e "${RED} Pattern: '$pattern'${NC}"
|
|
324
|
+
return 1
|
|
325
|
+
fi
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# 断言大于等于
|
|
329
|
+
assert_gte() {
|
|
330
|
+
local actual="$1"
|
|
331
|
+
local expected="$2"
|
|
332
|
+
local message="${3:-Value should be >= expected}"
|
|
333
|
+
|
|
334
|
+
if [[ "$actual" -ge "$expected" ]]; then
|
|
335
|
+
return 0
|
|
336
|
+
else
|
|
337
|
+
test_fail "$message"
|
|
338
|
+
echo -e "${RED} Expected: >= $expected${NC}"
|
|
339
|
+
echo -e "${RED} Actual: $actual${NC}"
|
|
340
|
+
return 1
|
|
341
|
+
fi
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
# 断言大于
|
|
345
|
+
assert_gt() {
|
|
346
|
+
local actual="$1"
|
|
347
|
+
local expected="$2"
|
|
348
|
+
local message="${3:-Value should be > expected}"
|
|
349
|
+
|
|
350
|
+
if [[ "$actual" -gt "$expected" ]]; then
|
|
351
|
+
return 0
|
|
352
|
+
else
|
|
353
|
+
test_fail "$message"
|
|
354
|
+
echo -e "${RED} Expected: > $expected${NC}"
|
|
355
|
+
echo -e "${RED} Actual: $actual${NC}"
|
|
356
|
+
return 1
|
|
357
|
+
fi
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
# 断言文件可执行
|
|
361
|
+
assert_file_executable() {
|
|
362
|
+
local file="$1"
|
|
363
|
+
local message="${2:-File should be executable}"
|
|
364
|
+
|
|
365
|
+
if [[ -x "$file" ]]; then
|
|
366
|
+
return 0
|
|
367
|
+
else
|
|
368
|
+
test_fail "$message"
|
|
369
|
+
echo -e "${RED} File: '$file'${NC}"
|
|
370
|
+
echo -e "${RED} Not executable or does not exist${NC}"
|
|
371
|
+
return 1
|
|
372
|
+
fi
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
# 断言文件包含内容
|
|
376
|
+
assert_file_contains() {
|
|
377
|
+
local file="$1"
|
|
378
|
+
local pattern="$2"
|
|
379
|
+
local message="${3:-File should contain pattern}"
|
|
380
|
+
|
|
381
|
+
if [[ ! -f "$file" ]]; then
|
|
382
|
+
test_fail "$message"
|
|
383
|
+
echo -e "${RED} File not found: '$file'${NC}"
|
|
384
|
+
return 1
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
if grep -qE "$pattern" "$file"; then
|
|
388
|
+
return 0
|
|
389
|
+
else
|
|
390
|
+
test_fail "$message"
|
|
391
|
+
echo -e "${RED} File: '$file'${NC}"
|
|
392
|
+
echo -e "${RED} Pattern not found: '$pattern'${NC}"
|
|
393
|
+
return 1
|
|
394
|
+
fi
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
# ============================================================================
|
|
398
|
+
# Mock 系统
|
|
399
|
+
# ============================================================================
|
|
400
|
+
|
|
401
|
+
# Mock Git 命令 (增强版)
|
|
402
|
+
mock_git() {
|
|
403
|
+
local git_command="$1"
|
|
404
|
+
local mock_output="$2"
|
|
405
|
+
local mock_exit_code="${3:-0}"
|
|
406
|
+
|
|
407
|
+
# 确保 TEST_TMP_DIR 存在
|
|
408
|
+
if [[ -z "$TEST_TMP_DIR" ]]; then
|
|
409
|
+
echo "ERROR: TEST_TMP_DIR is not set. Call run_tests first." >&2
|
|
410
|
+
return 1
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
# 创建或追加到 mock git 脚本 - 文件名必须是 "git" 才能被 PATH 找到
|
|
414
|
+
local mock_git_path="$TEST_TMP_DIR/git"
|
|
415
|
+
|
|
416
|
+
if [[ ! -f "$mock_git_path" ]]; then
|
|
417
|
+
# 创建新的 mock git 脚本
|
|
418
|
+
cat > "$mock_git_path" << 'HEADER'
|
|
419
|
+
#!/usr/bin/env bash
|
|
420
|
+
# Mock git script - supports multiple command mocks
|
|
421
|
+
|
|
422
|
+
HEADER
|
|
423
|
+
chmod +x "$mock_git_path"
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
# 追加新的命令匹配逻辑
|
|
427
|
+
cat >> "$mock_git_path" << EOF
|
|
428
|
+
if [[ "\$*" == "$git_command" ]]; then
|
|
429
|
+
echo "$mock_output"
|
|
430
|
+
exit $mock_exit_code
|
|
431
|
+
fi
|
|
432
|
+
|
|
433
|
+
EOF
|
|
434
|
+
|
|
435
|
+
# 如果还没有默认处理,添加它
|
|
436
|
+
if ! grep -q "/usr/bin/git" "$mock_git_path" 2>/dev/null; then
|
|
437
|
+
cat >> "$mock_git_path" << 'FOOTER'
|
|
438
|
+
# 传递给真实的 git (使用绝对路径避免递归)
|
|
439
|
+
/usr/bin/git "$@"
|
|
440
|
+
FOOTER
|
|
441
|
+
fi
|
|
442
|
+
|
|
443
|
+
# 添加到 PATH 开头
|
|
444
|
+
export PATH="$TEST_TMP_DIR:$PATH"
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
# 清除所有 Git mocks
|
|
448
|
+
clear_git_mocks() {
|
|
449
|
+
local mock_git_path="$TEST_TMP_DIR/git"
|
|
450
|
+
[[ -f "$mock_git_path" ]] && rm "$mock_git_path"
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
# Mock 文件
|
|
454
|
+
mock_file() {
|
|
455
|
+
local path="$1"
|
|
456
|
+
local content="$2"
|
|
457
|
+
|
|
458
|
+
# 确保目录存在
|
|
459
|
+
mkdir -p "$(dirname "$path")"
|
|
460
|
+
|
|
461
|
+
# 创建文件
|
|
462
|
+
echo "$content" > "$path"
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
# Mock JSON 文件
|
|
466
|
+
mock_json_file() {
|
|
467
|
+
local path="$1"
|
|
468
|
+
local json_content="$2"
|
|
469
|
+
|
|
470
|
+
# 确保目录存在
|
|
471
|
+
mkdir -p "$(dirname "$path")"
|
|
472
|
+
|
|
473
|
+
# 创建 JSON 文件
|
|
474
|
+
echo "$json_content" > "$path"
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
# Mock 函数
|
|
478
|
+
mock_function() {
|
|
479
|
+
local function_name="$1"
|
|
480
|
+
local mock_behavior="$2"
|
|
481
|
+
|
|
482
|
+
eval "$function_name() { $mock_behavior; }"
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
# 恢复函数 (从 mock 恢复)
|
|
486
|
+
restore_function() {
|
|
487
|
+
local function_name="$1"
|
|
488
|
+
unset -f "$function_name"
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
# Mock 目录结构 - 创建完整的目录树
|
|
492
|
+
mock_directory_tree() {
|
|
493
|
+
local base_dir="$1"
|
|
494
|
+
shift
|
|
495
|
+
local paths=("$@")
|
|
496
|
+
|
|
497
|
+
for path in "${paths[@]}"; do
|
|
498
|
+
if [[ "$path" == */ ]]; then
|
|
499
|
+
# 目录
|
|
500
|
+
mkdir -p "$base_dir/$path"
|
|
501
|
+
else
|
|
502
|
+
# 文件
|
|
503
|
+
mkdir -p "$(dirname "$base_dir/$path")"
|
|
504
|
+
touch "$base_dir/$path"
|
|
505
|
+
fi
|
|
506
|
+
done
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
# Mock 命令 - 创建假的命令脚本
|
|
510
|
+
mock_command() {
|
|
511
|
+
local command_name="$1"
|
|
512
|
+
local mock_output="$2"
|
|
513
|
+
local mock_exit_code="${3:-0}"
|
|
514
|
+
|
|
515
|
+
local mock_cmd_path="$TEST_TMP_DIR/mock_$command_name"
|
|
516
|
+
cat > "$mock_cmd_path" << EOF
|
|
517
|
+
#!/usr/bin/env bash
|
|
518
|
+
echo "$mock_output"
|
|
519
|
+
exit $mock_exit_code
|
|
520
|
+
EOF
|
|
521
|
+
chmod +x "$mock_cmd_path"
|
|
522
|
+
export PATH="$TEST_TMP_DIR:$PATH"
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
# Mock 时间戳 - 为测试创建一致的时间戳
|
|
526
|
+
mock_timestamp() {
|
|
527
|
+
local timestamp="${1:-2025-10-01T00:00:00Z}"
|
|
528
|
+
export MOCK_TIMESTAMP="$timestamp"
|
|
529
|
+
|
|
530
|
+
# Mock date 命令
|
|
531
|
+
mock_command "date" "$timestamp"
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
# 恢复所有 mocks
|
|
535
|
+
restore_all_mocks() {
|
|
536
|
+
# 移除 mock 目录中的所有 mock 脚本
|
|
537
|
+
find "$TEST_TMP_DIR" -name "mock_*" -type f -delete 2>/dev/null || true
|
|
538
|
+
|
|
539
|
+
# 清理环境变量
|
|
540
|
+
unset MOCK_TIMESTAMP
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
# ============================================================================
|
|
544
|
+
# 测试辅助函数
|
|
545
|
+
# ============================================================================
|
|
546
|
+
|
|
547
|
+
# 描述测试
|
|
548
|
+
describe() {
|
|
549
|
+
local description="$1"
|
|
550
|
+
if [[ "$VERBOSE" == "true" ]]; then
|
|
551
|
+
echo -e "${BLUE} $description${NC}"
|
|
552
|
+
fi
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
# 创建测试目录
|
|
556
|
+
create_test_dir() {
|
|
557
|
+
local dir_path="$1"
|
|
558
|
+
mkdir -p "$TEST_TMP_DIR/$dir_path"
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
# 创建测试文件
|
|
562
|
+
create_test_file() {
|
|
563
|
+
local file_path="$1"
|
|
564
|
+
local content="$2"
|
|
565
|
+
|
|
566
|
+
mkdir -p "$TEST_TMP_DIR/$(dirname "$file_path")"
|
|
567
|
+
echo "$content" > "$TEST_TMP_DIR/$file_path"
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
# 读取测试文件
|
|
571
|
+
read_test_file() {
|
|
572
|
+
local file_path="$1"
|
|
573
|
+
cat "$TEST_TMP_DIR/$file_path"
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
# 设置环境变量
|
|
577
|
+
set_test_env() {
|
|
578
|
+
local var_name="$1"
|
|
579
|
+
local var_value="$2"
|
|
580
|
+
export "$var_name=$var_value"
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
# 清理环境变量
|
|
584
|
+
unset_test_env() {
|
|
585
|
+
local var_name="$1"
|
|
586
|
+
unset "$var_name"
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
# ============================================================================
|
|
590
|
+
# 测试运行器
|
|
591
|
+
# ============================================================================
|
|
592
|
+
|
|
593
|
+
# 运行测试函数列表
|
|
594
|
+
run_tests() {
|
|
595
|
+
local test_functions=("$@")
|
|
596
|
+
|
|
597
|
+
# 初始化测试框架
|
|
598
|
+
init_test_framework
|
|
599
|
+
|
|
600
|
+
# 打印测试开始标题
|
|
601
|
+
if [[ "$QUIET" != "true" ]]; then
|
|
602
|
+
echo -e "${BOLD}${BLUE}═══════════════════════════════════════════════${NC}"
|
|
603
|
+
echo -e "${BOLD}${BLUE} Running Test Suite${NC}"
|
|
604
|
+
echo -e "${BOLD}${BLUE}═══════════════════════════════════════════════${NC}"
|
|
605
|
+
echo ""
|
|
606
|
+
fi
|
|
607
|
+
|
|
608
|
+
# 运行每个测试
|
|
609
|
+
for test_func in "${test_functions[@]}"; do
|
|
610
|
+
test_start "$test_func"
|
|
611
|
+
|
|
612
|
+
# 设置每个测试的独立环境
|
|
613
|
+
if type setup_test &>/dev/null; then
|
|
614
|
+
setup_test
|
|
615
|
+
fi
|
|
616
|
+
|
|
617
|
+
# 运行测试函数
|
|
618
|
+
if $test_func; then
|
|
619
|
+
test_pass
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
# 清理测试环境
|
|
623
|
+
if type teardown_test &>/dev/null; then
|
|
624
|
+
teardown_test
|
|
625
|
+
fi
|
|
626
|
+
done
|
|
627
|
+
|
|
628
|
+
# 清理测试框架
|
|
629
|
+
cleanup_test_framework
|
|
630
|
+
|
|
631
|
+
# 打印测试结果摘要
|
|
632
|
+
print_test_summary
|
|
633
|
+
|
|
634
|
+
# 返回失败数量
|
|
635
|
+
return $TESTS_FAILED
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
# 打印测试摘要
|
|
639
|
+
print_test_summary() {
|
|
640
|
+
echo ""
|
|
641
|
+
echo -e "${BOLD}${BLUE}═══════════════════════════════════════════════${NC}"
|
|
642
|
+
echo -e "${BOLD}${BLUE} Test Results${NC}"
|
|
643
|
+
echo -e "${BOLD}${BLUE}═══════════════════════════════════════════════${NC}"
|
|
644
|
+
echo ""
|
|
645
|
+
echo -e "Total: ${BOLD}$TESTS_RUN${NC}"
|
|
646
|
+
echo -e "Passed: ${GREEN}${BOLD}$TESTS_PASSED${NC}"
|
|
647
|
+
echo -e "Failed: ${RED}${BOLD}$TESTS_FAILED${NC}"
|
|
648
|
+
echo ""
|
|
649
|
+
|
|
650
|
+
if [[ $TESTS_FAILED -eq 0 ]]; then
|
|
651
|
+
echo -e "${GREEN}${BOLD}✓ ALL TESTS PASSED${NC}"
|
|
652
|
+
else
|
|
653
|
+
echo -e "${RED}${BOLD}✗ SOME TESTS FAILED${NC}"
|
|
654
|
+
fi
|
|
655
|
+
echo ""
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
# ============================================================================
|
|
659
|
+
# 覆盖率跟踪 (简单实现)
|
|
660
|
+
# ============================================================================
|
|
661
|
+
|
|
662
|
+
# 开始覆盖率跟踪
|
|
663
|
+
start_coverage() {
|
|
664
|
+
export COVERAGE_ENABLED=true
|
|
665
|
+
export COVERAGE_DIR="$TEST_TMP_DIR/coverage"
|
|
666
|
+
mkdir -p "$COVERAGE_DIR"
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
# 结束覆盖率跟踪
|
|
670
|
+
end_coverage() {
|
|
671
|
+
if [[ "$COVERAGE_ENABLED" == "true" ]]; then
|
|
672
|
+
# 生成覆盖率报告
|
|
673
|
+
generate_coverage_report
|
|
674
|
+
fi
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
# 生成覆盖率报告 (占位符 - 完整实现需要 bash 代码覆盖率工具)
|
|
678
|
+
generate_coverage_report() {
|
|
679
|
+
echo "Coverage reporting not yet implemented"
|
|
680
|
+
echo "TODO: Integrate bash coverage tool (e.g., kcov, bashcov)"
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
# ============================================================================
|
|
684
|
+
# 日志函数 (用于测试输出)
|
|
685
|
+
# ============================================================================
|
|
686
|
+
|
|
687
|
+
# 成功日志
|
|
688
|
+
log_success() {
|
|
689
|
+
local message="$1"
|
|
690
|
+
if [[ "$QUIET" != "true" ]]; then
|
|
691
|
+
echo -e "${GREEN}✓${NC} $message"
|
|
692
|
+
fi
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
# 错误日志
|
|
696
|
+
log_error() {
|
|
697
|
+
local message="$1"
|
|
698
|
+
echo -e "${RED}✗${NC} $message" >&2
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
# 警告日志
|
|
702
|
+
log_warning() {
|
|
703
|
+
local message="$1"
|
|
704
|
+
if [[ "$QUIET" != "true" ]]; then
|
|
705
|
+
echo -e "${YELLOW}⚠${NC} $message"
|
|
706
|
+
fi
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
# 信息日志
|
|
710
|
+
log_info() {
|
|
711
|
+
local message="$1"
|
|
712
|
+
if [[ "$QUIET" != "true" ]]; then
|
|
713
|
+
echo -e "${BLUE}ℹ${NC} $message"
|
|
714
|
+
fi
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
# ============================================================================
|
|
718
|
+
# 导出函数
|
|
719
|
+
# ============================================================================
|
|
720
|
+
|
|
721
|
+
# 使函数可用于调用此框架的脚本
|
|
722
|
+
export -f test_start test_pass test_fail
|
|
723
|
+
export -f log_success log_error log_warning log_info
|
|
724
|
+
export -f assert_equals assert_not_equals assert_contains assert_not_contains
|
|
725
|
+
export -f assert_file_exists assert_file_not_exists
|
|
726
|
+
export -f assert_dir_exists assert_dir_not_exists
|
|
727
|
+
export -f assert_json_valid assert_json_field
|
|
728
|
+
export -f assert_exit_code assert_success assert_failure
|
|
729
|
+
export -f mock_git mock_file mock_json_file mock_function restore_function
|
|
730
|
+
export -f describe create_test_dir create_test_file read_test_file
|
|
731
|
+
export -f set_test_env unset_test_env
|
|
732
|
+
export -f run_tests print_test_summary
|