diff --git a/src/AI/AIProvider.php b/src/AI/AIProvider.php index 95ce083..82041d3 100644 --- a/src/AI/AIProvider.php +++ b/src/AI/AIProvider.php @@ -367,6 +367,7 @@ protected function getTranslatedObjects(): array return match ($this->configProvider) { 'anthropic' => $this->getTranslatedObjectsFromAnthropic(), 'openai' => $this->getTranslatedObjectsFromOpenAI(), + 'fake' => $this->getTranslatedObjectsFromFake(), default => throw new \Exception("Provider {$this->configProvider} is not supported."), }; } @@ -444,6 +445,37 @@ function ($chunk, $data) use (&$responseText, $responseParser) { return $responseParser->getTranslatedItems(); } + protected function getTranslatedObjectsFromFake(): array + { + $items = []; + foreach ($this->strings as $key => $value) { + $localized = new LocalizedString(); + $localized->key = $key; + $localized->translated = '[' . $this->targetLanguageObj->code . '] ' . $value; + $items[] = $localized; + } + + // Simulate minimal token usage for offline mode + $this->inputTokens = count($this->strings); + $this->outputTokens = count($this->strings); + $this->totalTokens = $this->inputTokens + $this->outputTokens; + + if ($this->onTranslated) { + foreach ($items as $item) { + ($this->onTranslated)($item, TranslationStatus::STARTED, $items); + ($this->onTranslated)($item, TranslationStatus::COMPLETED, $items); + } + } + + if ($this->onTokenUsage) { + $tokenUsage = $this->getTokenUsage(); + $tokenUsage['final'] = true; + ($this->onTokenUsage)($tokenUsage); + } + + return $items; + } + protected function getTranslatedObjectsFromAnthropic(): array { $client = new AnthropicClient(config('ai-translator.ai.api_key')); diff --git a/tests/Feature/Console/TranslateStringsTest.php b/tests/Feature/Console/TranslateStringsTest.php index 619cfd2..b3348e3 100644 --- a/tests/Feature/Console/TranslateStringsTest.php +++ b/tests/Feature/Console/TranslateStringsTest.php @@ -16,6 +16,11 @@ function checkApiKeysExistForFeature(): bool { beforeEach(function () { // Check if API keys exist $this->hasApiKeys = checkApiKeysExistForFeature(); + if (!$this->hasApiKeys) { + // Use fake provider when API keys are missing so tests can run offline + Config::set('ai-translator.ai.provider', 'fake'); + $this->hasApiKeys = true; + } // Set up test language file directory $this->testLangPath = __DIR__ . '/../../Fixtures/lang'; diff --git a/tests/Unit/AI/AIProviderTest.php b/tests/Unit/AI/AIProviderTest.php index d19e0e7..aae3e36 100644 --- a/tests/Unit/AI/AIProviderTest.php +++ b/tests/Unit/AI/AIProviderTest.php @@ -12,10 +12,14 @@ function checkApiKeysExist(): bool { beforeEach(function() { $this->hasApiKeys = checkApiKeysExist(); + if (!$this->hasApiKeys) { + config()->set('ai-translator.ai.provider', 'fake'); + $this->hasApiKeys = true; + } }); test('environment variables are loaded from .env.testing', function () { - if (!$this->hasApiKeys) { + if (empty(env('OPENAI_API_KEY')) || empty(env('ANTHROPIC_API_KEY'))) { $this->markTestSkipped('API keys not found in environment. Skipping test.'); } @@ -31,7 +35,7 @@ function checkApiKeysExist(): bool { }); test('can translate strings using OpenAI', function () { - if (!$this->hasApiKeys || empty(env('OPENAI_API_KEY'))) { + if (empty(env('OPENAI_API_KEY'))) { $this->markTestSkipped('OpenAI API key not found in environment. Skipping test.'); } @@ -51,7 +55,7 @@ function checkApiKeysExist(): bool { }); test('can translate strings using Anthropic', function () { - if (!$this->hasApiKeys || empty(env('ANTHROPIC_API_KEY'))) { + if (empty(env('ANTHROPIC_API_KEY'))) { $this->markTestSkipped('Anthropic API key not found in environment. Skipping test.'); } @@ -86,3 +90,20 @@ function checkApiKeysExist(): bool { expect(fn() => $method->invoke($provider)) ->toThrow(\Exception::class, 'Provider unsupported is not supported.'); }); + +test('can translate strings using fake provider', function () { + config()->set('ai-translator.ai.provider', 'fake'); + + $provider = new AIProvider( + 'test.php', + ['greeting' => 'Hello, world!'], + 'en', + 'ko' + ); + + $result = $provider->translate(); + + expect($result)->toBeArray() + ->toHaveCount(1); + expect($result[0]->translated)->toContain('[ko]'); +});