Toll Free Leads to Infinity

Christopher Vollick created

If rate is zero, then minutes remaining becomes infinite.
That's hard to express in json...

So instead I just leave the limits out, which downstream can interpret
as "unlimited".

Change summary

lib/call_attempt.rb | 46 +++++++++++++++++++++++++++++++++++++++++++++-
test/test_web.rb    | 22 ++++++++++++++++++++++
2 files changed, 67 insertions(+), 1 deletion(-)

Detailed changes

lib/call_attempt.rb 🔗

@@ -20,7 +20,7 @@ class CallAttempt
 	end
 
 	def self.limits(customer, usage, credit, rate:, **)
-		return {} unless customer && usage && rate
+		return {} unless customer && usage && rate && rate.positive?
 
 		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
 		{
@@ -70,6 +70,50 @@ class CallAttempt
 		as_json.to_json(*args)
 	end
 
+	class TollFree
+		CallAttempt.register do |rate:, customer:, **kwargs|
+			if rate&.zero?
+				new(
+					**kwargs
+						.merge(customer_id: customer.customer_id)
+						.slice(*value_semantics.attributes.map(&:name))
+				)
+			end
+		end
+
+		value_semantics do
+			customer_id String
+			from String
+			to(/\A\+\d+\Z/)
+			call_id String
+			direction Either(:inbound, :outbound)
+		end
+
+		def to_render
+			["#{direction}/connect", { locals: to_h }]
+		end
+
+		def to_s
+			"TollFree"
+		end
+
+		def create_call(fwd, *args, &block)
+			fwd.create_call(*args, &block)
+		end
+
+		def as_json(*)
+			{
+				from: from,
+				to: to,
+				customer_id: customer_id
+			}.compact
+		end
+
+		def to_json(*args)
+			as_json.to_json(*args)
+		end
+	end
+
 	class Expired
 		CallAttempt.register do |customer:, direction:, **|
 			new(direction: direction) if customer.plan_name && !customer.active?

test/test_web.rb 🔗

@@ -112,6 +112,7 @@ class WebTest < Minitest::Test
 				["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }],
 				["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }],
 				["test_usd", "+14445556666", :inbound] => [{ "rate" => 0.01 }],
+				["test_usd", "+18001234567", :outbound] => [{ "rate" => 0.00 }],
 				["customerid_limit"] => FakeDB::MultiResult.new(
 					[{ "a" => 1000 }],
 					[{ "settled_amount" => 15 }]
@@ -337,6 +338,27 @@ class WebTest < Minitest::Test
 	end
 	em :test_outbound_atlimit_digits
 
+	def test_outbound_toll_free
+		post(
+			"/outbound/calls",
+			{
+				from: "ccustomerid",
+				to: "+18001234567",
+				callId: "acall"
+			}.to_json,
+			{ "CONTENT_TYPE" => "application/json" }
+		)
+
+		assert last_response.ok?
+		assert_equal(
+			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
+			"<Transfer transferCallerId=\"+15551234567\">" \
+			"<PhoneNumber>+18001234567</PhoneNumber></Transfer></Response>",
+			last_response.body
+		)
+	end
+	em :test_outbound_toll_free
+
 	def test_inbound
 		CustomerFwd::BANDWIDTH_VOICE.expect(
 			:create_call,