config = new Config( sid: 'test_sid', appKey: 'test_app_key', appSecret: 'test_app_secret' ); $this->authenticator = new Authenticator($this->config); } public function testGenerateSignature(): void { $params = [ 'shop_no' => 'test_shop', 'trade_list' => '[{"trade_no":"123"}]' ]; $signature = $this->authenticator->generateSignature($params); $this->assertIsString($signature); $this->assertEquals(32, strlen($signature)); // MD5 hash length $this->assertMatchesRegularExpression('/^[a-f0-9]{32}$/', $signature); } public function testGenerateSignatureRemovesExistingSign(): void { $params = [ 'shop_no' => 'test_shop', 'sign' => 'old_signature' ]; $signature1 = $this->authenticator->generateSignature($params); unset($params['sign']); $signature2 = $this->authenticator->generateSignature($params); $this->assertEquals($signature1, $signature2); } public function testGenerateSignatureConsistency(): void { $params = [ 'shop_no' => 'test_shop', 'trade_list' => '[{"trade_no":"123"}]' ]; $signature1 = $this->authenticator->generateSignature($params); $signature2 = $this->authenticator->generateSignature($params); // Note: signatures will be different due to timestamp, but structure should be consistent $this->assertIsString($signature1); $this->assertIsString($signature2); $this->assertEquals(32, strlen($signature1)); $this->assertEquals(32, strlen($signature2)); } public function testAddAuthParams(): void { $params = ['shop_no' => 'test_shop']; $authenticatedParams = $this->authenticator->addAuthParams($params); $this->assertArrayHasKey('sid', $authenticatedParams); $this->assertArrayHasKey('appkey', $authenticatedParams); $this->assertArrayHasKey('timestamp', $authenticatedParams); $this->assertArrayHasKey('sign', $authenticatedParams); $this->assertArrayHasKey('shop_no', $authenticatedParams); $this->assertEquals('test_sid', $authenticatedParams['sid']); $this->assertEquals('test_app_key', $authenticatedParams['appkey']); $this->assertEquals('test_shop', $authenticatedParams['shop_no']); $this->assertIsInt($authenticatedParams['timestamp']); $this->assertIsString($authenticatedParams['sign']); $this->assertEquals(32, strlen($authenticatedParams['sign'])); } public function testAddAuthParamsOverwritesExisting(): void { $params = [ 'shop_no' => 'test_shop', 'sid' => 'old_sid', 'appkey' => 'old_appkey', 'timestamp' => 123456, 'sign' => 'old_sign' ]; $authenticatedParams = $this->authenticator->addAuthParams($params); $this->assertEquals('test_sid', $authenticatedParams['sid']); $this->assertEquals('test_app_key', $authenticatedParams['appkey']); $this->assertNotEquals(123456, $authenticatedParams['timestamp']); $this->assertNotEquals('old_sign', $authenticatedParams['sign']); } public function testValidateParamsSuccess(): void { $params = [ 'sid' => 'test_sid', 'appkey' => 'test_app_key', 'timestamp' => time() ]; // Should not throw exception $this->authenticator->validateParams($params); $this->assertTrue(true); // Assert that we reach this point } public function testValidateParamsMissingSid(): void { $params = [ 'appkey' => 'test_app_key', 'timestamp' => time() ]; $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing required parameter: sid'); $this->authenticator->validateParams($params); } public function testValidateParamsMissingAppkey(): void { $params = [ 'sid' => 'test_sid', 'timestamp' => time() ]; $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing required parameter: appkey'); $this->authenticator->validateParams($params); } public function testValidateParamsMissingTimestamp(): void { $params = [ 'sid' => 'test_sid', 'appkey' => 'test_app_key' ]; $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing required parameter: timestamp'); $this->authenticator->validateParams($params); } public function testValidateParamsEmptyValues(): void { $params = [ 'sid' => '', 'appkey' => 'test_app_key', 'timestamp' => time() ]; $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Missing required parameter: sid'); $this->authenticator->validateParams($params); } public function testPackDataFormat(): void { // Test the pack data format by using reflection to access private method $reflection = new \ReflectionClass($this->authenticator); $packDataMethod = $reflection->getMethod('packData'); $packDataMethod->setAccessible(true); $params = [ 'b' => 'second', 'a' => 'first' ]; $packed = $packDataMethod->invoke($this->authenticator, $params); // Should be sorted by key: a first, then b // Format: length-key:length-value;length-key:length-value $expected = '01-a:0005-first;01-b:0006-second'; $this->assertEquals($expected, $packed); } public function testPackDataWithUtf8(): void { $reflection = new \ReflectionClass($this->authenticator); $packDataMethod = $reflection->getMethod('packData'); $packDataMethod->setAccessible(true); $params = [ 'name' => '测试' ]; $packed = $packDataMethod->invoke($this->authenticator, $params); // UTF-8 characters should be properly counted $expected = '04-name:0002-测试'; $this->assertEquals($expected, $packed); } public function testPackDataSkipsSignParameter(): void { $reflection = new \ReflectionClass($this->authenticator); $packDataMethod = $reflection->getMethod('packData'); $packDataMethod->setAccessible(true); $params = [ 'a' => 'value', 'sign' => 'should_be_ignored' ]; $packed = $packDataMethod->invoke($this->authenticator, $params); $expected = '01-a:0005-value'; $this->assertEquals($expected, $packed); } }