app = new Application(dirname(__DIR__, 2)); $this->app->instance('config', new Repository(['app' => ['locale' => 'zh', 'timezone' => 'Asia/Shanghai']])); Facade::setFacadeApplication($this->app); $db = new Manager($this->app); $db->addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'bot_']); $db->setEventDispatcher(new Dispatcher($this->app)); $db->setAsGlobal(); $db->bootEloquent(); $this->app->instance('db', $db->getDatabaseManager()); $this->app->bind('db.schema', fn () => $db->schema()); $translator = new Translator(new ArrayLoader(), 'zh'); $this->app->instance('translator', $translator); $this->app->instance('validator', new Factory($translator, $this->app)); $this->app->instance('log', new \Psr\Log\NullLogger()); $this->app->instance(\Illuminate\Contracts\Routing\ResponseFactory::class, new class { public function json($data, $status = 200, $headers = [], $options = 0) { return new JsonResponse($data, $status, $headers, $options); } }); $request = Request::create('/admin/agent/pageConfig'); $request->user = (object)['id' => 1, 'username' => 'admin']; $this->app->instance('request', $request); DB::statement('CREATE TABLE bot_operation_audits (id INTEGER PRIMARY KEY AUTOINCREMENT,resource_type TEXT,resource_id INTEGER,action TEXT,changes TEXT,operator_id INTEGER,operator_name TEXT,created_at TEXT)'); $this->migration()->up(); $this->service = new AgentPageConfigurationService(); } protected function tearDown(): void { DB::disconnect(); Facade::clearResolvedInstances(); parent::tearDown(); } private function migration() { return require dirname(__DIR__, 2) . '/database/migrations/2026_10_30_180000_create_agent_page_settings.php'; } private function emptyConfig(): array { return ['commission_plan_html' => '', 'contact_tg_url' => '', 'agent_backend_url' => '']; } private function payload(): array { return ['commission_plan_html' => '

佣金方案 / Tiếng Việt

代理比例
等级
一级代理30%
40%
', 'contact_tg_url' => ' https://t.me/customer_service ', 'agent_backend_url' => 'https://agent.example.com/login?from=member#start']; } public function test_defaults_and_migration_can_be_rolled_back_and_reapplied(): void { $this->assertSame($this->emptyConfig(), $this->service->get()); $this->migration()->down(); $this->migration()->up(); $this->assertSame($this->emptyConfig(), $this->service->get()); } public function test_rich_table_and_two_urls_save_together_and_only_public_fields_are_returned(): void { $this->app['request']->merge($this->payload()); $response = (new AgentPageConfiguration())->save($this->service); $result = $response->getData(true); $this->assertSame(0, $result['code']); $data = $result['data']; $this->assertSame(array_keys($this->emptyConfig()), array_keys($data)); $this->assertSame('https://t.me/customer_service', $data['contact_tg_url']); $this->assertSame($this->payload()['agent_backend_url'], $data['agent_backend_url']); foreach (['', '', '', '', 'colspan="2"', 'rowspan="2"', 'width: 100%', 'text-align: center', 'color: #f00', '佣金方案', 'Tiếng Việt', '30%', '40%'] as $text) $this->assertStringContainsString($text, $data['commission_plan_html']); $this->assertSame($data, $this->service->get()); $this->assertStringContainsString('no-store', $response->headers->get('Cache-Control')); $this->assertSame(1, DB::table('agent_page_settings')->count()); $this->assertSame(1, DB::table('operation_audits')->count()); $this->assertSame($data, $this->service->save($data)); $this->assertSame(1, DB::table('operation_audits')->count()); $this->assertSame($this->emptyConfig(), $this->service->save($this->emptyConfig())); } public function test_active_markup_and_unsafe_styles_are_removed_without_flattening_tables(): void { $input = $this->emptyConfig(); $input['commission_plan_html'] = '

中文

30%
错误链接TG'; $html = $this->service->save($input)['commission_plan_html']; foreach (['assertStringNotContainsString($danger, $html); foreach (['', '', '中文', 'href="https://t.me/customer_service"', 'rel="noopener noreferrer"'] as $safe) $this->assertStringContainsString($safe, $html); } /** @dataProvider invalidValues */ public function test_invalid_values_do_not_partially_save(string $field, $invalid): void { $before = $this->service->save($this->payload()); $input = $before; $input[$field] = $invalid; try { $this->service->save($input); $this->fail('Expected validation failure'); } catch (ValidationException $e) { $this->assertArrayHasKey($field, $e->errors()); } $this->assertSame($before, $this->service->get()); $this->assertSame(1, DB::table('operation_audits')->count()); } public static function invalidValues(): array { $cases = [['commission_plan_html', []], ['commission_plan_html', str_repeat('字', 100001)]]; foreach (['contact_tg_url', 'agent_backend_url'] as $field) { foreach (['javascript:alert(1)', '//example.com', 'https://user:password@example.com', "https://example.com/\na", 'https://example.com/\\evil', str_repeat('x', 2049), ['url']] as $value) $cases[] = [$field, $value]; } return $cases; } public function test_save_is_atomic_when_audit_fails_and_missing_form_fields_are_rejected(): void { $before = $this->service->save($this->payload()); foreach (array_keys($before) as $field) { $input = $before; unset($input[$field]); try { $this->service->save($input); $this->fail('Missing field accepted'); } catch (ValidationException $e) { $this->assertArrayHasKey($field, $e->errors()); } } DB::statement('DROP TABLE bot_operation_audits'); try { $this->service->save($this->emptyConfig()); $this->fail('Audit failure was swallowed'); } catch (\Illuminate\Database\QueryException $e) { $this->assertSame($before, $this->service->get()); } } public function test_admin_routes_keep_login_agent_admin_and_read_write_permissions(): void { $router = new Router(new Dispatcher($this->app), $this->app); $this->app->instance('router', $router); $router->group(['prefix' => 'admin'], function () { require dirname(__DIR__, 2) . '/routes/admin.php'; }); foreach (['GET' => ['show', 'admin/agent'], 'POST' => ['save', 'admin/agent/update']] as $method => [$action, $permission]) { $route = $router->getRoutes()->match(Request::create('/admin/agent/pageConfig', $method)); $this->assertSame(AgentPageConfiguration::class . '@' . $action, $route->getAction('uses')); foreach (['admin.jwt', 'check.button.uri', 'agent.admin'] as $middleware) $this->assertContains($middleware, $route->middleware()); $this->assertSame($permission, $route->defaults['permission_uri']); } $result = (new AgentPageConfiguration())->show($this->service)->getData(true); $this->assertSame($this->emptyConfig(), $result['data']); DB::statement('DROP TABLE bot_agent_page_settings'); $failed = (new AgentPageConfiguration())->show($this->service)->getData(true); $this->assertSame('代理页面配置暂不可用,请稍后重试', $failed['msg']); } }
30%