|
@@ -0,0 +1,213 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+declare(strict_types=1);
|
|
|
|
|
+
|
|
|
|
|
+namespace SixShop\Wangdian\Tests\Unit\Exception;
|
|
|
|
|
+
|
|
|
|
|
+use PHPUnit\Framework\TestCase;
|
|
|
|
|
+use SixShop\Wangdian\Exception\ApiException;
|
|
|
|
|
+use SixShop\Wangdian\Exception\AuthException;
|
|
|
|
|
+use SixShop\Wangdian\Exception\ConfigException;
|
|
|
|
|
+use SixShop\Wangdian\Exception\HttpException;
|
|
|
|
|
+use SixShop\Wangdian\Exception\ValidationException;
|
|
|
|
|
+use SixShop\Wangdian\Exception\WangdianException;
|
|
|
|
|
+
|
|
|
|
|
+class ExceptionTest extends TestCase
|
|
|
|
|
+{
|
|
|
|
|
+ public function testWangdianExceptionBasic(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $message = 'Test exception message';
|
|
|
|
|
+ $code = 123;
|
|
|
|
|
+ $context = ['key' => 'value'];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new class($message, $code, null, $context) extends WangdianException {};
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertEquals($message, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals($code, $exception->getCode());
|
|
|
|
|
+ $this->assertEquals($context, $exception->getContext());
|
|
|
|
|
+ $this->assertInstanceOf(\Exception::class, $exception);
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $exception);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testWangdianExceptionWithPrevious(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $previous = new \Exception('Previous exception');
|
|
|
|
|
+ $exception = new class('Test', 0, $previous) extends WangdianException {};
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertSame($previous, $exception->getPrevious());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testConfigException(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $message = 'Configuration error';
|
|
|
|
|
+ $context = ['config_key' => 'invalid_value'];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new ConfigException($message, 100, null, $context);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $exception);
|
|
|
|
|
+ $this->assertInstanceOf(ConfigException::class, $exception);
|
|
|
|
|
+ $this->assertEquals($message, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(100, $exception->getCode());
|
|
|
|
|
+ $this->assertEquals($context, $exception->getContext());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testAuthException(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $message = 'Authentication failed';
|
|
|
|
|
+ $context = ['user_id' => '123'];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new AuthException($message, 401, null, $context);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $exception);
|
|
|
|
|
+ $this->assertInstanceOf(AuthException::class, $exception);
|
|
|
|
|
+ $this->assertEquals($message, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(401, $exception->getCode());
|
|
|
|
|
+ $this->assertEquals($context, $exception->getContext());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testHttpException(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $message = 'HTTP request failed';
|
|
|
|
|
+ $httpStatusCode = 500;
|
|
|
|
|
+ $context = ['url' => 'https://api.example.com'];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new HttpException($message, 0, null, $context, $httpStatusCode);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $exception);
|
|
|
|
|
+ $this->assertInstanceOf(HttpException::class, $exception);
|
|
|
|
|
+ $this->assertEquals($message, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(0, $exception->getCode());
|
|
|
|
|
+ $this->assertEquals($context, $exception->getContext());
|
|
|
|
|
+ $this->assertEquals($httpStatusCode, $exception->getHttpStatusCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testHttpExceptionWithDefaults(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $exception = new HttpException();
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertEquals('', $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(0, $exception->getCode());
|
|
|
|
|
+ $this->assertNull($exception->getContext());
|
|
|
|
|
+ $this->assertNull($exception->getHttpStatusCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testApiException(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $message = 'API error occurred';
|
|
|
|
|
+ $apiCode = 'E1001';
|
|
|
|
|
+ $responseData = ['error' => 'Invalid parameter', 'code' => 1001];
|
|
|
|
|
+ $context = ['request_id' => 'req_123'];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new ApiException($message, 0, null, $context, $apiCode, $responseData);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $exception);
|
|
|
|
|
+ $this->assertInstanceOf(ApiException::class, $exception);
|
|
|
|
|
+ $this->assertEquals($message, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(0, $exception->getCode());
|
|
|
|
|
+ $this->assertEquals($context, $exception->getContext());
|
|
|
|
|
+ $this->assertEquals($apiCode, $exception->getApiCode());
|
|
|
|
|
+ $this->assertEquals($responseData, $exception->getResponseData());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testApiExceptionWithDefaults(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $exception = new ApiException();
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertEquals('', $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(0, $exception->getCode());
|
|
|
|
|
+ $this->assertNull($exception->getContext());
|
|
|
|
|
+ $this->assertNull($exception->getApiCode());
|
|
|
|
|
+ $this->assertNull($exception->getResponseData());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testValidationException(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $message = 'Validation failed';
|
|
|
|
|
+ $errors = ['field1' => 'Field 1 is required', 'field2' => 'Field 2 is invalid'];
|
|
|
|
|
+ $context = ['form_id' => 'user_form'];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new ValidationException($message, 422, null, $context, $errors);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $exception);
|
|
|
|
|
+ $this->assertInstanceOf(ValidationException::class, $exception);
|
|
|
|
|
+ $this->assertEquals($message, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals(422, $exception->getCode());
|
|
|
|
|
+ $this->assertEquals($context, $exception->getContext());
|
|
|
|
|
+ $this->assertEquals($errors, $exception->getErrors());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testValidationExceptionWithEmptyErrors(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $exception = new ValidationException('Test validation error');
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertEquals([], $exception->getErrors());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testExceptionInheritance(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $configException = new ConfigException();
|
|
|
|
|
+ $authException = new AuthException();
|
|
|
|
|
+ $httpException = new HttpException();
|
|
|
|
|
+ $apiException = new ApiException();
|
|
|
|
|
+ $validationException = new ValidationException();
|
|
|
|
|
+
|
|
|
|
|
+ // All should inherit from WangdianException
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $configException);
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $authException);
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $httpException);
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $apiException);
|
|
|
|
|
+ $this->assertInstanceOf(WangdianException::class, $validationException);
|
|
|
|
|
+
|
|
|
|
|
+ // All should inherit from standard Exception
|
|
|
|
|
+ $this->assertInstanceOf(\Exception::class, $configException);
|
|
|
|
|
+ $this->assertInstanceOf(\Exception::class, $authException);
|
|
|
|
|
+ $this->assertInstanceOf(\Exception::class, $httpException);
|
|
|
|
|
+ $this->assertInstanceOf(\Exception::class, $apiException);
|
|
|
|
|
+ $this->assertInstanceOf(\Exception::class, $validationException);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testExceptionChaining(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $rootCause = new \RuntimeException('Root cause');
|
|
|
|
|
+ $httpException = new HttpException('HTTP error', 0, $rootCause);
|
|
|
|
|
+ $apiException = new ApiException('API error', 0, $httpException);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertSame($httpException, $apiException->getPrevious());
|
|
|
|
|
+ $this->assertSame($rootCause, $apiException->getPrevious()->getPrevious());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testContextDataTypes(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $complexContext = [
|
|
|
|
|
+ 'string' => 'value',
|
|
|
|
|
+ 'integer' => 123,
|
|
|
|
|
+ 'float' => 45.67,
|
|
|
|
|
+ 'boolean' => true,
|
|
|
|
|
+ 'array' => ['nested' => 'data'],
|
|
|
|
|
+ 'null' => null,
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new ApiException('Test', 0, null, $complexContext);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertEquals($complexContext, $exception->getContext());
|
|
|
|
|
+ $this->assertIsString($exception->getContext()['string']);
|
|
|
|
|
+ $this->assertIsInt($exception->getContext()['integer']);
|
|
|
|
|
+ $this->assertIsFloat($exception->getContext()['float']);
|
|
|
|
|
+ $this->assertIsBool($exception->getContext()['boolean']);
|
|
|
|
|
+ $this->assertIsArray($exception->getContext()['array']);
|
|
|
|
|
+ $this->assertNull($exception->getContext()['null']);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testUnicodeInExceptions(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $unicodeMessage = '错误信息:参数无效';
|
|
|
|
|
+ $unicodeContext = ['错误码' => '1001', '描述' => '用户名包含非法字符'];
|
|
|
|
|
+ $unicodeApiCode = 'E错误1001';
|
|
|
|
|
+
|
|
|
|
|
+ $exception = new ApiException($unicodeMessage, 0, null, $unicodeContext, $unicodeApiCode);
|
|
|
|
|
+
|
|
|
|
|
+ $this->assertEquals($unicodeMessage, $exception->getMessage());
|
|
|
|
|
+ $this->assertEquals($unicodeContext, $exception->getContext());
|
|
|
|
|
+ $this->assertEquals($unicodeApiCode, $exception->getApiCode());
|
|
|
|
|
+ }
|
|
|
|
|
+}
|