diff --git a/lib/CompositionManager.php b/lib/CompositionManager.php index e0be254..9e25e2a 100644 --- a/lib/CompositionManager.php +++ b/lib/CompositionManager.php @@ -440,7 +440,14 @@ class CompositionManager { ]; } - $source = null; + // Reuse the synced remote draft (if any) so the provider can natively clean it up + $remoteEntity = $this->remoteEntityIdentifier($composed['remote']['entity'] ?? null); + $source = ($remoteEntity !== null + && $remoteEntity->provider() === $service->provider() + && (string)$remoteEntity->service() === (string)$service->identifier()) + ? $remoteEntity + : null; + $properties = $this->buildMessageProperties( service: $service, sender: $senderObject, diff --git a/tests/php/Unit/CompositionSynchronizationTest.php b/tests/php/Unit/CompositionSynchronizationTest.php index e8eae67..925af4f 100644 --- a/tests/php/Unit/CompositionSynchronizationTest.php +++ b/tests/php/Unit/CompositionSynchronizationTest.php @@ -285,6 +285,77 @@ final class CompositionSynchronizationTest extends TestCase { )); } + public function testSendReusesSyncedRemoteDraftAsSubmissionSource(): void { + $this->stageComposition('provider:service:drafts:remote-1'); + $properties = $this->createStub(MessagePropertiesMutableInterface::class); + $service = $this->createMockForIntersectionOfInterfaces([ + ServiceBaseInterface::class, + ServiceEntitySubmitInterface::class, + ]); + $entity = $this->createStub(EntityMutableInterface::class); + $entity->method('getProperties')->willReturn($properties); + $service->method('provider')->willReturn('provider'); + $service->method('identifier')->willReturn('service'); + $service->method('getEnabled')->willReturn(true); + $service->method('entityFresh')->willReturn($entity); + $service->expects($this->once()) + ->method('entitySubmit') + ->with( + $this->anything(), + $this->callback(static fn(?EntityIdentifier $source): bool => $source !== null + && (string)$source === 'provider:service:drafts:remote-1'), + $properties, + ) + ->willReturn(new EntitySubmitResult(EntitySubmitResult::DISPOSITION_SENT)); + + $mailManager = $this->createStub(\KTXM\MailManager\Manager::class); + $mailManager->method('serviceFindByAddress')->willReturn($service); + + $result = $this->compositionManager($mailManager)->send( + self::TENANT_ID, + self::USER_ID, + self::COMPOSITION_ID, + ['address' => 'sender@example.com'], + ['subject' => 'Sent draft'], + [], + ); + + $this->assertSame('sent', $result['disposition']); + } + + public function testSendIgnoresRemoteDraftBelongingToAnotherService(): void { + $this->stageComposition('provider:other-service:drafts:remote-1'); + $properties = $this->createStub(MessagePropertiesMutableInterface::class); + $service = $this->createMockForIntersectionOfInterfaces([ + ServiceBaseInterface::class, + ServiceEntitySubmitInterface::class, + ]); + $entity = $this->createStub(EntityMutableInterface::class); + $entity->method('getProperties')->willReturn($properties); + $service->method('provider')->willReturn('provider'); + $service->method('identifier')->willReturn('service'); + $service->method('getEnabled')->willReturn(true); + $service->method('entityFresh')->willReturn($entity); + $service->expects($this->once()) + ->method('entitySubmit') + ->with($this->anything(), $this->isNull(), $properties) + ->willReturn(new EntitySubmitResult(EntitySubmitResult::DISPOSITION_SENT)); + + $mailManager = $this->createStub(\KTXM\MailManager\Manager::class); + $mailManager->method('serviceFindByAddress')->willReturn($service); + + $result = $this->compositionManager($mailManager)->send( + self::TENANT_ID, + self::USER_ID, + self::COMPOSITION_ID, + ['address' => 'sender@example.com'], + ['subject' => 'Sent draft'], + [], + ); + + $this->assertSame('sent', $result['disposition']); + } + public function testSendFailureKeepsLocalComposition(): void { $this->stageComposition(); $properties = $this->createStub(MessagePropertiesMutableInterface::class); @@ -357,6 +428,7 @@ final class CompositionSynchronizationTest extends TestCase { ]); $entity = $this->createStub(EntityMutableInterface::class); $entity->method('getProperties')->willReturn($properties); + $service->method('provider')->willReturn('provider'); $service->method('identifier')->willReturn('service'); $service->method('getEnabled')->willReturn(true); $service->method('entityFresh')->willReturn($entity);