mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-10-29 08:58:22 +08:00
fix(update): .agents项目自维护文件改判可选更新,防止升级强制覆盖与强制删除
This commit is contained in:
@@ -664,6 +664,14 @@ class AdminUpdateServiceBase
|
||||
|
||||
protected function testIsOptionalFiles($file_path)
|
||||
{
|
||||
// .agents 下项目自维护的文件:可选更新,防止被强制覆盖/强制删除
|
||||
if ($file_path === '.agents/PROJECT.md') {
|
||||
return true;
|
||||
}
|
||||
if (str_starts_with($file_path, '.agents/rules/project-')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果file_path以app或config开头,则是可选更新的文件
|
||||
$optional_files_prefix = [
|
||||
'app',
|
||||
|
||||
81
tests/Unit/AdminUpdateServiceOptionalFilesTest.php
Normal file
81
tests/Unit/AdminUpdateServiceOptionalFilesTest.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\Unit;
|
||||
|
||||
use app\admin\service\AdminUpdateService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* AdminUpdateServiceBase::testIsOptionalFiles 分类矩阵单元测试(纯单元,不触 DB).
|
||||
*
|
||||
* 锚定 .agents 项目自维护文件改判"可选更新"的行为:
|
||||
* - .agents/PROJECT.md(精确匹配)与 .agents/rules/project- 前缀文件属项目自维护,
|
||||
* 升级时必须判为可选(optional=true),防止被强制覆盖/强制删除
|
||||
* - .agents 其余内容(ulthon-* 规则、skills、非精确路径、无连字符文件名)保持强制(false)
|
||||
*
|
||||
* 22 用例 = 可选 12 + 强制 10。用 PHPUnit 10 属性式 DataProvider(不支持 @dataProvider 注解),
|
||||
* 每个用例独立成测试,红/绿状态可逐例观察。
|
||||
*/
|
||||
class AdminUpdateServiceOptionalFilesTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* 通过匿名类暴露 protected testIsOptionalFiles(构造函数仅设常量属性,无副作用).
|
||||
*/
|
||||
private function makeExposer(): object
|
||||
{
|
||||
return new class extends AdminUpdateService {
|
||||
public function expose(string $path): bool
|
||||
{
|
||||
return $this->testIsOptionalFiles($path);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static function classificationProvider(): iterable
|
||||
{
|
||||
// 可选(true) 12 例:既有规则覆盖 8 例 + .agents 项目自维护新规则 4 例
|
||||
yield 'optional: root AGENTS.md' => ['AGENTS.md', true];
|
||||
yield 'optional: root composer.json' => ['composer.json', true];
|
||||
yield 'optional: root .php-cs-fixer.php' => ['.php-cs-fixer.php', true];
|
||||
yield 'optional: app prefix' => ['app/common/service/X.php', true];
|
||||
yield 'optional: config prefix' => ['config/app.php', true];
|
||||
yield 'optional: route prefix' => ['route/app.php', true];
|
||||
yield 'optional: source/docker prefix' => ['source/docker/Dockerfile', true];
|
||||
yield 'optional: extend/think prefix' => ['extend/think/store/X.php', true];
|
||||
yield 'optional: .agents/PROJECT.md exact' => ['.agents/PROJECT.md', true];
|
||||
yield 'optional: project rule flat' => ['.agents/rules/project-dev-runtime-deploy.md', true];
|
||||
yield 'optional: project rule flat 2' => ['.agents/rules/project-account-system.md', true];
|
||||
yield 'optional: project rule nested' => ['.agents/rules/project-deploy-detail/sub.md', true];
|
||||
|
||||
// 强制(false) 10 例:核心入口、框架内核、静态资源、迁移、.agents 其余内容
|
||||
yield 'force: think entry' => ['think', false];
|
||||
yield 'force: extend/base kernel' => ['extend/base/admin/service/AdminUpdateServiceBase.php', false];
|
||||
yield 'force: public static' => ['public/static/common/css/a.css', false];
|
||||
yield 'force: database migrations' => ['database/migrations/2024.php', false];
|
||||
yield 'force: ulthon rule' => ['.agents/rules/ulthon-naming-convention.md', false];
|
||||
yield 'force: ulthon skill' => ['.agents/skills/ulthon-timer/SKILL.md', false];
|
||||
yield 'force: project skill (skills 始终强制)' => ['.agents/skills/project-x/SKILL.md', false];
|
||||
yield 'force: project.md 无连字符不匹配前缀' => ['.agents/rules/project.md', false];
|
||||
yield 'force: .agents/PROJECT 非精确不匹配' => ['.agents/PROJECT/about.md', false];
|
||||
yield 'force: .agents/rules/.README' => ['.agents/rules/.README', false];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('classificationProvider')]
|
||||
public function test_classification(string $path, bool $expectedOptional): void
|
||||
{
|
||||
$exposer = $this->makeExposer();
|
||||
|
||||
$this->assertSame(
|
||||
$expectedOptional,
|
||||
$exposer->expose($path),
|
||||
sprintf(
|
||||
'路径 %s 分类不符:预期%s,实际%s',
|
||||
$path,
|
||||
$expectedOptional ? '可选(true)' : '强制(false)',
|
||||
$expectedOptional ? '强制(false)' : '可选(true)'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user