refactor(integration): resolver credenciales mediante instancias
Resuelve primero la instancia del cliente y luego la del tipo de sitio cuando existe contexto de tenant. Adapta SMTP y Telepagos, con tokens identificados por instancia y version de credenciales. Separa la edicion de instancias de sus asociaciones y conserva el guardado por cliente creando una instancia nueva. Impide eliminar instancias en uso y adapta las pruebas existentes, incluida la herencia SMTP.
This commit is contained in:
@@ -9,6 +9,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class IntegrationInstance extends Model
|
||||
{
|
||||
// The ciphertext changes whenever credentials are saved, so old tokens cannot be reused.
|
||||
public function tokenCacheKey(): string
|
||||
{
|
||||
return 'integration_token:instance:'.$this->id.':'.hash('sha256', (string) $this->getRawOriginal('integration_data'));
|
||||
}
|
||||
|
||||
protected $fillable = ['integration_code', 'name', 'integration_data'];
|
||||
|
||||
protected $hidden = ['integration_data'];
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace App\Domains\Integration\Services;
|
||||
use App\Domains\Client\Models\Client;
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
use App\Domains\Integration\Models\IntegrationInstance;
|
||||
use App\Domains\Integration\Models\WebsiteTypeIntegration;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Exception;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
@@ -32,9 +34,9 @@ abstract class BaseIntegrationService
|
||||
protected ?Integration $integration = null;
|
||||
|
||||
/**
|
||||
* The client-owned integration configuration.
|
||||
* The effective integration instance.
|
||||
*/
|
||||
protected ?ClientIntegration $clientIntegration = null;
|
||||
protected ?IntegrationInstance $integrationInstance = null;
|
||||
|
||||
/**
|
||||
* Set the integration code.
|
||||
@@ -85,7 +87,7 @@ abstract class BaseIntegrationService
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the integration definition and its client-owned configuration.
|
||||
* Load the integration definition and its effective instance configuration.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -95,6 +97,7 @@ abstract class BaseIntegrationService
|
||||
throw new Exception('Integration code is not set.');
|
||||
}
|
||||
|
||||
$this->integrationInstance = null;
|
||||
$this->integration = Integration::where('integration_code', $this->integrationCode)->first();
|
||||
if (! $this->integration) {
|
||||
throw new Exception("Integration with code '{$this->integrationCode}' not found.");
|
||||
@@ -104,11 +107,18 @@ abstract class BaseIntegrationService
|
||||
throw new Exception('Client context is not set.');
|
||||
}
|
||||
|
||||
$this->clientIntegration = ClientIntegration::where('client_id', $this->clientContext->id)
|
||||
$this->integrationInstance = ClientIntegration::with('integrationInstance')->where('client_id', $this->clientContext->id)
|
||||
->where('integration_code', $this->integrationCode)
|
||||
->first();
|
||||
->first()?->integrationInstance;
|
||||
|
||||
if (! $this->clientIntegration && $this->integration->requires_configuration) {
|
||||
if (! $this->integrationInstance && $this->tenant?->website_type_code) {
|
||||
$this->integrationInstance = WebsiteTypeIntegration::with('integrationInstance')
|
||||
->where('website_type_code', $this->tenant->website_type_code)
|
||||
->where('integration_code', $this->integrationCode)
|
||||
->first()?->integrationInstance;
|
||||
}
|
||||
|
||||
if (! $this->integrationInstance && $this->integration->requires_configuration) {
|
||||
throw new Exception("Client '{$this->clientContext->code}' does not have integration '{$this->integrationCode}' configured.");
|
||||
}
|
||||
}
|
||||
@@ -131,15 +141,15 @@ abstract class BaseIntegrationService
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an integration setting from the client-owned configuration.
|
||||
* Get an integration setting from the effective instance configuration.
|
||||
*/
|
||||
protected function getIntegrationSetting(string $key, mixed $default = null): mixed
|
||||
{
|
||||
if (! $this->clientIntegration || ! $this->clientIntegration->integration_data) {
|
||||
if (! $this->integrationInstance || ! $this->integrationInstance->integration_data) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->clientIntegration->integration_data[$key] ?? $default;
|
||||
return $this->integrationInstance->integration_data[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ class ClientIntegrationService
|
||||
public function getClientIntegration(Client $client, string $integrationCode): ?ClientIntegration
|
||||
{
|
||||
return $client->integrations()
|
||||
->with(['integration', 'integrationInstance'])
|
||||
->where('integration_code', $integrationCode)
|
||||
->first();
|
||||
}
|
||||
@@ -20,7 +21,7 @@ class ClientIntegrationService
|
||||
/** @return Collection<int, ClientIntegration> */
|
||||
public function getAllForClient(Client $client): Collection
|
||||
{
|
||||
return $client->integrations()->with('integration')->get();
|
||||
return $client->integrations()->with(['integration', 'integrationInstance'])->get();
|
||||
}
|
||||
|
||||
public function updateOrCreateIntegration(
|
||||
@@ -29,12 +30,18 @@ class ClientIntegrationService
|
||||
array $data,
|
||||
): ClientIntegration {
|
||||
return DB::transaction(function () use ($client, $integration, $data): ClientIntegration {
|
||||
// Legacy configuration endpoint replaces this client's instance only.
|
||||
$instance = app(IntegrationInstanceService::class)->create([
|
||||
'integration_code' => $integration->integration_code,
|
||||
'name' => $integration->name.' / '.$client->name,
|
||||
'integration_data' => $data,
|
||||
]);
|
||||
$clientIntegration = ClientIntegration::query()->updateOrCreate(
|
||||
[
|
||||
'client_id' => $client->id,
|
||||
'integration_code' => $integration->integration_code,
|
||||
],
|
||||
['integration_data' => $data],
|
||||
['integration_instance_id' => $instance->id],
|
||||
);
|
||||
|
||||
$service = $this->resolveService($integration->integration_code);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Integration\Services;
|
||||
|
||||
use App\Domains\Client\Models\Client;
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\IntegrationInstance;
|
||||
use App\Domains\Integration\Models\WebsiteTypeIntegration;
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
|
||||
class IntegrationAssociationService
|
||||
{
|
||||
public function associate(Client|WebsiteType $owner, string $code, IntegrationInstance $instance): ClientIntegration|WebsiteTypeIntegration
|
||||
{
|
||||
abort_unless($instance->integration_code === $code, 422, 'The instance belongs to another integration.');
|
||||
|
||||
return $owner->integrations()->updateOrCreate(
|
||||
['integration_code' => $code],
|
||||
['integration_instance_id' => $instance->id],
|
||||
)->load('integrationInstance');
|
||||
}
|
||||
|
||||
public function detach(Client|WebsiteType $owner, string $code): void
|
||||
{
|
||||
$owner->integrations()->where('integration_code', $code)->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Integration\Services;
|
||||
|
||||
use App\Domains\Integration\Models\IntegrationInstance;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class IntegrationInstanceService
|
||||
{
|
||||
public function create(array $data): IntegrationInstance
|
||||
{
|
||||
return IntegrationInstance::create($data);
|
||||
}
|
||||
|
||||
public function update(IntegrationInstance $instance, array $data): IntegrationInstance
|
||||
{
|
||||
return DB::transaction(function () use ($instance, $data): IntegrationInstance {
|
||||
$instance = IntegrationInstance::query()->lockForUpdate()->findOrFail($instance->id);
|
||||
$cacheKey = $instance->tokenCacheKey();
|
||||
$instance->update($data);
|
||||
if (array_key_exists('integration_data', $data)) {
|
||||
DB::afterCommit(fn () => Cache::forget($cacheKey));
|
||||
}
|
||||
|
||||
return $instance;
|
||||
});
|
||||
}
|
||||
|
||||
public function delete(IntegrationInstance $instance): void
|
||||
{
|
||||
DB::transaction(function () use ($instance): void {
|
||||
$instance = IntegrationInstance::query()->lockForUpdate()->findOrFail($instance->id);
|
||||
abort_if($instance->clientIntegrations()->exists() || $instance->websiteTypeIntegrations()->exists(), 409, 'Unlink the instance before deleting it.');
|
||||
$cacheKey = $instance->tokenCacheKey();
|
||||
$instance->delete();
|
||||
DB::afterCommit(fn () => Cache::forget($cacheKey));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class MailService extends BaseIntegrationService
|
||||
|
||||
private ?Mailer $mailer = null;
|
||||
|
||||
private bool $usesClientMailer = false;
|
||||
private bool $usesInstanceMailer = false;
|
||||
|
||||
public function __construct(?MailFactory $mailFactory = null)
|
||||
{
|
||||
@@ -40,12 +40,12 @@ class MailService extends BaseIntegrationService
|
||||
{
|
||||
parent::forTenant($tenantCode);
|
||||
|
||||
if ($this->clientIntegration) {
|
||||
if ($this->integrationInstance) {
|
||||
$this->mailer = $this->resolveMailer();
|
||||
$this->usesClientMailer = true;
|
||||
$this->usesInstanceMailer = true;
|
||||
} else {
|
||||
$this->mailer = $this->mailFactory->mailer();
|
||||
$this->usesClientMailer = false;
|
||||
$this->usesInstanceMailer = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -56,12 +56,12 @@ class MailService extends BaseIntegrationService
|
||||
parent::forClient($client);
|
||||
$this->tenant = $this->clientContext?->tenants()->first();
|
||||
|
||||
if ($this->clientIntegration) {
|
||||
if ($this->integrationInstance) {
|
||||
$this->mailer = $this->resolveMailer();
|
||||
$this->usesClientMailer = true;
|
||||
$this->usesInstanceMailer = true;
|
||||
} else {
|
||||
$this->mailer = $this->mailFactory->mailer();
|
||||
$this->usesClientMailer = false;
|
||||
$this->usesInstanceMailer = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -122,8 +122,8 @@ class MailService extends BaseIntegrationService
|
||||
|
||||
public function mailerName(): string
|
||||
{
|
||||
return $this->usesClientMailer
|
||||
? 'client-smtp'
|
||||
return $this->usesInstanceMailer
|
||||
? 'integration-smtp'
|
||||
: (string) config('mail.default');
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ class MailService extends BaseIntegrationService
|
||||
|
||||
private function resolveMailer(): Mailer
|
||||
{
|
||||
$data = $this->clientIntegration?->integration_data;
|
||||
$data = $this->integrationInstance?->integration_data;
|
||||
|
||||
if (! is_array($data)) {
|
||||
throw new InvalidArgumentException('La configuración SMTP del cliente no es válida.');
|
||||
@@ -205,7 +205,7 @@ class MailService extends BaseIntegrationService
|
||||
}
|
||||
|
||||
$mailer = $this->mailFactory->build([
|
||||
'name' => 'client-smtp-'.$this->clientContext?->id,
|
||||
'name' => 'integration-smtp-'.$this->integrationInstance?->id,
|
||||
'transport' => 'smtp',
|
||||
'scheme' => $data['MAIL_SCHEME'] ?? null,
|
||||
'host' => $data['MAIL_HOST'],
|
||||
|
||||
@@ -45,11 +45,11 @@ class TelepagosIntegrationService extends BaseIntegrationService
|
||||
*/
|
||||
public function getToken(): string
|
||||
{
|
||||
if (! $this->clientIntegration || ! $this->clientContext) {
|
||||
if (! $this->integrationInstance) {
|
||||
throw new Exception('Client integration is not loaded. Call forTenant() or forClient() first.');
|
||||
}
|
||||
|
||||
$cacheKey = "integration_token:{$this->clientContext->id}:{$this->integrationCode}";
|
||||
$cacheKey = $this->integrationInstance->tokenCacheKey();
|
||||
|
||||
$token = Cache::get($cacheKey);
|
||||
|
||||
@@ -94,7 +94,7 @@ class TelepagosIntegrationService extends BaseIntegrationService
|
||||
// Calculate TTL and subtract a buffer of 60 seconds
|
||||
$ttlSeconds = max(1, $expiresAt->diffInSeconds(now()) - 60);
|
||||
|
||||
$cacheKey = "integration_token:{$this->clientContext->id}:{$this->integrationCode}";
|
||||
$cacheKey = $this->integrationInstance->tokenCacheKey();
|
||||
Cache::put($cacheKey, $token, $ttlSeconds);
|
||||
|
||||
return $token;
|
||||
@@ -220,11 +220,11 @@ class TelepagosIntegrationService extends BaseIntegrationService
|
||||
*/
|
||||
public function clearToken(): void
|
||||
{
|
||||
if (! $this->clientContext) {
|
||||
if (! $this->integrationInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cacheKey = "integration_token:{$this->clientContext->id}:{$this->integrationCode}";
|
||||
$cacheKey = $this->integrationInstance->tokenCacheKey();
|
||||
Cache::forget($cacheKey);
|
||||
}
|
||||
|
||||
|
||||
21
tests/Concerns/CreatesIntegrationInstances.php
Normal file
21
tests/Concerns/CreatesIntegrationInstances.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Concerns;
|
||||
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\IntegrationInstance;
|
||||
|
||||
trait CreatesIntegrationInstances
|
||||
{
|
||||
private function createClientIntegration(array $attributes): ClientIntegration
|
||||
{
|
||||
$instance = IntegrationInstance::create([
|
||||
'integration_code' => $attributes['integration_code'],
|
||||
'name' => 'Test instance',
|
||||
'integration_data' => $attributes['integration_data'],
|
||||
]);
|
||||
unset($attributes['integration_data']);
|
||||
|
||||
return ClientIntegration::create($attributes + ['integration_instance_id' => $instance->id]);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ namespace Tests\Feature\Integration;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
use App\Domains\Integration\Services\TelepagosIntegrationService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
@@ -15,10 +14,12 @@ use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\Concerns\CreatesIntegrationInstances;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IntegrationServiceTest extends TestCase
|
||||
{
|
||||
use CreatesIntegrationInstances;
|
||||
use RefreshDatabase;
|
||||
|
||||
private Tenant $tenant;
|
||||
@@ -110,7 +111,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -139,7 +140,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -198,7 +199,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -242,7 +243,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -272,7 +273,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -323,7 +324,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -375,7 +376,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -427,7 +428,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -480,7 +481,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -541,7 +542,7 @@ class IntegrationServiceTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $this->tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
@@ -566,7 +567,7 @@ class IntegrationServiceTest extends TestCase
|
||||
Log::shouldReceive('error')
|
||||
->once()
|
||||
->with('Telepagos get cash-in details failed: Cashin no encontrado', \Mockery::on(function ($context) {
|
||||
return $context['cashin_id'] === 6351
|
||||
return $context['cashin_id'] === '6351'
|
||||
&& $context['response_status'] === 404
|
||||
&& $context['response_body'] === ['status' => 'error', 'message' => 'Cashin no encontrado'];
|
||||
}));
|
||||
|
||||
@@ -4,28 +4,32 @@ namespace Tests\Feature\Integration;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
use App\Domains\Integration\Models\IntegrationInstance;
|
||||
use App\Domains\Integration\Services\ClientIntegrationService;
|
||||
use App\Domains\Integration\Services\IntegrationAssociationService;
|
||||
use App\Domains\Integration\Services\MailService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailer;
|
||||
use Illuminate\Mail\MailManager;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Mockery;
|
||||
use Tests\Concerns\CreatesIntegrationInstances;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MailServiceTest extends TestCase
|
||||
{
|
||||
use CreatesIntegrationInstances;
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_builds_an_isolated_smtp_mailer_from_the_client_integration(): void
|
||||
{
|
||||
$tenant = $this->createTenant();
|
||||
$this->createEmailIntegration();
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $tenant->client_id,
|
||||
'integration_code' => 'email',
|
||||
'integration_data' => $this->emailData(),
|
||||
@@ -40,7 +44,7 @@ class MailServiceTest extends TestCase
|
||||
$manager->shouldReceive('build')
|
||||
->once()
|
||||
->with(Mockery::on(fn (array $config): bool => $config === [
|
||||
'name' => 'client-smtp-'.$tenant->client_id,
|
||||
'name' => 'integration-smtp-'.IntegrationInstance::firstOrFail()->id,
|
||||
'transport' => 'smtp',
|
||||
'scheme' => 'smtp',
|
||||
'host' => 'smtp.example.com',
|
||||
@@ -54,7 +58,7 @@ class MailServiceTest extends TestCase
|
||||
|
||||
$service = (new MailService($manager))->forTenant($tenant->codigo);
|
||||
|
||||
$this->assertSame('client-smtp', $service->mailerName());
|
||||
$this->assertSame('integration-smtp', $service->mailerName());
|
||||
}
|
||||
|
||||
public function test_it_uses_the_default_mailer_when_client_configuration_is_not_required(): void
|
||||
@@ -84,7 +88,7 @@ class MailServiceTest extends TestCase
|
||||
Mail::fake();
|
||||
$tenant = $this->createTenant();
|
||||
$this->createEmailIntegration();
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $tenant->client_id,
|
||||
'integration_code' => 'email',
|
||||
'integration_data' => $this->emailData(),
|
||||
@@ -118,6 +122,24 @@ class MailServiceTest extends TestCase
|
||||
Mail::assertSent(Mailable::class, 1);
|
||||
}
|
||||
|
||||
public function test_it_uses_the_website_type_smtp_instance_without_a_client_association(): void
|
||||
{
|
||||
Mail::fake();
|
||||
$tenant = $this->createTenant();
|
||||
$this->createEmailIntegration();
|
||||
$type = WebsiteType::create(['codigo' => 'mail-brand', 'nombre' => 'Mail Brand']);
|
||||
$tenant->update(['website_type_code' => $type->codigo]);
|
||||
$instance = IntegrationInstance::create([
|
||||
'integration_code' => 'email', 'name' => 'Brand SMTP', 'integration_data' => $this->emailData(),
|
||||
]);
|
||||
(new IntegrationAssociationService)->associate($type, 'email', $instance);
|
||||
|
||||
$service = (new MailService)->forTenant($tenant->codigo);
|
||||
self::assertSame('integration-smtp', $service->mailerName());
|
||||
$service->send('customer@example.com', 'Brand mail', '<p>Brand SMTP</p>');
|
||||
Mail::assertSent(Mailable::class, 1);
|
||||
}
|
||||
|
||||
private function createTenant(): Tenant
|
||||
{
|
||||
$logo = Attachment::create([
|
||||
|
||||
@@ -11,7 +11,6 @@ use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Purchase\Models\TelepagosPayment;
|
||||
@@ -22,10 +21,12 @@ use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\Concerns\CreatesIntegrationInstances;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TelepagosWebhookTest extends TestCase
|
||||
{
|
||||
use CreatesIntegrationInstances;
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
@@ -633,7 +634,7 @@ class TelepagosWebhookTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $tenant->client_id,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
|
||||
@@ -4,17 +4,18 @@ namespace Tests\Feature\MailTest;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Integration\Models\ClientIntegration;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
use App\Domains\MailTest\Mailables\TestMail;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Tests\Concerns\CreatesIntegrationInstances;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MailTestControllerTest extends TestCase
|
||||
{
|
||||
use CreatesIntegrationInstances;
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_sends_a_test_email(): void
|
||||
@@ -22,7 +23,7 @@ class MailTestControllerTest extends TestCase
|
||||
Mail::fake();
|
||||
$tenant = $this->createTenant();
|
||||
|
||||
$response = $this->postJson('/api/acme/mail-test/send', [
|
||||
$response = $this->withHeader('Accept-Language', 'es')->postJson('/api/acme/mail-test/send', [
|
||||
'to' => 'recipient@example.com',
|
||||
'subject' => 'SMTP test',
|
||||
'message' => 'Test message',
|
||||
@@ -32,7 +33,7 @@ class MailTestControllerTest extends TestCase
|
||||
->assertJsonPath('message', 'Correo de prueba enviado correctamente.')
|
||||
->assertJsonPath('recipient', 'recipient@example.com')
|
||||
->assertJsonPath('tenant_code', 'acme')
|
||||
->assertJsonPath('mailer', 'tenant-smtp')
|
||||
->assertJsonPath('mailer', 'integration-smtp')
|
||||
->assertJsonStructure(['sent_at']);
|
||||
|
||||
Mail::assertSent(Mailable::class, function (Mailable $mail) use ($tenant): bool {
|
||||
@@ -48,7 +49,7 @@ class MailTestControllerTest extends TestCase
|
||||
Mail::fake();
|
||||
$this->createTenant();
|
||||
|
||||
$this->postJson('/api/acme/mail-test/send', [
|
||||
$this->withHeader('Accept-Language', 'es')->postJson('/api/acme/mail-test/send', [
|
||||
'to' => 'recipient@example.com',
|
||||
])->assertOk();
|
||||
|
||||
@@ -63,7 +64,7 @@ class MailTestControllerTest extends TestCase
|
||||
Mail::fake();
|
||||
$this->createTenant();
|
||||
|
||||
$this->postJson('/api/acme/mail-test/send', [
|
||||
$this->withHeader('Accept-Language', 'es')->postJson('/api/acme/mail-test/send', [
|
||||
'to' => 'invalid-email',
|
||||
])->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['to']);
|
||||
@@ -117,7 +118,7 @@ class MailTestControllerTest extends TestCase
|
||||
{
|
||||
Mail::fake();
|
||||
|
||||
$this->postJson('/api/unknown/mail-test/send', [
|
||||
$this->withHeader('Accept-Language', 'es')->postJson('/api/unknown/mail-test/send', [
|
||||
'to' => 'recipient@example.com',
|
||||
])->assertNotFound();
|
||||
|
||||
@@ -129,7 +130,7 @@ class MailTestControllerTest extends TestCase
|
||||
Mail::fake();
|
||||
$tenant = $this->createTenant();
|
||||
|
||||
$this->postJson("/api/{$tenant->id}/mail-test/send", [
|
||||
$this->withHeader('Accept-Language', 'es')->postJson("/api/{$tenant->id}/mail-test/send", [
|
||||
'to' => 'recipient@example.com',
|
||||
])->assertNotFound();
|
||||
|
||||
@@ -171,7 +172,7 @@ class MailTestControllerTest extends TestCase
|
||||
'integration_code' => 'email',
|
||||
'name' => 'Email',
|
||||
]);
|
||||
ClientIntegration::create([
|
||||
$this->createClientIntegration([
|
||||
'client_id' => $tenant->client_id,
|
||||
'integration_code' => 'email',
|
||||
'integration_data' => [
|
||||
|
||||
Reference in New Issue
Block a user