CaiWuXiTong/tests/Unit/Deployment/FinanceImportRuntimeTest.php
qiuxiang 3a7fe1ba82 feat: 注册种菜报销工单明细批处理 (#895)
将种菜报销工单向种菜明细表的同步命令纳入受限批处理入口,并验证生产网页容器不能直接执行。

> 开工时标题使用 `WIP: <任务名称>` 并先填写“任务占用与边界”。Codex 完成独立合并前自审后补齐其余内容并移除 `WIP:`。

## 任务占用与边界

- 任务名称:
- 实际开发者:
- 任务来源/团队分工:
- 目标结果:
- 预计修改路径:
- 将写入的公共契约(路由、Controller 方法、Repository、表/视图、`source_key`、`scope`、权限名、菜单键或共享组件):
- 仅复用、明确不修改的公共契约:
- 数据源、表/视图和字段:
- 上游 `source_key` / `scope`:
- 权限名、菜单键或共享组件:
- 明确不修改的范围:

## 开工碰撞检查

- [ ] Codex 已执行 `scripts/codex-task-preflight.ps1`
- [ ] 已检查最新 `main` 和所有远程任务分支
- [ ] 已搜索主线既有页面、接口、Repository 和数据查询
- [ ] 已把“将写入”和“仅复用、不修改”的公共契约分开;仅复用项不作为单独阻断条件
- [ ] 未发现实际写入重叠;或已在下方写明相关分支、具体重叠项和协调结论

相关分支、重叠项与协调结论:

## 实际改动

- 完成内容:
- Codex 实际修改的文件:
- 新增或变更的公共契约:
- 与原计划的差异:

## 验证证据

- 正常流程:
- 空数据/错误输入/无权限场景:
- 受影响旧功能:
- 自动测试及结果:
- 实际页面、接口或只读业务查询结果:

## 风险检查

- [ ] 不涉及金额、权限、审批、数据库迁移、生产配置或数据修复
- [ ] 如涉及上述高风险内容,已在下方写明影响、回滚和所需授权
- [ ] 未提交 `.env`、密码、Token、私钥、真实财务数据、生产日志或构建产物
- [ ] 前端改动符合 `DESIGN.md`;无前端改动则写“不适用”

高风险影响、回滚和授权状态:

## 合并前复查

- [ ] 已重新拉取远程引用并用实际修改范围再次运行碰撞检查
- [ ] PR 不再是 `WIP:`,所有测试结果已经补齐
- [ ] Codex 已重新读取原始需求和验收标准,并逐文件审核 `origin/main...任务分支` 的完整 diff
- [ ] Codex 自审确认无越界修改、重复实现、敏感内容、未处理冲突和未验证结果
- [ ] 合并顺序不会覆盖其他未合并任务
- [ ] 已授权当前任务的 Codex 执行 Squash merge、验证 `main` 并清理任务分支
- [ ] 如涉及生产迁移、数据修复、删除、覆盖配置或部署,已另行取得对应高风险授权

Reviewed-on: #895
2026-11-05 14:42:46 +08:00

93 lines
3.4 KiB
PHP

<?php
namespace Tests\Unit\Deployment;
use App\Listeners\Common\RequireImportBatchContainer;
use Illuminate\Config\Repository;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Container\Container;
use Illuminate\Foundation\Application;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
class FinanceImportRuntimeTest extends TestCase
{
private $previousContainer;
protected function setUp(): void
{
$this->previousContainer = Container::getInstance();
$app = new Application(dirname(__DIR__, 3));
$app['env'] = 'production';
$app->instance('config', new Repository([
'finance_batch' => ['container' => false, 'import_commands' => ['product:monthly-weighted-average:base']],
]));
Container::setInstance($app);
}
protected function tearDown(): void
{
Container::setInstance($this->previousContainer);
parent::tearDown();
}
private function starting(string $name): CommandStarting
{
return new CommandStarting($name, new ArrayInput([]), new BufferedOutput());
}
public function test_production_imports_cannot_start_in_web_container(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('run-finance-batch.sh');
(new RequireImportBatchContainer())->handle($this->starting('order-data:import-after-sales-cost'));
}
public function test_registered_import_without_import_in_its_name_is_also_protected(): void
{
$this->expectException(RuntimeException::class);
(new RequireImportBatchContainer())->handle($this->starting('product:monthly-weighted-average:base'));
}
public function test_work_order_sync_is_registered_as_a_protected_import(): void
{
config([
'finance_batch.import_commands' => [
'planting-details:sync-work-orders',
],
]);
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('run-finance-batch.sh');
(new RequireImportBatchContainer())->handle($this->starting('planting-details:sync-work-orders'));
}
public function test_production_batch_runtime_can_import_and_health_checks_stay_available(): void
{
$listener = new RequireImportBatchContainer();
$listener->handle($this->starting('app:ready'));
config(['finance_batch.container' => true]);
$listener->handle($this->starting('order-data:import-after-sales-cost'));
$this->addToAssertionCount(2);
}
public function test_local_and_test_imports_keep_their_existing_usage(): void
{
app()['env'] = 'testing';
(new RequireImportBatchContainer())->handle($this->starting('order-data:import-after-sales-cost'));
$this->addToAssertionCount(1);
}
public function test_registry_includes_every_existing_import_command(): void
{
$root = dirname(__DIR__, 3);
$commands = json_decode(file_get_contents($root . '/deploy/finance-batch-imports.json'), true, 512, JSON_THROW_ON_ERROR);
foreach (glob($root . '/app/Console/Commands/Import*.php') as $file) {
preg_match('/\$signature\s*=\s*\'([^\s\']+)/', file_get_contents($file), $match);
$this->assertContains($match[1], $commands, basename($file));
}
}
}