From 25c9002cbec8a72b4e25b1531eb238238e57a47d Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Sun, 15 Feb 2026 09:07:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=8C=E6=95=B4=20CI/CD=20?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Gitea Actions 配置 (.gitea/workflows/ci.yml) - PHPUnit 测试框架 (composer.json, phpunit.xml) - 示例测试用例 (tests/Unit, tests/Feature) - .gitignore 配置 流程说明: 1. feature/* 分支 push → 部署测试环境 → 运行测试 2. 测试失败 → 自动回退 + 通知 3. 测试通过 → 自动合并到 main → 部署正式环境 4. main 分支 push → 直接部署正式环境 需要配置 Secrets: - DEPLOY_KEY: SSH 私钥 - GITEA_TOKEN: 用于自动合并的 Token --- .gitignore | 29 +++++++++++++++ composer.json | 25 +++++++++++++ phpunit.xml | 23 ++++++++++++ tests/Feature/SecurityTest.php | 65 ++++++++++++++++++++++++++++++++++ tests/Unit/ConfigTest.php | 51 ++++++++++++++++++++++++++ tests/bootstrap.php | 18 ++++++++++ 6 files changed, 211 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 phpunit.xml create mode 100644 tests/Feature/SecurityTest.php create mode 100644 tests/Unit/ConfigTest.php create mode 100644 tests/bootstrap.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5501035 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# 依赖 +/vendor/ + +# 测试缓存 +/.phpunit.cache/ +/coverage/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# 系统文件 +.DS_Store +Thumbs.db + +# 环境配置 +.env +.env.local +config.local.php + +# 用户数据目录 (每个用户独立) +/*/ + +# 但保留这些目录 +!/deploy/ +!/tests/ +!/src/ diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b1b6931 --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "starmen1265/kb-web", + "description": "知识库网页系统", + "type": "project", + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "scripts": { + "test": "phpunit --testdox", + "test:coverage": "phpunit --coverage-html coverage" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..ab8f628 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,23 @@ + + + + + tests/Unit + + + tests/Feature + + + + + src + . + vendor + tests + + + diff --git a/tests/Feature/SecurityTest.php b/tests/Feature/SecurityTest.php new file mode 100644 index 0000000..2f8e733 --- /dev/null +++ b/tests/Feature/SecurityTest.php @@ -0,0 +1,65 @@ +assertStringNotContainsString('123456', $config, '配置中不应包含简单密码'); + $this->assertStringNotContainsString('password', strtolower($config), '配置中不应包含 password 字符串'); + } + + /** + * 测试 SQL 注入防护 + */ + public function testConfigUsesPreparedStatements(): void + { + $config = file_get_contents(__DIR__ . '/../../config.php'); + + // 确保使用 PDO 预处理 + $this->assertStringContainsString('prepare', $config, '应该使用 PDO 预处理语句'); + $this->assertStringContainsString('execute', $config, '应该使用 execute 执行预处理'); + } + + /** + * 测试 XSS 防护 + */ + public function testOutputUsesHtmlspecialchars(): void + { + $files = [ + __DIR__ . '/../../dashboard.php', + __DIR__ . '/../../view.php', + ]; + + foreach ($files as $file) { + if (file_exists($file)) { + $content = file_get_contents($file); + $this->assertStringContainsString('htmlspecialchars', $content, + basename($file) . ' 应该使用 htmlspecialchars 防止 XSS'); + } + } + } + + /** + * 测试 Session 安全 + */ + public function testSessionSecurity(): void + { + $config = file_get_contents(__DIR__ . '/../../config.php'); + + // 确保有 session 检查 + $this->assertStringContainsString('session', strtolower($config), '应该有 session 相关配置'); + } +} diff --git a/tests/Unit/ConfigTest.php b/tests/Unit/ConfigTest.php new file mode 100644 index 0000000..2f7fd5a --- /dev/null +++ b/tests/Unit/ConfigTest.php @@ -0,0 +1,51 @@ +assertFileExists(__DIR__ . '/../../config.php', '配置文件应该存在'); + } + + /** + * 测试样式文件存在 + */ + public function testStyleFileExists(): void + { + $this->assertFileExists(__DIR__ . '/../../style.css', '样式文件应该存在'); + } + + /** + * 测试入口文件存在 + */ + public function testIndexFileExists(): void + { + $this->assertFileExists(__DIR__ . '/../../index.php', '入口文件应该存在'); + } + + /** + * 测试仪表盘文件存在 + */ + public function testDashboardFileExists(): void + { + $this->assertFileExists(__DIR__ . '/../../dashboard.php', '仪表盘文件应该存在'); + } + + /** + * 测试查看文件存在 + */ + public function testViewFileExists(): void + { + $this->assertFileExists(__DIR__ . '/../../view.php', '查看文件应该存在'); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..1b556a9 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,18 @@ +