CompanyMonthlyReportTest.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. <?php
  2. namespace Tests\Integration;
  3. use App\Http\Controllers\admin\CompanyReport;
  4. use App\Http\Controllers\agent\CompanyReportController;
  5. use App\Models\Agent\Agent;
  6. use App\Services\CompanyMonthlyReportService;
  7. use Carbon\Carbon;
  8. use Illuminate\Database\Schema\Blueprint;
  9. use Illuminate\Filesystem\FilesystemManager;
  10. use Illuminate\Http\Request;
  11. use Illuminate\Http\UploadedFile;
  12. use Illuminate\Support\Facades\DB;
  13. use Illuminate\Support\Facades\Schema;
  14. use PHPUnit\Framework\TestCase;
  15. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  16. use Tests\Support\AgentTestEnvironment;
  17. /** 公司营业报表:总后台按月上传 / 重新上传,入股代理查看和下载。 */
  18. class CompanyMonthlyReportTest extends TestCase
  19. {
  20. use AgentTestEnvironment { setUp as environmentSetUp; tearDown as environmentTearDown; }
  21. private string $root;
  22. private array $temp = [];
  23. protected function setUp(): void
  24. {
  25. $this->environmentSetUp();
  26. Carbon::setTestNow('2026-11-12 10:00:00');
  27. (require dirname(__DIR__, 2) . '/database/migrations/2026_11_12_120000_create_company_monthly_reports.php')->up();
  28. Schema::create('admin', function (Blueprint $t) { $t->id(); $t->string('username'); });
  29. DB::table('admin')->insert([['id' => 1, 'username' => 'root'], ['id' => 2, 'username' => 'finance']]);
  30. $this->root = sys_get_temp_dir() . '/company-report-' . bin2hex(random_bytes(4));
  31. config(['filesystems.disks.local' => ['driver' => 'local', 'root' => $this->root]]);
  32. $this->app->instance('filesystem', new FilesystemManager($this->app));
  33. }
  34. protected function tearDown(): void
  35. {
  36. Carbon::setTestNow();
  37. foreach ($this->temp as $file) @unlink($file);
  38. if (is_dir($this->root)) { array_map('unlink', glob($this->root . '/company-reports/*') ?: []); @rmdir($this->root . '/company-reports'); @rmdir($this->root); }
  39. $this->environmentTearDown();
  40. }
  41. private function excel(string $name, string $head = "PK\x03\x04"): UploadedFile
  42. {
  43. $path = tempnam(sys_get_temp_dir(), 'xls');
  44. file_put_contents($path, $head . random_bytes(32));
  45. $this->temp[] = $path;
  46. return new UploadedFile($path, $name, null, null, true);
  47. }
  48. private function admin(string $action, array $params = [], array $files = [], int $adminId = 1)
  49. {
  50. $request = Request::create('/admin/companyReport/' . $action, $files ? 'POST' : 'GET', $params, [], $files);
  51. $request->user = (object) ['id' => $adminId];
  52. $this->app->instance('request', $request);
  53. $response = (new CompanyReport())->{$action === '' ? 'index' : $action}($request, new CompanyMonthlyReportService());
  54. return $response instanceof BinaryFileResponse ? $response : $response->getData(true);
  55. }
  56. private function agentCall(Agent $agent, string $action, array $params = [])
  57. {
  58. $request = Request::create('/agent/company-reports', 'GET', $params);
  59. $request->attributes->set('agent', $agent);
  60. $this->app->instance('request', $request);
  61. $response = (new CompanyReportController())->{$action}($request, new CompanyMonthlyReportService());
  62. return $response instanceof BinaryFileResponse ? $response : $response->getData(true);
  63. }
  64. public function test_admin_uploads_last_month_once_and_reuploads_through_edit(): void
  65. {
  66. $this->assertSame('2026-10', CompanyMonthlyReportService::allowedMonth());
  67. $wrong = $this->admin('create', ['month' => '2026-11'], ['file' => $this->excel('11月.xlsx')]);
  68. $this->assertSame('只能上传已结束月份的报表(2026-10 及以前)', $wrong['msg']);
  69. $this->assertSame('请上传 Excel 文件(.xlsx 或 .xls)', $this->admin('create', ['month' => '2026-10'], ['file' => $this->excel('a.pdf')])['msg']);
  70. $this->assertSame('请上传 Excel 文件(.xlsx 或 .xls)', $this->admin('create', ['month' => '2026-10'], ['file' => $this->excel('fake.xlsx', 'abcd')])['msg'], 'content must be excel');
  71. $encrypted = $this->admin('create', ['month' => '2026-08'], ['file' => $this->excel('8月-加密.xlsx', "\xD0\xCF\x11\xE0")]);
  72. $this->assertSame([0, '2026-08'], [$encrypted['code'], $encrypted['data']['month'] ?? null], 'a missed earlier month can be added; password-protected xlsx is OLE');
  73. $created = $this->admin('create', ['month' => '2026-10'], ['file' => $this->excel('10月营业报表.xlsx')]);
  74. $this->assertSame(0, $created['code'], $created['msg']);
  75. $this->assertSame(['2026-10', '10月营业报表.xlsx'], [$created['data']['month'], $created['data']['file_name']]);
  76. $this->assertArrayNotHasKey('file_path', $created['data']);
  77. $this->assertSame('2026-10 的报表已存在,请在列表中编辑重新上传', $this->admin('create', ['month' => '2026-10'], ['file' => $this->excel('again.xlsx')])['msg']);
  78. $oldPath = DB::table('company_monthly_reports')->where('month', '2026-10')->value('file_path');
  79. $this->assertFileExists($this->root . '/' . $oldPath);
  80. $updated = $this->admin('update', ['id' => $created['data']['id']], ['file' => $this->excel('10月营业报表-修正.xls', "\xD0\xCF\x11\xE0")], 2);
  81. $this->assertSame(0, $updated['code'], $updated['msg']);
  82. $this->assertSame(['2026-10', '10月营业报表-修正.xls'], [$updated['data']['month'], $updated['data']['file_name']]);
  83. $this->assertFileDoesNotExist($this->root . '/' . $oldPath, 'old file is removed after re-upload');
  84. $list = $this->admin('', [])['data'];
  85. $this->assertSame([2, '2026-10'], [$list['total'], $list['allowed_month']]);
  86. $this->assertSame(['2026-10', '2026-08'], array_column($list['list'], 'month'));
  87. $this->assertSame(['root', 'finance'], [$list['list'][0]['created_by_name'], $list['list'][0]['updated_by_name']]);
  88. $file = $this->admin('download', ['id' => $created['data']['id']]);
  89. $this->assertInstanceOf(BinaryFileResponse::class, $file);
  90. $this->assertStringContainsString('attachment', (string) $file->headers->get('content-disposition'));
  91. $this->assertSame('数据不存在', $this->admin('download', ['id' => 999])['msg']);
  92. }
  93. public function test_only_equity_agents_can_list_and_download(): void
  94. {
  95. Carbon::setTestNow('2026-10-05 10:00:00');
  96. $this->admin('create', ['month' => '2026-09'], ['file' => $this->excel('9月.xlsx')]);
  97. Carbon::setTestNow('2026-11-12 10:00:00');
  98. $id = $this->admin('create', ['month' => '2026-10'], ['file' => $this->excel('10月.xlsx')])['data']['id'];
  99. $equity = $this->agent(Agent::TYPE_EQUITY);
  100. $list = $this->agentCall($equity, 'index');
  101. $this->assertSame(0, $list['code'], $list['msg']);
  102. $this->assertSame(['2026-10', '2026-09'], array_column($list['data']['list'], 'month'), 'newest month first');
  103. $this->assertArrayNotHasKey('created_by', $list['data']['list'][0], 'admin info stays in the admin');
  104. $this->assertSame(['2026-09'], array_column($this->agentCall($equity, 'index', ['end_month' => '2026-09'])['data']['list'], 'month'));
  105. $this->assertInstanceOf(BinaryFileResponse::class, $this->agentCall($equity, 'download', ['id' => $id]));
  106. $joint = $this->agent();
  107. $this->assertSame('仅入股代理可查看公司营业报表', $this->agentCall($joint, 'index')['msg']);
  108. $this->assertSame('仅入股代理可查看公司营业报表', $this->agentCall($joint, 'download', ['id' => $id])['msg']);
  109. }
  110. }