assertReadyForApi(); $this->assertTrue($configuration->isConfigured()); $this->assertSame('https://openapi.esign.cn', $configuration->baseUrl()); $this->assertSame('production-app-id', $configuration->appId()); } public function test_it_rejects_production_credentials_with_the_sandbox_endpoint(): void { $configuration = $this->configuration(baseUrl: 'https://smlopenapi.esign.cn'); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('正式环境不能使用沙箱 API 地址'); $configuration->assertReadyForApi(); } public function test_it_rejects_sandbox_credentials_with_the_production_endpoint(): void { $configuration = $this->configuration(environment: 'sandbox', baseUrl: 'https://openapi.esign.cn'); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('沙箱环境不能使用正式 API 地址'); $configuration->assertReadyForApi(); } public function test_it_requires_both_credentials_before_an_api_request(): void { $configuration = $this->configuration(appSecret: ''); $this->assertFalse($configuration->isConfigured()); $this->expectExceptionMessage('尚未配置 App ID 或 AppSecret'); $configuration->assertReadyForApi(); } public function test_selected_company_overrides_the_default_initiator(): void { $configuration = $this->configuration( initiatorAccounts: json_encode([ '默认企业' => ['org_id' => 'org-default', 'psn_id' => 'person-default'], '所选企业' => ['org_id' => 'org-selected', 'psn_id' => 'person-selected'], ], JSON_THROW_ON_ERROR), defaultInitiator: '默认企业', ); $this->assertSame(['orgInitiator' => [ 'orgId' => 'org-selected', 'transactor' => ['psnId' => 'person-selected'], ]], $configuration->signFlowInitiator('所选企业')); $this->assertSame('org-default', $configuration->signFlowInitiator()['orgInitiator']['orgId']); } public function test_unconfigured_installations_keep_the_application_initiator(): void { $this->assertNull($this->configuration()->signFlowInitiator('旧企业')); } public function test_unmapped_company_cannot_silently_use_the_default_initiator(): void { $configuration = $this->configuration( initiatorAccounts: '{"默认企业":{"org_id":"org-default","psn_id":"person-default"}}', defaultInitiator: '默认企业', ); $this->expectExceptionMessage('所选签署主体未配置完整'); $configuration->signFlowInitiator('另一企业'); } /** @dataProvider invalidInitiatorAccounts */ public function test_invalid_initiator_configuration_prevents_new_flows(string $accounts): void { $configuration = $this->configuration(initiatorAccounts: $accounts, defaultInitiator: '企业'); // Historical reads still use the existing application credentials. $configuration->assertReadyForApi(); $this->expectException(RuntimeException::class); $configuration->signFlowInitiator('企业'); } public static function invalidInitiatorAccounts(): array { return [ 'invalid json' => ['{'], 'invalid type' => ['true'], 'missing transactor' => ['{"企业":{"org_id":"org-a"}}'], 'blank organization' => ['{"企业":{"org_id":" ","psn_id":"person-a"}}'], 'invalid account type' => ['{"企业":{"org_id":"org-a","psn_id":123}}'], ]; } private function configuration( string $environment = 'production', string $baseUrl = 'https://openapi.esign.cn', string $appId = 'production-app-id', string $appSecret = 'production-app-secret', string $initiatorAccounts = '{}', string $defaultInitiator = '', ): EsignConfiguration { return new EsignConfiguration( environment: $environment, baseUrl: $baseUrl, appId: $appId, appSecret: $appSecret, notifyUrl: '', callbackToleranceSeconds: 900, timeout: 20, initiatorAccounts: $initiatorAccounts, defaultInitiator: $defaultInitiator, ); } }