test_helper.rb

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