1# frozen_string_literal: true
2
3require "simplecov"
4SimpleCov.start do
5 add_filter "/test/"
6 enable_coverage :branch
7 command_name ENV.fetch("COVERAGE_NAME", "tests")
8end
9
10require "minitest/autorun"
11require "webmock/minitest"
12
13MMS_PROXY = "https://proxy.test.example.com/"
14BW_MESSAGES_URL = "https://messaging.bandwidth.com/api/v2/users/account/messages"
15
16_saved_argv = ARGV.dup
17
18ARGV[0] = "component"
19ARGV[6] = MMS_PROXY
20
21require_relative "../sgx-bwmsgsv2"
22ARGV.replace(_saved_argv)
23
24def SGXbwmsgsv2.write_to_stream(s)
25 @written ||= []
26 @written << s
27end
28
29def reset_stanzas!
30 SGXbwmsgsv2.instance_variable_set(:@written, [])
31end
32
33def reset_redis!
34 REDIS.reset!
35 REDIS.set("catapult_jid-", "HERE")
36 REDIS.set("catapult_jid-+15550000000", "test@example.com")
37 REDIS.set("catapult_cred-test@example.com", [
38 'account', 'user', 'password', '+15550000000'
39 ])
40end
41
42def written
43 SGXbwmsgsv2.instance_variable_get(:@written)
44end
45
46def invoke_webhook(payload, extra_env: {})
47 with_stubs([
48 [
49 SGXbwmsgsv2,
50 :write,
51 ->(data) { SGXbwmsgsv2.write_to_stream(data) }
52 ]
53 ]) do
54 handler = WebhookHandler.new
55 env = {
56 "REQUEST_URI" => "/",
57 "REQUEST_METHOD" => "POST",
58 "params" => { "_json" => [payload] }
59 }.merge(extra_env)
60 handler.instance_variable_set(:@env, env)
61 def handler.params
62 @env["params"]
63 end
64 EMPromise.resolve(nil).then {
65 handler.response(env)
66 }.sync
67 end
68end
69
70def process_stanza(s)
71 SGXbwmsgsv2.send(:client).receive_data(s)
72 raise $panic if $panic
73end
74
75def xmpp_error_name(error)
76 error.find_first(
77 "child::*[name()!='text']",
78 Blather::StanzaError::STANZA_ERR_NS
79 ).element_name
80end
81
82def xmpp_error_text(error)
83 error.find_first("ns:text", ns: Blather::StanzaError::STANZA_ERR_NS)&.text
84end
85
86
87begin
88 require "pry-byebug"
89
90 module Minitest
91 class Test
92 alias old_capture_exceptions capture_exceptions
93 def capture_exceptions
94 old_capture_exceptions do
95 yield
96 rescue Minitest::Skip => e
97 failures << e
98 end
99 end
100 end
101 end
102rescue LoadError, NameError
103 # Just helpers for dev, no big deal if missing
104 nil
105end
106
107
108$VERBOSE = nil
109
110class FakeRedis
111 def initialize(values={})
112 @values = values
113 end
114
115 def reset!(values={})
116 @values = values
117 end
118
119 def set(key, value, *)
120 @values[key] = value
121 EMPromise.resolve("OK")
122 end
123
124 def setex(key, _expiry, value)
125 set(key, value)
126 end
127
128 def del(*keys)
129 keys.each { |key| @values.delete(key) }
130 end
131
132 def mget(*keys)
133 EMPromise.all(keys.map(&method(:get)))
134 end
135
136 def get(key)
137 EMPromise.resolve(@values[key])
138 end
139
140 def getbit(key, bit)
141 get(key).then { |v| v.to_i.to_s(2)[bit].to_i }
142 end
143
144 def bitfield(key, *ops)
145 get(key).then do |v|
146 bits = v.to_i.to_s(2)
147 ops.each_slice(3).map do |(op, encoding, offset)|
148 raise "unsupported bitfield op" unless op == "GET"
149 raise "unsupported bitfield op" unless encoding == "u1"
150
151 bits[offset].to_i
152 end
153 end
154 end
155
156 def hget(key, field)
157 @values.dig(key, field)
158 end
159
160 def hincrby(key, field, incrby)
161 @values[key] ||= {}
162 @values[key][field] ||= 0
163 @values[key][field] += incrby
164 end
165
166 def sadd(key, member)
167 @values[key] ||= Set.new
168 @values[key] << member
169 end
170
171 def srem(key, member)
172 @values[key].delete(member)
173 end
174
175 def scard(key)
176 @values[key]&.size || 0
177 end
178
179 def expire(_, _); end
180
181 def exists(*keys)
182 EMPromise.resolve(
183 @values.select { |k, _| keys.include? k }.size.to_s
184 )
185 end
186
187 def lindex(key, index)
188 get(key).then { |v| v&.fetch(index) }
189 end
190
191 def lrange(key, sindex, eindex)
192 get(key).then { |v| v ? v[sindex..eindex] : [] }
193 end
194
195 def rpush(key, *values)
196 @values[key] ||= []
197 values.each { |v| @values[key].push(v) }
198 end
199
200 def multi
201 end
202
203 def exec
204 end
205
206 def discard
207 end
208
209 def xadd(stream, id, *args)
210 @values[stream] ||= []
211 entry_id = id == "*" ? "#{Time.now.to_i}-0" : id
212 fields = Hash[*args]
213 @values[stream] << { id: entry_id, fields: fields }
214 EMPromise.resolve(entry_id)
215 end
216
217 def stream_entries(stream)
218 EMPromise.resolve(@values[stream] || [])
219 end
220end
221
222REDIS = FakeRedis.new
223
224module Minitest
225 class Test
226 def with_stubs(stubs, &block)
227 if stubs.empty?
228 block.call
229 else
230 obj, method, value = stubs.first
231 obj.stub(method, value) do
232 with_stubs(stubs[1..], &block)
233 end
234 end
235 end
236
237 def self.em(m)
238 alias_method "raw_#{m}", m
239 define_method(m) do
240 $panic = nil
241 e = nil
242 EM.run do
243 Fiber.new {
244 ARGV[0] = "component"
245 ARGV[6] = MMS_PROXY
246 begin
247 send("raw_#{m}")
248 rescue
249 e = $!
250 ensure
251 EM.stop
252 end
253 }.resume
254 end
255 raise e if e
256 end
257 end
258 end
259end