Repository: mateusjunges/laravel-kafka (php)

Issue:
Facade Fake loses macros
When using macros to define global settings (f.e. sasl, serializer) the facade fake does not know about those macros.

Let's assume you have defined a macro like this:
```php
Kafka::macro('producer', function (string $topic) use ($serializer, $sasl) {
    return $this
        ->publishOn($topic)
        ->withSasl($sasl)
        ->usingSerializer($serializer($topic));
});
```

and then you want to test the producing of a message like this

```php
Kafka::fake();

Kafka::producer($topic)->withMessage(new Message(body: $body))->send();

Kafka::assertPublishedOn($topic);
```
the test will fail with the following message: 

>  Call to undefined method Junges\Kafka\Support\Testing\Fakes\KafkaFake::producer()

A possible solution would be to forward calls (see this PR in Laravel https://github.com/laravel/framework/pull/45988 which solves the same problem https://github.com/laravel/framework/issues/45986)

I've tried to implement it the same way but I had quite the troubles with it and didn't get it to work :(

Would be really cool if you or somebody else could make this happen

Pull request:
Fix passthru of missing methods to root
Follow up on #247. Should fix #245 

Patch:
diff --git a/src/Facades/Kafka.php b/src/Facades/Kafka.php
index b935b7a..2f28ea8 100644
--- a/src/Facades/Kafka.php
+++ b/src/Facades/Kafka.php
@@ -28,7 +28,9 @@ class Kafka extends Facade
      */
     public static function fake(): KafkaFake
     {
-        static::swap($fake = new KafkaFake(static::getFacadeRoot()));
+        static::swap($fake = new KafkaFake(
+            (static::getFacadeRoot())->shouldFake()
+        ));
 
         return $fake;
     }
diff --git a/src/Kafka.php b/src/Kafka.php
index 3573a89..0f7f71d 100644
--- a/src/Kafka.php
+++ b/src/Kafka.php
@@ -8,11 +8,14 @@ use Junges\Kafka\Contracts\CanConsumeMessagesFromKafka;
 use Junges\Kafka\Contracts\CanProduceMessages;
 use Junges\Kafka\Contracts\CanPublishMessagesToKafka;
 use Junges\Kafka\Producers\ProducerBuilder;
+use Junges\Kafka\Support\Testing\Fakes\KafkaFake;
 
 class Kafka implements CanPublishMessagesToKafka, CanConsumeMessagesFromKafka
 {
     use Macroable;
 
+    private bool $shouldFake = false;
+
     /**
      * Creates a new ProducerBuilder instance, setting brokers and topic.
      *
@@ -22,6 +25,10 @@ class Kafka implements CanPublishMessagesToKafka, CanConsumeMessagesFromKafka
      */
     public function publishOn(string $topic, string $broker = null): CanProduceMessages
     {
+        if ($this->shouldFake) {
+            return Facades\Kafka::fake()->publishOn($topic, $broker);
+        }
+
         return new ProducerBuilder(
             topic: $topic,
             broker: $broker ?? config('kafka.brokers')
@@ -38,10 +45,23 @@ class Kafka implements CanPublishMessagesToKafka, CanConsumeMessagesFromKafka
      */
     public function createConsumer(array $topics = [], string $groupId = null, string $brokers = null): ConsumerBuilder
     {
+        if ($this->shouldFake) {
+            return Facades\Kafka::fake()->createConsumer(
+                $topics, $groupId, $brokers
+            );
+        }
+
         return ConsumerBuilder::create(
             brokers: $brokers ?? config('kafka.brokers'),
             topics: $topics,
             groupId: $groupId ?? config('kafka.consumer_group_id')
         );
     }
+    
+    public function shouldFake(): self
+    {
+        $this->shouldFake = true;
+
+        return $this;
+    }
 }


Test patch:
diff --git a/src/Support/Testing/Fakes/KafkaFake.php b/src/Support/Testing/Fakes/KafkaFake.php
index c505d17..3986a96 100644
--- a/src/Support/Testing/Fakes/KafkaFake.php
+++ b/src/Support/Testing/Fakes/KafkaFake.php
@@ -9,6 +9,7 @@ use Junges\Kafka\Contracts\CanConsumeMessagesFromKafka;
 use Junges\Kafka\Contracts\CanPublishMessagesToKafka;
 use Junges\Kafka\Contracts\KafkaConsumerMessage;
 use Junges\Kafka\Contracts\KafkaProducerMessage;
+use Junges\Kafka\Kafka;
 use Junges\Kafka\Message\Message;
 use PHPUnit\Framework\Assert as PHPUnit;
 
@@ -16,16 +17,15 @@ class KafkaFake implements CanPublishMessagesToKafka
 {
     use ForwardsCalls;
 
-    private CanPublishMessagesToKafka&CanConsumeMessagesFromKafka $manager;
+    private ?Kafka $manager;
 
     private array $publishedMessages = [];
     /** @var \Junges\Kafka\Contracts\KafkaConsumerMessage[] */
     private array $messagesToConsume = [];
 
-    public function __construct(CanPublishMessagesToKafka&CanConsumeMessagesFromKafka $manager)
+    public function __construct(?Kafka $manager = null)
     {
-        $this->manager = $manager;
-        $this->makeProducerBuilderFake();
+        $this->manager = $manager?->shouldFake();
     }
 
     /**
@@ -164,8 +164,7 @@ class KafkaFake implements CanPublishMessagesToKafka
         return (new ProducerBuilderFake(
             topic: $topic,
             broker: $broker
-        )
-        )->withProducerCallback(fn (Message $message) => $this->publishedMessages[] = $message);
+        ))->withProducerCallback(fn (Message $message) => $this->publishedMessages[] = $message);
     }
 
     /**
diff --git a/src/Support/Testing/Fakes/ProducerFake.php b/src/Support/Testing/Fakes/ProducerFake.php
index 4e244aa..55bb96a 100644
--- a/src/Support/Testing/Fakes/ProducerFake.php
+++ b/src/Support/Testing/Fakes/ProducerFake.php
@@ -10,7 +10,6 @@ use RdKafka\Conf;
 
 class ProducerFake
 {
-    private array $messages = [];
     private ?Closure $producerCallback = null;
 
     public function __construct(
diff --git a/tests/KafkaTest.php b/tests/KafkaTest.php
index 1eb8230..6d52791 100644
--- a/tests/KafkaTest.php
+++ b/tests/KafkaTest.php
@@ -308,4 +308,23 @@ class KafkaTest extends LaravelKafkaTestCase
         $this->assertInstanceOf(ProducerBuilder::class, $producer);
         $this->assertEquals($sasl, $this->getPropertyWithReflection('saslConfig', $producer));
     }
+
+    /** @test */
+    public function it_stores_published_messages_when_using_macros(): void
+    {
+        $expectedMessage = (new Message)
+            ->withBodyKey('test', ['test'])
+            ->withHeaders(['custom' => 'header'])
+            ->withKey($uuid = Str::uuid()->toString());
+
+        Kafka::macro('testProducer', function () use ($expectedMessage) {
+            return $this->publishOn('topic')
+                ->withMessage($expectedMessage);
+        });
+
+        Kafka::fake();
+        Kafka::testProducer()->send();
+
+        Kafka::assertPublished($expectedMessage);
+    }
 }