whatsapp_notifier 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ require "spec_helper"
2
+
3
+ # WhatsAppNotifier.session_ready? — the one call hosts make before sending, so
4
+ # they stop each writing their own "authenticated == true, rescue to false".
5
+ RSpec.describe "session readiness" do
6
+ def configure(adapter)
7
+ WhatsAppNotifier.configure do |config|
8
+ config.provider = :web_automation
9
+ config.web_automation_enabled = true
10
+ config.web_adapter = adapter
11
+ config.logger = Logger.new(nil)
12
+ config.warn_on_risky_provider = false
13
+ end
14
+ end
15
+
16
+ def adapter_returning(status)
17
+ adapter = double(send_message: { success: true, session: {} }, fetch_qr_code: "qr")
18
+ allow(adapter).to receive(:connection_status).and_return(status)
19
+ adapter
20
+ end
21
+
22
+ def adapter_raising(error)
23
+ adapter = double(send_message: { success: true, session: {} }, fetch_qr_code: "qr")
24
+ allow(adapter).to receive(:connection_status).and_raise(error)
25
+ adapter
26
+ end
27
+
28
+ it "is true only when the service reports an authenticated session" do
29
+ configure(adapter_returning(state: "AUTHENTICATED", authenticated: true))
30
+
31
+ expect(WhatsAppNotifier.session_ready?(user_id: 7)).to be(true)
32
+ end
33
+
34
+ it "is false when the session is not authenticated" do
35
+ configure(adapter_returning(state: "QR_REQUIRED", authenticated: false))
36
+
37
+ expect(WhatsAppNotifier.session_ready?(user_id: 7)).to be(false)
38
+ end
39
+
40
+ # Truthy-but-not-true (a "yes" string from a sloppy adapter) must not read
41
+ # as ready — the check is deliberately exact.
42
+ it "is false for a truthy non-true authenticated value" do
43
+ configure(adapter_returning(authenticated: "yes"))
44
+
45
+ expect(WhatsAppNotifier.session_ready?(user_id: 7)).to be(false)
46
+ end
47
+
48
+ it "is false when the adapter answers with something that is not a hash" do
49
+ configure(adapter_returning(nil))
50
+
51
+ expect(WhatsAppNotifier.session_ready?(user_id: 7)).to be(false)
52
+ end
53
+
54
+ # An unreachable status endpoint means the service is down, so sends can't
55
+ # succeed either — not ready, and logged rather than raised.
56
+ it "is false and logs when the status endpoint is unreachable" do
57
+ logger = double(warn: true)
58
+ configure(adapter_raising(Errno::ECONNREFUSED))
59
+ WhatsAppNotifier.configuration.logger = logger
60
+
61
+ expect(WhatsAppNotifier.session_ready?(user_id: 7)).to be(false)
62
+ expect(logger).to have_received(:warn).with(/session_ready\? failed/)
63
+ end
64
+
65
+ it "survives a nil logger" do
66
+ configure(adapter_raising(Errno::ECONNREFUSED))
67
+ WhatsAppNotifier.configuration.logger = nil
68
+
69
+ expect(WhatsAppNotifier.session_ready?(user_id: 7)).to be(false)
70
+ end
71
+
72
+ # A ConfigurationError is a boot-time mistake in the HOST. Swallowing it
73
+ # would quietly park every campaign behind a "session down" backoff.
74
+ it "does not swallow a configuration error" do
75
+ configure(adapter_returning(authenticated: true))
76
+ WhatsAppNotifier.configuration.web_automation_enabled = false
77
+
78
+ expect { WhatsAppNotifier.session_ready?(user_id: 7) }
79
+ .to raise_error(WhatsAppNotifier::ConfigurationError, /disabled/)
80
+ end
81
+
82
+ it "passes the user id through as status metadata" do
83
+ adapter = adapter_returning(authenticated: true)
84
+ configure(adapter)
85
+
86
+ WhatsAppNotifier.session_ready?(user_id: 42)
87
+
88
+ expect(adapter).to have_received(:connection_status).with(metadata: { user_id: 42 })
89
+ end
90
+
91
+ it "accepts a metadata hash instead of user_id" do
92
+ adapter = adapter_returning(authenticated: true)
93
+ configure(adapter)
94
+
95
+ WhatsAppNotifier.session_ready?(metadata: { user_id: 9, tenant: "x" })
96
+
97
+ expect(adapter).to have_received(:connection_status).with(metadata: { user_id: 9, tenant: "x" })
98
+ end
99
+
100
+ it "asks for the default session when neither is given" do
101
+ adapter = adapter_returning(authenticated: true)
102
+ configure(adapter)
103
+
104
+ WhatsAppNotifier.session_ready?
105
+
106
+ expect(adapter).to have_received(:connection_status).with(metadata: {})
107
+ end
108
+
109
+ it "is not implemented by a bare provider" do
110
+ provider = WhatsAppNotifier::Providers::Base.new(configuration: WhatsAppNotifier.configuration)
111
+
112
+ expect { provider.session_ready? }.to raise_error(NotImplementedError)
113
+ end
114
+ end
@@ -104,6 +104,41 @@ RSpec.describe WhatsAppNotifier::WebAdapter do
104
104
  end.to raise_error(/service request failed/)
105
105
  end
106
106
 
107
+ # The status is what ErrorCode classifies on — re-deriving it from the
108
+ # message text would be a regex round-trip through our own string.
109
+ it "carries the HTTP status on the raised service error" do
110
+ allow(Net::HTTP).to receive(:start)
111
+ .and_return(http_failure(code: "401", body: JSON.generate({ error: "User not authenticated" })))
112
+
113
+ expect { adapter.send_message(payload: { to: "+1", body: "hi" }, session: {}) }
114
+ .to raise_error(WhatsAppNotifier::ServiceError) { |error|
115
+ expect(error.status.to_i).to eq(401)
116
+ # Pre-0.9.0 this was a bare RuntimeError; hosts rescue that class by
117
+ # name around media fetches and status polls.
118
+ expect(error).to be_a(RuntimeError)
119
+ # Wording unchanged from 0.8.x — hosts that fingerprinted it still match.
120
+ expect(error.message).to eq("service request failed (401): User not authenticated")
121
+ }
122
+ end
123
+
124
+ # Forward compatibility: a future service can name the failure itself and
125
+ # the gem stops guessing from text.
126
+ it "normalizes an error code the service supplied" do
127
+ response = http_success(body: { "success" => false, "errorCode" => "NUMBER_NOT_REGISTERED", "error" => "boom" })
128
+ allow(Net::HTTP).to receive(:start).and_return(response)
129
+
130
+ result = adapter.send_message(payload: { to: "+1", body: "hi" }, session: {})
131
+
132
+ expect(result).to include(success: false, error_code: :not_on_whatsapp, error_message: "boom")
133
+ end
134
+
135
+ it "leaves error_code nil when the service does not send one" do
136
+ response = http_success(body: { "success" => false, "error" => "boom" })
137
+ allow(Net::HTTP).to receive(:start).and_return(response)
138
+
139
+ expect(adapter.send_message(payload: { to: "+1", body: "hi" }, session: {})[:error_code]).to be_nil
140
+ end
141
+
107
142
  it "handles empty and invalid response bodies gracefully" do
108
143
  empty_body = instance_double(Net::HTTPOK, body: "", code: "200", is_a?: true)
109
144
  invalid_body = instance_double(Net::HTTPInternalServerError, body: "raw-error", code: "500", is_a?: false)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whatsapp_notifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kshitiz Sinha
@@ -56,6 +56,7 @@ files:
56
56
  - app/controllers/whatsapp_notifier/sessions_controller.rb
57
57
  - bin/whatsapp_notifier
58
58
  - config/routes.rb
59
+ - docs/CONFIGURATION.md
59
60
  - docs/bulk_messaging_policy.md
60
61
  - docs/graphify.md
61
62
  - docs/rails_setup.md
@@ -72,6 +73,7 @@ files:
72
73
  - lib/whatsapp_notifier/configuration.rb
73
74
  - lib/whatsapp_notifier/doctor.rb
74
75
  - lib/whatsapp_notifier/engine.rb
76
+ - lib/whatsapp_notifier/error_code.rb
75
77
  - lib/whatsapp_notifier/errors.rb
76
78
  - lib/whatsapp_notifier/jobs/send_message_job.rb
77
79
  - lib/whatsapp_notifier/notification.rb
@@ -107,6 +109,7 @@ files:
107
109
  - spec/client_spec.rb
108
110
  - spec/configuration_spec.rb
109
111
  - spec/doctor_spec.rb
112
+ - spec/error_code_spec.rb
110
113
  - spec/generators/install_service_generator_spec.rb
111
114
  - spec/jobs/send_message_job_spec.rb
112
115
  - spec/notification_spec.rb
@@ -116,6 +119,7 @@ files:
116
119
  - spec/result_spec.rb
117
120
  - spec/session/qr_service_spec.rb
118
121
  - spec/session/store_spec.rb
122
+ - spec/session_ready_spec.rb
119
123
  - spec/spec_helper.rb
120
124
  - spec/web_adapter_spec.rb
121
125
  - spec/whatsapp_notifier_spec.rb