diff --git a/.github/workflows/php-test.yml b/.github/workflows/php-test.yml
new file mode 100644
index 0000000..bfdc56b
--- /dev/null
+++ b/.github/workflows/php-test.yml
@@ -0,0 +1,61 @@
+name: Unit Tests
+
+on:
+ push:
+ branches:
+ - trunk
+ - 'feature/**'
+ - 'release/**'
+ # Only run if PHP-related files changed.
+ paths:
+ - '.github/workflows/php-test.yml'
+ - '**.php'
+ - 'phpunit.xml.dist'
+ - 'composer.json'
+ - 'composer.lock'
+ pull_request:
+ # Only run if PHP-related files changed.
+ paths:
+ - '.github/workflows/php-test.yml'
+ - '**.php'
+ - 'phpunit.xml.dist'
+ - 'composer.json'
+ - 'composer.lock'
+ types:
+ - opened
+ - reopened
+ - synchronize
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-version: ['7.4', '8.0', '8.4']
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+ coverage: xdebug
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate --strict
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v3
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-${{ matrix.php-version }}-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
+
+ - name: Run unit tests
+ run: composer phpunit
diff --git a/.gitignore b/.gitignore
index dfcbf4f..9a14a78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,9 @@ vendor/
CLAUDE.md
.cursor/
GEMINI.md
+
+############
+## PHPUnit
+############
+
+.phpunit.cache/
\ No newline at end of file
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..035bf01
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,27 @@
+
+
+
+
+ tests/unit
+
+
+
+
+
+ src
+
+
+
diff --git a/tests/mocks/Enums/InvalidNameTestEnum.php b/tests/mocks/Enums/InvalidNameTestEnum.php
new file mode 100644
index 0000000..3023bde
--- /dev/null
+++ b/tests/mocks/Enums/InvalidNameTestEnum.php
@@ -0,0 +1,17 @@
+assertInstanceOf(ValidTestEnum::class, $enum);
+ $this->assertSame('first', $enum->value);
+ $this->assertSame('FIRST_NAME', $enum->name);
+ }
+
+
+ /**
+ * Tests that from() throws an exception for invalid values.
+ */
+ public function testFromWithInvalidValueThrowsException(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('invalid is not a valid backing value for enum WordPress\AiClient\Tests\mocks\Enums\ValidTestEnum');
+ ValidTestEnum::from('invalid');
+ }
+
+ /**
+ * Tests that tryFrom() returns an enum instance for valid values.
+ */
+ public function testTryFromWithValidValue(): void
+ {
+ $enum = ValidTestEnum::tryFrom('first');
+ $this->assertInstanceOf(ValidTestEnum::class, $enum);
+ $this->assertSame('first', $enum->value);
+ }
+
+ /**
+ * Tests that tryFrom() returns null for invalid values.
+ */
+ public function testTryFromWithInvalidValueReturnsNull(): void
+ {
+ $enum = ValidTestEnum::tryFrom('invalid');
+ $this->assertNull($enum);
+ }
+
+ /**
+ * Tests that cases() returns all enum instances.
+ */
+ public function testCasesReturnsAllEnumInstances(): void
+ {
+ $cases = ValidTestEnum::cases();
+ $this->assertCount(2, $cases);
+
+ $values = array_map(fn($case) => $case->value, $cases);
+ $this->assertContains('first', $values);
+ $this->assertContains('last', $values);
+
+ $names = array_map(fn($case) => $case->name, $cases);
+ $this->assertContains('FIRST_NAME', $names);
+ $this->assertContains('LAST_NAME', $names);
+ }
+
+ /**
+ * Tests that enum instances are singletons.
+ */
+ public function testSingletonBehavior(): void
+ {
+ $enum1 = ValidTestEnum::from('first');
+ $enum2 = ValidTestEnum::from('first');
+ $enum3 = ValidTestEnum::firstName();
+
+ $this->assertSame($enum1, $enum2);
+ $this->assertSame($enum1, $enum3);
+ }
+
+ /**
+ * Tests static factory methods for creating enum instances.
+ */
+ public function testStaticFactoryMethods(): void
+ {
+ $firstName = ValidTestEnum::firstName();
+ $this->assertSame('first', $firstName->value);
+ $this->assertSame('FIRST_NAME', $firstName->name);
+
+ $lastName = ValidTestEnum::lastName();
+ $this->assertSame('last', $lastName->value);
+ $this->assertSame('LAST_NAME', $lastName->name);
+ }
+
+ /**
+ * Tests that invalid static methods throw exceptions.
+ */
+ public function testInvalidStaticMethodThrowsException(): void
+ {
+ $this->expectException(BadMethodCallException::class);
+ $this->expectExceptionMessage(
+ 'Method WordPress\AiClient\Tests\mocks\Enums\ValidTestEnum::invalidMethod does not exist'
+ );
+ ValidTestEnum::invalidMethod();
+ }
+
+ /**
+ * Tests the is* check methods.
+ */
+ public function testIsCheckMethods(): void
+ {
+ $enum = ValidTestEnum::firstName();
+
+ $this->assertTrue($enum->isFirstName());
+ $this->assertFalse($enum->isLastName());
+ }
+
+ /**
+ * Tests that invalid is* methods throw exceptions.
+ */
+ public function testInvalidIsMethodThrowsException(): void
+ {
+ $enum = ValidTestEnum::firstName();
+
+ $this->expectException(BadMethodCallException::class);
+ $this->expectExceptionMessage(
+ 'Method WordPress\AiClient\Tests\mocks\Enums\ValidTestEnum::isInvalidMethod does not exist'
+ );
+ $enum->isInvalidMethod();
+ }
+
+ /**
+ * Tests the equals() method with various values.
+ */
+ public function testEqualsWithSameValue(): void
+ {
+ $enum = ValidTestEnum::firstName();
+
+ $this->assertTrue($enum->equals('first'));
+ $this->assertTrue($enum->equals(ValidTestEnum::firstName()));
+ $this->assertFalse($enum->equals('last'));
+ $this->assertFalse($enum->equals(ValidTestEnum::lastName()));
+ }
+
+
+ /**
+ * Tests the is() method for identity comparison.
+ */
+ public function testIsMethodForIdentityComparison(): void
+ {
+ $enum1 = ValidTestEnum::firstName();
+ $enum2 = ValidTestEnum::firstName();
+ $enum3 = ValidTestEnum::lastName();
+
+ $this->assertTrue($enum1->is($enum2)); // Same instance
+ $this->assertFalse($enum1->is($enum3)); // Different instance
+ }
+
+ /**
+ * Tests that getValues() returns all valid enum values.
+ */
+ public function testGetValuesReturnsAllValidValues(): void
+ {
+ $values = ValidTestEnum::getValues();
+
+ $this->assertSame([
+ 'FIRST_NAME' => 'first',
+ 'LAST_NAME' => 'last',
+ ], $values);
+ }
+
+ /**
+ * Tests the isValidValue() method.
+ */
+ public function testIsValidValue(): void
+ {
+ $this->assertTrue(ValidTestEnum::isValidValue('first'));
+ $this->assertTrue(ValidTestEnum::isValidValue('last'));
+
+ $this->assertFalse(ValidTestEnum::isValidValue('invalid'));
+ }
+
+ /**
+ * Tests that enum properties are read-only.
+ */
+ public function testPropertiesAreReadOnly(): void
+ {
+ $enum = ValidTestEnum::firstName();
+
+ $this->expectException(BadMethodCallException::class);
+ $this->expectExceptionMessage(
+ 'Cannot modify property WordPress\AiClient\Tests\mocks\Enums\ValidTestEnum::value - enum properties are read-only'
+ );
+ $enum->value = 'modified';
+ }
+
+ /**
+ * Tests that accessing invalid properties throws exceptions.
+ */
+ public function testInvalidPropertyAccessThrowsException(): void
+ {
+ $enum = ValidTestEnum::firstName();
+
+ $this->expectException(BadMethodCallException::class);
+ $this->expectExceptionMessage(
+ 'Property WordPress\AiClient\Tests\mocks\Enums\ValidTestEnum::invalid does not exist'
+ );
+ $enum->invalid;
+ }
+
+ /**
+ * Tests the __toString() method.
+ */
+ public function testToString(): void
+ {
+ $stringEnum = ValidTestEnum::firstName();
+
+ $this->assertSame('first', (string) $stringEnum);
+ }
+
+ /**
+ * Tests that invalid constant names throw exceptions.
+ */
+ public function testInvalidConstantNameThrowsException(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage(
+ 'Invalid enum constant name "invalid_name" in ' .
+ 'WordPress\AiClient\Tests\mocks\Enums\InvalidNameTestEnum. Constants must be UPPER_SNAKE_CASE.'
+ );
+
+ InvalidNameTestEnum::cases();
+ }
+
+ /**
+ * Tests that invalid constant types throw exceptions.
+ */
+ public function testInvalidConstantTypeThrowsException(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage(
+ 'Invalid enum value type for constant ' .
+ 'WordPress\AiClient\Tests\mocks\Enums\InvalidTypeTestEnum::INT_VALUE. ' .
+ 'Only string values are allowed, integer given.'
+ );
+
+ InvalidTypeTestEnum::cases();
+ }
+}
diff --git a/tests/unit/EnumTestTrait.php b/tests/unit/EnumTestTrait.php
new file mode 100644
index 0000000..3d282fd
--- /dev/null
+++ b/tests/unit/EnumTestTrait.php
@@ -0,0 +1,133 @@
+ The enum class name.
+ */
+ abstract protected function getEnumClass(): string;
+
+ /**
+ * Gets expected enum values and their constant names.
+ *
+ * @return array Array of CONSTANT_NAME => value.
+ */
+ abstract protected function getExpectedValues(): array;
+
+ /**
+ * Tests that the enum has expected values.
+ */
+ public function testEnumHasExpectedValues(): void
+ {
+ $enumClass = $this->getEnumClass();
+ $expectedValues = $this->getExpectedValues();
+
+ $actualValues = $enumClass::getValues();
+
+ $this->assertEquals($expectedValues, $actualValues);
+ }
+
+ /**
+ * Tests that enum cases return correct instances.
+ */
+ public function testEnumCasesReturnCorrectInstances(): void
+ {
+ $enumClass = $this->getEnumClass();
+ $expectedValues = $this->getExpectedValues();
+
+ $cases = $enumClass::cases();
+
+ $this->assertCount(count($expectedValues), $cases);
+
+ foreach ($cases as $case) {
+ $this->assertInstanceOf($enumClass, $case);
+ $this->assertContains($case->value, $expectedValues);
+ $this->assertArrayHasKey($case->name, $expectedValues);
+ $this->assertEquals($expectedValues[$case->name], $case->value);
+ }
+ }
+
+ /**
+ * Tests that the from() method works correctly.
+ */
+ public function testEnumFromMethodWorks(): void
+ {
+ $enumClass = $this->getEnumClass();
+ $expectedValues = $this->getExpectedValues();
+
+ foreach ($expectedValues as $name => $value) {
+ $enum = $enumClass::from($value);
+ $this->assertInstanceOf($enumClass, $enum);
+ $this->assertEquals($value, $enum->value);
+ $this->assertEquals($name, $enum->name);
+ }
+ }
+
+ /**
+ * Tests that the tryFrom() method works correctly.
+ */
+ public function testEnumTryFromMethodWorks(): void
+ {
+ $enumClass = $this->getEnumClass();
+ $expectedValues = $this->getExpectedValues();
+
+ foreach ($expectedValues as $value) {
+ $enum = $enumClass::tryFrom($value);
+ $this->assertInstanceOf($enumClass, $enum);
+ }
+
+ // Test invalid value
+ $invalidEnum = $enumClass::tryFrom('definitely_not_a_valid_value_12345');
+ $this->assertNull($invalidEnum);
+ }
+
+ /**
+ * Tests enum singleton behavior.
+ */
+ public function testEnumSingletonBehavior(): void
+ {
+ $enumClass = $this->getEnumClass();
+ $expectedValues = $this->getExpectedValues();
+
+ if (empty($expectedValues)) {
+ $this->markTestSkipped('No enum values to test');
+ }
+
+ $firstValue = reset($expectedValues);
+
+ $enum1 = $enumClass::from($firstValue);
+ $enum2 = $enumClass::from($firstValue);
+
+ $this->assertSame($enum1, $enum2);
+ }
+
+ /**
+ * Tests that enum properties are read-only.
+ */
+ public function testEnumPropertiesAreReadOnly(): void
+ {
+ $enumClass = $this->getEnumClass();
+ $expectedValues = $this->getExpectedValues();
+
+ if (empty($expectedValues)) {
+ $this->markTestSkipped('No enum values to test');
+ }
+
+ $firstValue = reset($expectedValues);
+ $enum = $enumClass::from($firstValue);
+
+ $this->expectException(\BadMethodCallException::class);
+ $enum->value = 'modified';
+ }
+}
diff --git a/tests/unit/Messages/Enums/MessagePartTypeEnumTest.php b/tests/unit/Messages/Enums/MessagePartTypeEnumTest.php
new file mode 100644
index 0000000..937c6dc
--- /dev/null
+++ b/tests/unit/Messages/Enums/MessagePartTypeEnumTest.php
@@ -0,0 +1,63 @@
+ 'text',
+ 'INLINE_FILE' => 'inline_file',
+ 'REMOTE_FILE' => 'remote_file',
+ 'FUNCTION_CALL' => 'function_call',
+ 'FUNCTION_RESPONSE' => 'function_response',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $text = MessagePartTypeEnum::text();
+ $this->assertTrue($text->isText());
+ $this->assertFalse($text->isInlineFile());
+
+ $inlineFile = MessagePartTypeEnum::inlineFile();
+ $this->assertTrue($inlineFile->isInlineFile());
+ $this->assertFalse($inlineFile->isRemoteFile());
+
+ $functionCall = MessagePartTypeEnum::functionCall();
+ $this->assertTrue($functionCall->isFunctionCall());
+ $this->assertFalse($functionCall->isFunctionResponse());
+ }
+}
diff --git a/tests/unit/Messages/Enums/MessageRoleEnumTest.php b/tests/unit/Messages/Enums/MessageRoleEnumTest.php
new file mode 100644
index 0000000..9e35432
--- /dev/null
+++ b/tests/unit/Messages/Enums/MessageRoleEnumTest.php
@@ -0,0 +1,64 @@
+ 'user',
+ 'MODEL' => 'model',
+ 'SYSTEM' => 'system',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $user = MessageRoleEnum::user();
+ $this->assertTrue($user->isUser());
+ $this->assertFalse($user->isModel());
+ $this->assertFalse($user->isSystem());
+
+ $model = MessageRoleEnum::model();
+ $this->assertFalse($model->isUser());
+ $this->assertTrue($model->isModel());
+ $this->assertFalse($model->isSystem());
+
+ $system = MessageRoleEnum::system();
+ $this->assertFalse($system->isUser());
+ $this->assertFalse($system->isModel());
+ $this->assertTrue($system->isSystem());
+ }
+}
diff --git a/tests/unit/Messages/Enums/ModalityEnumTest.php b/tests/unit/Messages/Enums/ModalityEnumTest.php
new file mode 100644
index 0000000..cea4e5f
--- /dev/null
+++ b/tests/unit/Messages/Enums/ModalityEnumTest.php
@@ -0,0 +1,63 @@
+ 'text',
+ 'DOCUMENT' => 'document',
+ 'IMAGE' => 'image',
+ 'AUDIO' => 'audio',
+ 'VIDEO' => 'video',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $text = ModalityEnum::text();
+ $this->assertTrue($text->isText());
+ $this->assertFalse($text->isDocument());
+
+ $image = ModalityEnum::image();
+ $this->assertTrue($image->isImage());
+ $this->assertFalse($image->isAudio());
+
+ $video = ModalityEnum::video();
+ $this->assertTrue($video->isVideo());
+ $this->assertFalse($video->isText());
+ }
+}
diff --git a/tests/unit/Operations/Enums/OperationStateEnumTest.php b/tests/unit/Operations/Enums/OperationStateEnumTest.php
new file mode 100644
index 0000000..fd7f296
--- /dev/null
+++ b/tests/unit/Operations/Enums/OperationStateEnumTest.php
@@ -0,0 +1,63 @@
+ 'starting',
+ 'PROCESSING' => 'processing',
+ 'SUCCEEDED' => 'succeeded',
+ 'FAILED' => 'failed',
+ 'CANCELED' => 'canceled',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $starting = OperationStateEnum::starting();
+ $this->assertTrue($starting->isStarting());
+ $this->assertFalse($starting->isProcessing());
+
+ $succeeded = OperationStateEnum::succeeded();
+ $this->assertTrue($succeeded->isSucceeded());
+ $this->assertFalse($succeeded->isFailed());
+
+ $failed = OperationStateEnum::failed();
+ $this->assertTrue($failed->isFailed());
+ $this->assertFalse($failed->isCanceled());
+ }
+}
diff --git a/tests/unit/Providers/Enums/ProviderTypeEnumTest.php b/tests/unit/Providers/Enums/ProviderTypeEnumTest.php
new file mode 100644
index 0000000..e658231
--- /dev/null
+++ b/tests/unit/Providers/Enums/ProviderTypeEnumTest.php
@@ -0,0 +1,64 @@
+ 'cloud',
+ 'SERVER' => 'server',
+ 'CLIENT' => 'client',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $cloud = ProviderTypeEnum::cloud();
+ $this->assertTrue($cloud->isCloud());
+ $this->assertFalse($cloud->isServer());
+ $this->assertFalse($cloud->isClient());
+
+ $server = ProviderTypeEnum::server();
+ $this->assertFalse($server->isCloud());
+ $this->assertTrue($server->isServer());
+ $this->assertFalse($server->isClient());
+
+ $client = ProviderTypeEnum::client();
+ $this->assertFalse($client->isCloud());
+ $this->assertFalse($client->isServer());
+ $this->assertTrue($client->isClient());
+ }
+}
diff --git a/tests/unit/Providers/Enums/ToolTypeEnumTest.php b/tests/unit/Providers/Enums/ToolTypeEnumTest.php
new file mode 100644
index 0000000..ddd1a19
--- /dev/null
+++ b/tests/unit/Providers/Enums/ToolTypeEnumTest.php
@@ -0,0 +1,56 @@
+ 'function_declarations',
+ 'WEB_SEARCH' => 'web_search',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $functionDeclarations = ToolTypeEnum::functionDeclarations();
+ $this->assertTrue($functionDeclarations->isFunctionDeclarations());
+ $this->assertFalse($functionDeclarations->isWebSearch());
+
+ $webSearch = ToolTypeEnum::webSearch();
+ $this->assertFalse($webSearch->isFunctionDeclarations());
+ $this->assertTrue($webSearch->isWebSearch());
+ }
+}
diff --git a/tests/unit/Providers/Models/Enums/CapabilityEnumTest.php b/tests/unit/Providers/Models/Enums/CapabilityEnumTest.php
new file mode 100644
index 0000000..fa1e0b0
--- /dev/null
+++ b/tests/unit/Providers/Models/Enums/CapabilityEnumTest.php
@@ -0,0 +1,66 @@
+ 'text_generation',
+ 'IMAGE_GENERATION' => 'image_generation',
+ 'TEXT_TO_SPEECH_CONVERSION' => 'text_to_speech_conversion',
+ 'SPEECH_GENERATION' => 'speech_generation',
+ 'MUSIC_GENERATION' => 'music_generation',
+ 'VIDEO_GENERATION' => 'video_generation',
+ 'EMBEDDING_GENERATION' => 'embedding_generation',
+ 'CHAT_HISTORY' => 'chat_history',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $textGen = CapabilityEnum::textGeneration();
+ $this->assertTrue($textGen->isTextGeneration());
+ $this->assertFalse($textGen->isImageGeneration());
+
+ $imageGen = CapabilityEnum::imageGeneration();
+ $this->assertTrue($imageGen->isImageGeneration());
+ $this->assertFalse($imageGen->isTextToSpeechConversion());
+
+ $chatHistory = CapabilityEnum::chatHistory();
+ $this->assertTrue($chatHistory->isChatHistory());
+ $this->assertFalse($chatHistory->isEmbeddingGeneration());
+ }
+}
diff --git a/tests/unit/Providers/Models/Enums/OptionEnumTest.php b/tests/unit/Providers/Models/Enums/OptionEnumTest.php
new file mode 100644
index 0000000..6869898
--- /dev/null
+++ b/tests/unit/Providers/Models/Enums/OptionEnumTest.php
@@ -0,0 +1,68 @@
+ 'input_modalities',
+ 'OUTPUT_MODALITIES' => 'output_modalities',
+ 'SYSTEM_INSTRUCTION' => 'system_instruction',
+ 'CANDIDATE_COUNT' => 'candidate_count',
+ 'MAX_TOKENS' => 'max_tokens',
+ 'TEMPERATURE' => 'temperature',
+ 'TOP_K' => 'top_k',
+ 'TOP_P' => 'top_p',
+ 'OUTPUT_MIME_TYPE' => 'output_mime_type',
+ 'OUTPUT_SCHEMA' => 'output_schema',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $inputModalities = OptionEnum::inputModalities();
+ $this->assertTrue($inputModalities->isInputModalities());
+ $this->assertFalse($inputModalities->isOutputModalities());
+
+ $temperature = OptionEnum::temperature();
+ $this->assertTrue($temperature->isTemperature());
+ $this->assertFalse($temperature->isTopK());
+
+ $outputSchema = OptionEnum::outputSchema();
+ $this->assertTrue($outputSchema->isOutputSchema());
+ $this->assertFalse($outputSchema->isOutputMimeType());
+ }
+}
diff --git a/tests/unit/Results/Enums/FinishReasonEnumTest.php b/tests/unit/Results/Enums/FinishReasonEnumTest.php
new file mode 100644
index 0000000..4883203
--- /dev/null
+++ b/tests/unit/Results/Enums/FinishReasonEnumTest.php
@@ -0,0 +1,63 @@
+ 'stop',
+ 'LENGTH' => 'length',
+ 'CONTENT_FILTER' => 'content_filter',
+ 'TOOL_CALLS' => 'tool_calls',
+ 'ERROR' => 'error',
+ ];
+ }
+
+ /**
+ * Tests the specific enum methods.
+ *
+ * @return void
+ */
+ public function testSpecificEnumMethods(): void
+ {
+ $stop = FinishReasonEnum::stop();
+ $this->assertTrue($stop->isStop());
+ $this->assertFalse($stop->isLength());
+
+ $contentFilter = FinishReasonEnum::contentFilter();
+ $this->assertTrue($contentFilter->isContentFilter());
+ $this->assertFalse($contentFilter->isToolCalls());
+
+ $error = FinishReasonEnum::error();
+ $this->assertTrue($error->isError());
+ $this->assertFalse($error->isStop());
+ }
+}