diff --git a/spec/ami_connection_spec.cr b/spec/ami_connection_spec.cr index 7a6c683..a6df0ad 100644 --- a/spec/ami_connection_spec.cr +++ b/spec/ami_connection_spec.cr @@ -8,7 +8,7 @@ describe Asterisk::AMI do unless Asterisk::Server.running? Asterisk::Server.start # let Asterisk boot - sleep 3.seconds + sleep 5.seconds end end @@ -33,7 +33,6 @@ describe Asterisk::AMI do ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" ami.login ami.connected?.should be_true - sleep 0.01 ami.logoff ami.connected?.should be_false end @@ -42,33 +41,25 @@ describe Asterisk::AMI do ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" ami.login ami.connected?.should be_true - channel = Channel(Exception? | Nil).new - spawn do - sleep 2.seconds - Asterisk::Server.kill - channel.send(nil) - end - channel.receive - sleep 0.5.seconds + sleep 2.seconds + Asterisk::Server.kill Asterisk::Server.running?.should be_false ami.connected?.should be_false ami.logoff end - # TODO: rework this test - # it "should not let to login if asterisk is not yet fully booted" do - # Asterisk::Server.kill - # Asterisk::Server.running?.should be_false - # Asterisk::Server.start - # sleep 0.2 - # loop do - # sleep 0.1 - # break if Asterisk::Server.running? - # end - # ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - # expect_raises(Asterisk::AMI::NotBootedError) do - # ami.login - # end - # end + it "should not let to login if asterisk is not yet fully booted" do + Asterisk::Server.kill + Asterisk::Server.running?.should be_false + Asterisk::Server.start + # nmap dependency + while Asterisk::Server.port_closed? + sleep 0.01 + end + ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" + expect_raises(Asterisk::AMI::NotBootedError) do + ami.login + end + end end end diff --git a/spec/ami_loadtest_spec.cr b/spec/ami_loadtest_spec.cr index 72ff2a6..da2f9d2 100644 --- a/spec/ami_loadtest_spec.cr +++ b/spec/ami_loadtest_spec.cr @@ -1,66 +1,78 @@ require "./spec_helper" - describe Asterisk::AMI do - Spec.before_each do - # less noicy, please - # Asterisk.logger.level = Logger::ERROR - # Ensure that asterisk is up and running for each test - unless Asterisk::Server.running? - Asterisk::Server.start - # let Asterisk boot - sleep 3.seconds - end - end - it "should correctly respond to all the invoked events within heavy loaded Asterisk" do + # # less noicy, please + # Asterisk.logger.level = Logger::ERROR fibers_count = 10 + test_loops_count = 500 fibers = Channel(Nil).new(fibers_count) fibers_count.times do |spawn_no| - spawn_no_pretty = "0000#{spawn_no}"[-4,4] + spawn_no_pretty = "0000#{spawn_no}"[-4, 4] foobar = "foobar_#{spawn_no_pretty}" spawn do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - # ami = Asterisk::AMI.new port: "15038", username: "88a26ad6ee3cff2cbb2fe3f49f7532e6", secret: "e47108082ec2bb74d83f7b3067b19396" - ami.login + with_ami(username: "asterisk.cr", secret: "asterisk.cr") do |ami| + test_loops_count.times do |test_no| + test_no_pretty = "0000#{test_no}"[-4, 4] - 50.times do |test_no| - test_no_pretty = "0000#{test_no}"[-4,4] + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Ping", "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response["ping"].should match /Pong/i - response = ami.send_action({"action" => "Ping"}) - response["ping"].should match /Pong/i + actionid = Random::Secure.hex(8) + foobar_value = %(#{spawn_no_pretty}-#{test_no_pretty}-#{Random::Secure.hex(8)}) + response = ami.send_action({"action" => "Setvar", "Variable" => foobar, "Value" => foobar_value, "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response.message.should match /Variable Set/i - foobar_value = %(#{spawn_no_pretty}-#{test_no_pretty}-#{Random::Secure.hex(8)}) - response = ami.send_action({"action" => "Setvar", "Variable" => foobar, "Value" => foobar_value}) - response.message.should match /Variable Set/i + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "SIPpeers", "actionid" => actionid}, expects_answer_before: 0.5) + response.success?.should be_true + response.actionid.should eq(actionid) + response["eventlist"].should match /^start$/i + response.message.should match /list will follow/i - response = ami.send_action({"action" => "SIPpeers"}, expects_answer_before: 0.5) - response["eventlist"].should match /^start$/i - response.message.should match /list will follow/i + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Getvar", "Variable" => foobar, "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response.value.should eq(foobar_value) - response = ami.send_action({"action" => "Getvar", "Variable" => foobar}) - response.value.should eq(foobar_value) + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "ListCommands", "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response.data.has_key?("blindtransfer").should be_truthy + response.success?.should be_true - response = ami.send_action({"action" => "ListCommands"}) - response.data.has_key?("blindtransfer").should be_truthy + # actionid = Random::Secure.hex(8) + # response = ami.send_action({"action" => "Queues", "actionid" => actionid}) + # response.actionid?.should be_falsey + # response["unknown"].should match /No queues/i - # response = ami.send_action({"action" => "Queues"}) - # response["unknown"].should match /No queues/i + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Command", "command" => "agi show commands", "actionid" => actionid}) + response.actionid.should eq(actionid) + response.output.as(Array(String)).join("\n").should match /database del/im - response = ami.send_action({"action" => "Command", "command" => "agi show commands"}) - response.output.as(Array(String)).join("\n").should match /database del/im + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Command", "command" => "sip show peers", "actionid" => actionid}) + response.actionid.should eq(actionid) + response.output.as(Array(String)).join("\n").should match /test-account-905/im - response = ami.send_action({"action" => "Command", "command" => "sip show peers"}) - response.output.as(Array(String)).join("\n").should match /test-account-905/im + # short random pause after loop to randomize fibers data + sleep 0.002 + rand(0.05) + end # test loop - # short random pause after loop to randomize fibers data - sleep 0.002 + rand(0.05) - end # test loop - rescue ex - logger.error "AMI loadtest spec: #{ex.class}:#{ex.message} #{ex.backtrace}" - ensure - fibers.send nil - end # spawn + rescue ex + puts "\n\nAMI loadtest spec: #{ex.class}:#{ex.message}\n#{ex.backtrace.pretty_inspect}\n\n" + ensure + fibers.send nil + end # with_ami + end # spawn end fibers.receive diff --git a/spec/ami_spec.cr b/spec/ami_spec.cr index 10a0e99..1706427 100644 --- a/spec/ami_spec.cr +++ b/spec/ami_spec.cr @@ -1,186 +1,127 @@ require "./spec_helper" describe Asterisk::AMI do - Spec.before_each do - # less noicy, please - Asterisk.logger.level = Logger::ERROR - # Ensure that asterisk is up and running for each test - unless Asterisk::Server.running? - Asterisk::Server.start - # let Asterisk boot - sleep 3.seconds - end - end - # Testing basic actions describe "#basic_actions" do it "should respond with 'Pong' to action 'ping'" do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - 10.times do |i| - actionid = "#{i + 1}" - response = ami.send_action({"action" => "Ping", "actionid" => actionid}) - # {"response" => "Success", "actionid" => "3", ping" => "Pong"} - response["actionid"].should eq(actionid) - response["response"].should eq("Success") - response["ping"].should eq("Pong") + with_ami do |ami| + 10.times do |i| + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Ping", "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response["ping"].should match /Pong/i + end end - ami.logoff end - it "should successfully set asterisk global variable and then read it" do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - foobar_value = Random::Secure.hex(8) - response = ami.send_action({"action" => "Setvar", "Variable" => "foobar", "Value" => foobar_value}) - # {"response" => "Success", "message" => "Variable Set"} - response["response"].should eq("Success") - response["message"].should match /set$/i - - response = ami.send_action({"action" => "Getvar", "Variable" => "foobar"}) - ami.logoff - # {"response" => "Success", "variable" => "foobar", "value" => "39c56eb0b580ad7f"} - response["response"].should eq("Success") - response["variable"].should eq("foobar") - response["value"].should eq(foobar_value) + it "should successfully set and read asterisk dialplan global variable" do + with_ami do |ami| + # set + actionid = Random::Secure.hex(8) + foobar_value = Random::Secure.hex(8) + response = ami.send_action({"action" => "Setvar", "Variable" => "foobar", "Value" => foobar_value, "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response["message"].should match /Variable Set$/i + + # read + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Getvar", "Variable" => "foobar", "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response["variable"].should eq("foobar") + response.value.should eq(foobar_value) + end end end # Testing actions that receive multiline event or multiple events as a response describe "#multiline_events" do - # TODO it "should process action that contain multiple lines in response (ListCommands)" do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - response = ami.send_action({"action" => "ListCommands"}) - ami.logoff - # {"response" => "Success", "actionid" => "58a480df-32a1-4378-8f3d-03327b78465f", - # "waitevent" => "Wait for an event to occur. (Priv: )", - # "devicestatelist" => "List the current known device states. (Priv: call,reporting,all)", "..." => "..."} - response.has_key?("waitevent").should be_true + with_ami do |ami| + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "ListCommands", "actionid" => actionid}) + response.actionid.should eq(actionid) + response["waitevent"]?.should be_truthy + end end it "should process complex action that include multiple events in response (Queues)" do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - response = ami.send_action({"action" => "Queues"}) - ami.logoff - # D, [2019-07-21 05:12:41 +00:00 #7416] DEBUG -- : Received Asterisk manager event: - # D, [2019-07-21 05:12:41 +00:00 #7416] DEBUG -- : Processing line: No queues. - response["unknown"].should match /No queues/i + with_ami do |ami| + response = ami.send_action({"action" => "Queues"}) + # D, [2019-07-21 05:12:41 +00:00 #7416] DEBUG -- : Received Asterisk manager event: + # D, [2019-07-21 05:12:41 +00:00 #7416] DEBUG -- : Processing line: No queues. + response["unknown"].should match /No queues/i + end end - # it "should process complex action that include multiple events in response (SIPpeers)" do - # Asterisk.logger.level = Logger::DEBUG - # ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - # ami.login - # response = ami.send_action({"action" => "SIPpeers"}) - # - # ami.logoff - # # INFO -- : {"response" => "Success", "actionid" => "01b9e8bf-1b6a-4c8b-8f57-8880f1d895db", "eventlist" => "start", - # # res.not_nil!.first["objectname"].should match /^test-account-\d{3}/ - # true.should be_true - # end - - # it "should process complex action that include multiple events in response (IAXpeers)" do - # ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - # ami.login - # response = ami.send_action({"action" => "IAXpeers"}) - # ami.logoff - # # INFO -- : {"response" => "Success", "actionid" => "01b9e8bf-1b6a-4c8b-8f57-8880f1d895db", "eventlist" => "start", - # # res.not_nil!.first["objectname"].should match /^test-account-\d{3}/ - # true.should be_true - # end - - - # # TODO - # it "should successfully process complex action (that include multiple events in response)" do - # ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - # ami.login - # ami.send_action({"action" => "Command", "command" => "agi show commands"}) - # # D, [2019-07-21 19:45:44 +00:00 #11671] DEBUG -- : format_event: processing line: Response: Follows - # # D, [2019-07-21 19:45:44 +00:00 #11671] DEBUG -- : format_event: processing line: Privilege: Command - # # D, [2019-07-21 19:45:44 +00:00 #11671] DEBUG -- : format_event: processing line: ActionID: 37df9dee-2593-4ab3-a015-ef16c8df8e3b - # # D, [2019-07-21 19:45:44 +00:00 #11671] DEBUG -- : format_event: processing line: Dead Command Description - # # No answer Answer channel - # # Yes asyncagi break Interrupts Async AGI - # # No channel status Returns status of the connected channel. - # # ... - # # --END COMMAND-- - # sleep 10.seconds - # ami.connected?.should be_true - # end - - it "should successfully process complex action (that include multiple events in response)" do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - response = ami.send_action({"action" => "Command", "command" => "core show uptime"}) - ami.logoff - # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: Response: Follows - # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: Privilege: Command - # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: ActionID: 75443869-e766-451f-8194-c0e925191030 - # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: System uptime: 23 minutes, 36 seconds - # Last reload: 23 minutes, 36 seconds - # --END COMMAND-- - response["result"].should match /System uptime/m + it "should process complex action that include multiple events in response (SIPpeers)" do + with_ami do |ami| + # # increase verbosity + # Asterisk.logger.level = Logger::DEBUG + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "SIPpeers", "actionid" => actionid}) + # sleep 3.seconds + logger.debug response + # INFO -- : {"response" => "Success", "actionid" => "01b9e8bf-1b6a-4c8b-8f57-8880f1d895db", "eventlist" => "start", + # res.not_nil!.first["objectname"].should match /^test-account-\d{3}/ + response.success?.should be_true + response.actionid.should eq(actionid) + response["eventlist"].should match /start/i + response["message"].should match /follow/i + # Asterisk.logger.level = Logger::ERROR + end end - it "should successfully process complex action (that include multiple events in response)" do - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - response = ami.send_action({"action" => "Command", "command" => "sip show peers"}) - ami.logoff - # D, [2019-07-21 19:58:04 +00:00 #11917] DEBUG -- : format_event: processing line: Response: Follows - # D, [2019-07-21 19:58:04 +00:00 #11917] DEBUG -- : format_event: processing line: Privilege: Command - # D, [2019-07-21 19:58:04 +00:00 #11917] DEBUG -- : format_event: processing line: ActionID: b3ccf896-0ce5-4daf-9576-8a44cbcd6e43 - # D, [2019-07-21 19:58:04 +00:00 #11917] DEBUG -- : format_event: processing line: Name/username Host Dyn Forcerport Comedia ACL Port Status Description - # test-account-900/900 (Unspecified) D Auto (No) No 0 UNKNOWN - # test-account-901/901 (Unspecified) D Auto (No) No 0 UNKNOWN - # ... - # 10 sip peers [Monitored: 0 online, 10 offline Unmonitored: 0 online, 0 offline] - # --END COMMAND-- - response["result"].should match /test-account-903/m + it "should process complex action that include multiple events in response (IAXpeers)" do + with_ami do |ami| + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "IAXpeers", "actionid" => actionid}) + response.success?.should be_true + response.actionid.should eq(actionid) + response["eventlist"].should match /start/i + response["message"].should match /follow/i + end end - it "should correctly process responses on heavy loaded system" do - # Asterisk.logger.level = Logger::ERROR - - # background spam - spam = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - spam.login - # spawn do - # 5_000.times do - # spam.send_action({"action" => "Ping"}) - # end - # end - - ami = Asterisk::AMI.new username: "asterisk.cr", secret: "asterisk.cr" - ami.login - 5_000.times do |i| - foobar_value = Random::Secure.hex(8) - response = ami.send_action({"action" => "Setvar", "Variable" => "foobar", "Value" => foobar_value}) - # {"response" => "Success", "message" => "Variable Set"} - pp response - response["response"].should eq("Success") - response["message"].should match /set$/i + it "should successfully process asterisk command 'agi show commands'" do + with_ami do |ami| + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Command", "command" => "agi show commands", "actionid" => actionid}) + response.actionid.should eq(actionid) + response.output.as(Array(String)).join("\n").should match /database del/im + end + end - actionid = "#{i + 1}" - response = ami.send_action({"action" => "Ping", "actionid" => actionid}) - if response["actionid"]? != actionid - ami.logger.error "incorrect response for ping actionid #{actionid}: #{response.inspect}" - end - response["actionid"].should eq(actionid) - response["response"].should eq("Success") - response["ping"].should eq("Pong") + it "should successfully process asterisk command 'core show uptime'" do + with_ami do |ami| + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Command", "command" => "core show uptime", "actionid" => actionid}) + # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: Response: Follows + # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: Privilege: Command + # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: ActionID: 75443869-e766-451f-8194-c0e925191030 + # D, [2019-07-21 20:04:01 +00:00 #11966] DEBUG -- : format_event: processing line: System uptime: 23 minutes, 36 seconds + # Last reload: 23 minutes, 36 seconds + # --END COMMAND-- + response.output.as(Array(String)).join("\n") =~ /(?|uptime: (.+)|reload: (.+))/i + # 23 hours, 52 minutes, 16 seconds + uptime = $1 + # 12 hours, 52 minutes, 16 seconds + last_reload = $1 + uptime.should be_a(String) + last_reload.should be_a(String) + end + end - response = ami.send_action({"action" => "Getvar", "Variable" => "foobar"}) - # {"response" => "Success", "variable" => "foobar", "value" => "39c56eb0b580ad7f"} - response["response"].should eq("Success") - response["variable"].should eq("foobar") - response["value"].should eq(foobar_value) + it "should successfully process asterisk command 'sip show peers'" do + with_ami do |ami| + actionid = Random::Secure.hex(8) + response = ami.send_action({"action" => "Command", "command" => "sip show peers", "actionid" => actionid}) + response.actionid.should eq(actionid) + response.output.as(Array(String)).join("\n").should match /test-account-905/im end - ami.logoff - spam.logoff end + end end diff --git a/spec/helpers/asterisk.cr b/spec/helpers/asterisk.cr index ebbbf60..8dfe9b7 100644 --- a/spec/helpers/asterisk.cr +++ b/spec/helpers/asterisk.cr @@ -23,6 +23,7 @@ module Asterisk def self.kill shell_command %(ps x | grep "asterisk -vvv" | grep -v grep | awk '{print $1}' | xargs kill -9) + sleep 0.5.seconds end def self.running? : Bool @@ -34,9 +35,13 @@ module Asterisk shell_command %(#{asterisk} -V).split.last end - def self.port_is_open?(port : Int32 | Int64 | String, host = "127.0.0.1") : Bool - result = shell_command %(nmap -p #{port} #{host} | grep #{port} | awk '{print $2}') - result.to_s.strip.chomp == "open" + def self.port_open?(port : Int64 | String = "5038", host = "127.0.0.1") : Bool + # 5038/tcp open unknown + shell_command("nmap -p #{port} #{host} | grep #{port}").split[1] == "open" + end + + def self.port_closed?(port : Int64 | String = "5038", host = "127.0.0.1") : Bool + ! port_open?(port, host) end def self.shell_command(command) diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 75a33da..616baa2 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -17,3 +17,22 @@ def logger Asterisk.logger end logger.level = LOG_LEVEL + +module TestHelpers + def with_ami(username = "asterisk.cr", secret = "asterisk.cr", &block) + unless Asterisk::Server.running? + Asterisk::Server.start + # let Asterisk boot + sleep 3.seconds + end + + ami = Asterisk::AMI.new username: username, secret: secret + ami.login + + yield ami + + ami.logoff + end +end + +extend TestHelpers diff --git a/src/asterisk/ami.cr b/src/asterisk/ami.cr index 1b75a4d..6bb2f54 100644 --- a/src/asterisk/ami.cr +++ b/src/asterisk/ami.cr @@ -39,7 +39,7 @@ module Asterisk @conn = TCPSocket.new(@host, @port) @conn.sync = true @conn.keepalive = false - listen + run response = send_action({"action" => "Login", "username" => @username, "secret" => @secret}) logger.debug "#{self.class}.login response: #{response}" if response.success? @@ -88,16 +88,16 @@ module Asterisk response end - private def listen + private def run raise LoginError.new("Already running!") if running? running! spawn do - logger.debug "#{self.class}.listen: Starting connection listener" + logger.debug "#{self.class}.run: Starting" while running? io_data = read! - # logger.debug "#{self.class}.listen: <<< AMI data received: #{data}" + # logger.debug "#{self.class}.run: <<< AMI data received: #{data}" data = format(io_data) - logger.debug "#{self.class}.listen: Formatted data: #{data.inspect}" + logger.debug "#{self.class}.run: Formatted data: #{data.inspect}" if receiver.waiting? # received message is an AMI unstructured (text) information @@ -105,10 +105,10 @@ module Asterisk # that's response of action containing same actionid as receiver if ! data.is_a?(Event) && data.actionid?.nil? && ! data.response_present? receiver.send data - logger.debug "#{self.class}.listen: <<< sending response: #{data.inspect} to receiver: #{receiver.inspect}" + logger.debug "#{self.class}.run: <<< sending response: #{data.inspect} to receiver: #{receiver.inspect}" elsif data.actionid_present? && data.actionid == receiver.actionid receiver.send data - logger.debug "#{self.class}.listen: <<< sending response: #{data.inspect} to receiver: #{receiver.inspect}" + logger.debug "#{self.class}.run: <<< sending response: #{data.inspect} to receiver: #{receiver.inspect}" end end @@ -130,7 +130,7 @@ module Asterisk # "Shutdown" event logoff! if data.to_h == {"unknown" => ""} end - logger.debug "#{self.class}.listen: Connection gone, login again!" + logger.debug "#{self.class}.run: Connection gone, login again!" end end @@ -159,7 +159,12 @@ module Asterisk rescue IO::Timeout raise ConnectionError.new("TCPSocket timeout error") rescue ex - raise ex + # Errno Bad file descriptor could come after connection get closed + if running? + raise ex + else + "" + end end # `format` process each line of given multi-line string (array), splitting diff --git a/src/asterisk/ami/response.cr b/src/asterisk/ami/response.cr index f1d5e69..4ea8b46 100644 --- a/src/asterisk/ami/response.cr +++ b/src/asterisk/ami/response.cr @@ -30,8 +30,8 @@ module Asterisk def_property value, String def_property output, Array(String) - def success? - response?.to_s.match /Success/i + def success? : Bool + response?.to_s == "Success" end def initialize(data : AMIData? = nil)