Blob Blame History Raw
From ef621c739aa892985937b7a2260111a102d98847 Mon Sep 17 00:00:00 2001
From: Adam Williamson <awilliam@redhat.com>
Date: Wed, 24 Jul 2019 11:40:10 -0700
Subject: [PATCH] AMQP: allow passing headers to publish_amqp

Fedora's new AMQP-based messaging system requires messages to
have certain headers:

https://fedora-messaging.readthedocs.io/en/stable/wire-format.html

I'm planning to write a plugin for emitting Fedora-compliant
AMQP messages as a subclass of the existing AMQP plugin, but in
order to be able to use `publish_amqp` in that plugin, I need to
be able to pass it headers like this. Otherwise I'd have to
reimplement it.

This implementation aims to ensure it works just as before if
you *don't* want to pass it headers, and without needing to
duplicate the `publish_p` calls for "headers" and "no headers"
cases - that's the reason for making `$headers` a reference to
an empty hash if it's not defined, and then checking that it's
a hashref: to be sure about what we're asking `publish_p` to do.
Passing it a ref to an empty hash should effectively be a no-op
so far as headers are concerned, and not interfere with passing
`routing_key` as a param.

Signed-off-by: Adam Williamson <awilliam@redhat.com>
---
 lib/OpenQA/WebAPI/Plugin/AMQP.pm |  6 ++--
 t/23-amqp.t                      | 59 ++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/lib/OpenQA/WebAPI/Plugin/AMQP.pm b/lib/OpenQA/WebAPI/Plugin/AMQP.pm
index e46d7915..7553bc3c 100644
--- a/lib/OpenQA/WebAPI/Plugin/AMQP.pm
+++ b/lib/OpenQA/WebAPI/Plugin/AMQP.pm
@@ -71,7 +71,9 @@ sub log_event {
 }
 
 sub publish_amqp {
-    my ($self, $topic, $event_data) = @_;
+    my ($self, $topic, $event_data, $headers) = @_;
+    $headers //= {};
+    die "publish_amqp headers must be a hashref!" unless (ref($headers) eq 'HASH');
 
     log_debug("Sending AMQP event: $topic");
     my $publisher = Mojo::RabbitMQ::Client::Publisher->new(
@@ -79,7 +81,7 @@ sub publish_amqp {
 
     # A hard reference to the publisher object needs to be kept until the event
     # has been published asynchronously, or it gets destroyed too early
-    $publisher->publish_p($event_data, routing_key => $topic)->then(
+    $publisher->publish_p($event_data, $headers, routing_key => $topic)->then(
         sub {
             log_debug "$topic published";
         }
diff --git a/t/23-amqp.t b/t/23-amqp.t
index b6d6f68d..4b7d82f3 100644
--- a/t/23-amqp.t
+++ b/t/23-amqp.t
@@ -29,6 +29,7 @@ use lib "$FindBin::Bin/lib";
 use OpenQA::Client;
 use OpenQA::Jobs::Constants;
 use OpenQA::Test::Database;
+use Test::Exception;
 use Test::MockModule;
 use Test::More;
 use Test::Mojo;
@@ -189,4 +190,62 @@ subtest 'create parent group comment' => sub {
     is($json->{parent_group_id}, 2000,  'parent group id');
 };
 
+# Now let's unmock publish_amqp so we can test it...
+$plugin_mock->unmock('publish_amqp');
+%published = ();
+# ...but we'll mock the thing it calls.
+my $publisher_mock = Test::MockModule->new('Mojo::RabbitMQ::Client::Publisher');
+$publisher_mock->mock(
+    publish_p => sub {
+        # copied from upstream git master as of 2019-07-24
+        my $self    = shift;
+        my $body    = shift;
+        my $headers = {};
+        my %args    = ();
+
+        if (ref($_[0]) eq 'HASH') {
+            $headers = shift;
+        }
+        if (@_) {
+            %args = (@_);
+        }
+        # end copying
+        $published{body}    = $body;
+        $published{headers} = $headers;
+        $published{args}    = \%args;
+        # we need to return a Promise or stuff breaks
+        my $client_promise = Mojo::Promise->new();
+        return $client_promise;
+    });
+
+# we need an instance of the plugin now. I can't find a documented
+# way to access the one that's already loaded...
+my $amqp = OpenQA::WebAPI::Plugin::AMQP->new;
+$amqp->register($app);
+
+subtest 'amqp_publish call without headers' => sub {
+    $amqp->publish_amqp('some.topic', 'some message');
+    is($published{body}, 'some message', "message body correctly passed");
+    is_deeply($published{headers}, {}, "headers is empty hashref");
+    is_deeply($published{args}->{routing_key}, 'some.topic', "topic appears as routing key");
+};
+
+subtest 'amqp_publish call with headers' => sub {
+    %published = ();
+    $amqp->publish_amqp('some.topic', 'some message', {'someheader' => 'something'});
+    is($published{body}, 'some message', "message body correctly passed");
+    is_deeply($published{headers}, {'someheader' => 'something'}, "headers is expected hashref");
+    is_deeply($published{args}->{routing_key}, 'some.topic', "topic appears as routing key");
+};
+
+subtest 'amqp_publish call with incorrect headers' => sub {
+    throws_ok(
+        sub {
+            $amqp->publish_amqp('some.topic', 'some message', 'some headers');
+        },
+        qr/publish_amqp headers must be a hashref!/,
+        'dies on bad headers'
+    );
+};
+
 done_testing();
-- 
2.22.0