AdminUserLoginLogTest.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. namespace Tests\Integration;
  3. use App\Http\Controllers\admin\User as UserController;
  4. use Illuminate\Config\Repository;
  5. use Illuminate\Database\Capsule\Manager;
  6. use Illuminate\Database\Eloquent\Model;
  7. use Illuminate\Database\Schema\Blueprint;
  8. use Illuminate\Foundation\Application;
  9. use Illuminate\Http\Request;
  10. use Illuminate\Support\Facades\DB;
  11. use Illuminate\Support\Facades\Facade;
  12. use Illuminate\Translation\ArrayLoader;
  13. use Illuminate\Translation\Translator;
  14. use Illuminate\Validation\Factory;
  15. use PHPUnit\Framework\TestCase;
  16. class AdminUserLoginLogTest extends TestCase
  17. {
  18. private Application $app;
  19. protected function setUp(): void
  20. {
  21. parent::setUp();
  22. Facade::clearResolvedInstances();
  23. Model::clearBootedModels();
  24. $this->app = new Application(dirname(__DIR__, 2));
  25. $this->app->instance('config', new Repository(['app' => ['locale' => 'zh', 'timezone' => 'Asia/Shanghai']]));
  26. $translator = new Translator(new ArrayLoader(), 'zh');
  27. $this->app->instance('translator', $translator);
  28. $this->app->instance('validator', new Factory($translator, $this->app));
  29. $this->app->instance(\Illuminate\Contracts\Routing\ResponseFactory::class, new \Tests\Support\JsonResponseFactory());
  30. Request::macro('validate', function (array $rules) {
  31. return app('validator')->make($this->all(), $rules)->validate();
  32. });
  33. Facade::setFacadeApplication($this->app);
  34. $db = new Manager($this->app);
  35. $db->addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'bot_']);
  36. $db->setAsGlobal();
  37. $db->bootEloquent();
  38. $this->app->instance('db', $db->getDatabaseManager());
  39. $db->schema()->create('users', function (Blueprint $t) {
  40. $t->id();
  41. $t->string('user_id')->nullable();
  42. $t->string('member_id');
  43. $t->string('account')->nullable();
  44. $t->string('from')->nullable();
  45. $t->string('first_name')->default('');
  46. $t->string('username')->default('');
  47. });
  48. $db->schema()->create('user_login', function (Blueprint $t) {
  49. $t->id();
  50. $t->string('user_id')->nullable();
  51. foreach (['login_account', 'login_ip', 'login_domain', 'country', 'browser', 'os', 'platform', 'user_agent'] as $field) {
  52. $t->string($field)->default('');
  53. }
  54. $t->integer('status');
  55. $t->dateTime('logout_time')->nullable();
  56. $t->integer('online_duration')->nullable();
  57. $t->timestamps();
  58. });
  59. foreach ([
  60. ['1001', 'alice', '1'],
  61. ['1002', 'tg1002', '-1'],
  62. ['1003', 'guest1003', '2'],
  63. ['1004', 'kefu_member', '1'],
  64. ['1005', 'legacy', null],
  65. ['9001', 'kefu001', '0'],
  66. ['9002', 'kefu002', '0'],
  67. ['9003', 'support-alpha', '0'],
  68. ['', 'desk-blank-id', '0'],
  69. [null, 'desk-null-id', '0'],
  70. ] as [$userId, $account, $from]) {
  71. DB::table('users')->insert([
  72. 'user_id' => $userId, 'member_id' => $account, 'account' => $account,
  73. 'from' => $from, 'first_name' => $account,
  74. ]);
  75. }
  76. }
  77. protected function tearDown(): void
  78. {
  79. DB::disconnect();
  80. Facade::clearResolvedInstances();
  81. Model::clearBootedModels();
  82. parent::tearDown();
  83. }
  84. public function test_excludes_customer_service_successes_and_failures_from_list_and_total(): void
  85. {
  86. $expected = [];
  87. foreach (['1001', '1002', '1003', '1004', '1005'] as $userId) {
  88. $expected[] = $this->log($userId, '', 1);
  89. }
  90. foreach (['9001', '9002', '9003'] as $userId) {
  91. $this->log($userId, '', 1);
  92. $this->log($userId, '', 0);
  93. }
  94. $data = $this->logs();
  95. $this->assertSame(5, $data['total']);
  96. $this->assertSame(array_reverse($expected), array_column($data['data'], 'id'));
  97. $this->assertSame(11, DB::table('user_login')->count());
  98. }
  99. public function test_identifies_customer_service_failures_by_account_when_user_id_is_missing(): void
  100. {
  101. foreach (['', null, 'deleted-id'] as $userId) {
  102. foreach (['kefu001', 'kefu002', 'support-alpha', 'desk-blank-id', 'desk-null-id'] as $account) {
  103. $this->log($userId, $account, 0);
  104. }
  105. }
  106. $expected = [
  107. $this->log('', 'unknown-account', 0),
  108. $this->log(null, 'another-unknown', 0),
  109. $this->log('deleted-member', 'former-member', 0),
  110. $this->log('', 'kefu_not_staff', 0),
  111. $this->log('', '', 0),
  112. ];
  113. $data = $this->logs(['status' => 0]);
  114. $this->assertSame(5, $data['total']);
  115. $this->assertSame(array_reverse($expected), array_column($data['data'], 'id'));
  116. foreach ($data['data'] as $row) {
  117. $this->assertNull($row['account']);
  118. }
  119. }
  120. public function test_account_and_user_filters_cannot_restore_customer_service_records(): void
  121. {
  122. $this->log('9001', 'kefu001', 1);
  123. $this->log('', 'kefu001', 0);
  124. $memberId = $this->log('1004', 'kefu_member', 1);
  125. $data = $this->logs(['account' => 'kefu']);
  126. $this->assertSame(1, $data['total']);
  127. $this->assertSame([$memberId], array_column($data['data'], 'id'));
  128. foreach ([['account' => 'kefu001'], ['user_id' => '9001'], ['member_id' => '9001'], ['first_name' => 'kefu001']] as $params) {
  129. $this->assertSame(['total' => 0, 'data' => []], $this->logs($params));
  130. }
  131. }
  132. public function test_member_filters_and_pagination_keep_normal_failed_logins(): void
  133. {
  134. $this->log('1001', 'alice', 1);
  135. $older = $this->log('1001', 'alice', 0);
  136. $newer = $this->log('1001', 'alice', 0, '2026-11-07 12:00:01');
  137. $this->log('9001', 'kefu001', 0, '2026-11-07 12:00:02');
  138. $this->log('1001', 'alice', 0, '2026-11-06 23:59:59');
  139. $params = ['account' => 'ali', 'user_id' => '1001', 'status' => 0,
  140. 'start_time' => '2026-11-07', 'end_time' => '2026-11-07', 'limit' => 1];
  141. $data = $this->logs($params);
  142. $this->assertSame(2, $data['total']);
  143. $this->assertSame([$newer], array_column($data['data'], 'id'));
  144. $this->assertSame('alice', $data['data'][0]['account']);
  145. $this->assertFalse($data['data'][0]['is_online']);
  146. $data = $this->logs($params + ['page' => 2]);
  147. $this->assertSame(2, $data['total']);
  148. $this->assertSame([$older], array_column($data['data'], 'id'));
  149. }
  150. private function log(?string $userId, string $account, int $status, string $at = '2026-11-07 12:00:00'): int
  151. {
  152. return DB::table('user_login')->insertGetId([
  153. 'user_id' => $userId, 'login_account' => $account, 'status' => $status,
  154. 'created_at' => $at, 'updated_at' => $at,
  155. ]);
  156. }
  157. private function logs(array $params = []): array
  158. {
  159. $this->app->instance('request', Request::create('/admin/user/loginLog', 'GET', $params));
  160. $body = (new UserController())->loginLog()->getData(true);
  161. $this->assertSame(0, $body['code'], $body['msg']);
  162. return $body['data'];
  163. }
  164. }