Test SIP Lua Agent

Introduction

The TestSipLuaAgent is an asynchronous helper for LUA scripts running within the LogicApp. It is used for performing SIP Testing. This agent exists primarily to support the automated regression testing of the N-Squared family of SIP products (the N2SIP framework).

The agent supports the ability to automate testing of:

initiating an outbound SIP call A-Leg. This agent will send an outbound SIP INVITE Request to open an A-Leg and then will allow interaction and B-Leg termination in the same manner as supported for inbound A-Legs.

The TestSipLuaAgent communicates with one or more instances of the TestSipApp which performs the SIP Messaging.

The TestSipLuaAgent communicates with the TestSipApp using the TEST-SIP-… messages.

The TestSipLuaAgent is tied to the test_sip action key.

The N-Squared application with which we exchange the SIP Requests and Responses is the LhoSipApp, although in theory any external SIP User Agent can be tested using this library.

Configuring TestSipLuaAgent

The TestSipLuaAgent is configured within a LogicApp.

    <?xml version="1.0" encoding="utf-8"?>
    <n2svcd>
      ...
      <applications>
        ...
        <application name="Logic" module="LogicApp">
          <include>
            <lib>../apps/logic/lib</lib>
          </include>
          <parameters>
            ...
            <parameter name="default_test_sip_app_name" value="TestSipApp"/>
          </parameters>
          <config>
            <services>
              ...
            </services>
            <agents>
              <agent module="TestSipApp::TestSipLuaAgent" libs="../../n2sip/apps/test_sip/lib"/>
            </agents>
          </config>
        </application>
        ...
      </application>
      ...
    </n2svcd>

Under normal installation, following agent attributes apply:

Attribute Type XML Type Description
module TestSipApp::TestSipLuaAgent Attribute [Required] The module name containing the Test SIP Agent code.
libs ../../n2sip/apps/test_sip/lib Element Location of the module for TestSipLuaAgent.
expect_secs Integer Attribute The number of seconds to wait for an inbound message (or no-message).
This value is set globally and currently cannot be controlled on a per-request basis.
(Default = 10 seconds).

In addition, the TestSipLuaAgent must be configured with the name of the TestSipApp with which it will communicate. This is configured within the parameters of the containing LogicApp.

Attribute Type Description
parameters Array Array of name = value Parameters for this Application instance.
.default_test_sip_app_name String Default name for the TestSipApp to which TestSipLuaAgent will send test instructions and receive test inputs.

The TestSipLuaAgent API

All methods may raise a LUA Error in the case of exception, including:

Complete Example

A LUA Script can access the TestSipLuaAgent test_sip action with code such as the following.

This example sends a SIP INVITE Request, and expects back in sequence:

The test script sends an ACK, and then checks EDRs associated with the call.

local n2svcd = require "n2.n2svcd"
local utils = require "n2.utils"
local match = require "n2.n2svcd.tester.match"
local manage = require "n2.n2svcd.tester.manage"
local edr_file_agent = require "n2.n2svcd.edr_file_agent"
local tsuo = require "n2.n2svcd.tester.test_sip_agent"

local args = ...

-- Start timer for elapsed checking.
local tv = match.elapsed ()

-- Checkpoint stats for "Logic" and "LHO" apps.
local stats_Logic = match.stats ('Logic')
local stats_LHO = match.stats ('LHO')

-- Static for our call.
local endpoints = tsuo.default_endpoints ({ local_rtp_port = 3668 })
local calling_party = '665566'
local called_party = '902112233'

-- Get a SIP outcall context from our helper library.
local context = tsuo.invite_context (endpoints, calling_party, called_party, {
    contact_sip_instance = '"<urn:uuid:27b843f9-d300-49fb-b108-fff03a6369e3>"',
    contact_expires = '3600'
})

-- Enable ACK EDRs.
n2svcd.management_configuration_scalar ('LHO', 'sip_edr_ack', '1')

-- Construct the SDP
local sdp_offer = tsuo.sdp_offer (context, tsuo.SDP_LINPHONE)

-- Construct and Send INVITE Request.
tsuo.invite_send_request (context, sdp_offer, {
    { name = 'P-Charging-Vector', value = '9132432-pogSX-009121' }, 
    { name = 'Other', value = 'Test Header; my=tag' }               -- Logged in EDR and is returned in 602.
})

-- Expect Trying & Ringing.
tsuo.invite_expect_response (context, 100, "Trying", { ['User-Agent'] = "N-Squared LHO", Other = UNDEF, ['P-Charging-Vector'] = '9132432-pogSX-009121' })
tsuo.invite_expect_response (context, 180, "Ringing", { Require = UNDEF })

-- Expect INVITE Response (Status Code 602).
tsuo.invite_expect_response (context, 602, "Unknown code", { ['P-Charging-Vector'] = '9132432-pogSX-009121', Reason = 'SIP ;cause=404' })
tv = match.elapsed ("Initial 602", tv, 0.0)

-- Deliberately pretend we lost the response.  Don't ACK yet.  Wait for the 602 to be repeated.
tsuo.invite_expect_response (context, 602, "Unknown code", { ['P-Charging-Vector'] = '9132432-pogSX-009121' })
tv = match.elapsed ("Retransmitted 602", tv, 0.49)

-- Construct and Send ACK Request suitable for the 300-699 response we received.
tsuo.invite_send_decline_ack (context)

-- Wait for SIP to handle the ACK and write/flush EDRs.
n2svcd.wait (0.5)

-- Disable ACK EDRs.
n2svcd.management_configuration_scalar ('LHO', 'sip_edr_ack', '0')

-- Check statistics changes.
stats_Logic = match.stats ('Logic', stats_Logic, {  ['instance.start'] = 1 })
stats_LHO   = match.stats ('LHO',   stats_LHO,   {  ['instance.sip.in.INVITE'] = 1 })

---------------------------------------------------------------------------
-- Check SIP EDRs (MAIN CALL)
---------------------------------------------------------------------------
local edrs = edr_file_agent.find_fields ('n2scc', 'IN-INVITE-ALEG', { CALL_ID = context.call_id })
if (match.kpath_integer (edrs, 'count', 3)) then error ("Primary Call: " .. edr_file_agent.summary (edrs)) end

if (match.kpath_size (edrs, 'list.DECLINE', 1)) then error ("Missing EDR(s) for DECLINE.") end
match.kpath_string (edrs, 'list.DECLINE.[0].CODE', '602')
match.kpath_string (edrs, 'list.DECLINE.[0].REASON', 'SIP ;cause=404')

if (match.kpath_size (edrs, 'list.IN-INVITE-ALEG', 1)) then error ("Missing EDR(s) for IN-INVITE-ALEG.") end
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].TRANSPORT', 'udp')
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].TO_URI', context.remote_uri_notag)
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].CODE', '602')
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].RCONTACT', 'sip:665566@' .. LOCAL_SIP_IP .. ':5063;transport=udp')
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].LCONTACT', 'sip:902112233@' .. LOCAL_SIP_IP .. ':5061')
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].OTHER', 'Test Header; my=tag')
match.kpath_string (edrs, 'list.IN-INVITE-ALEG.[0].PCV', '9132432-pogSX-009121')

if (match.kpath_size (edrs, 'list.IN-ACK-ALEG', 1)) then error ("Missing EDR(s) for IN-INVITE-ALEG.") end

---------------------------------------------------------------------------
-- Check LHO INSTANCE (MAIN CALL)
---------------------------------------------------------------------------
local instance = manage.resource_row_instance ('LHO', edrs.instance_idx)
if (match.kpath_string (instance, 'called_party', called_party)) then error ("LHO instance wrong called party.") end
if (match.kpath_string (instance, 'calling_party', calling_party)) then error ("LHO instance wrong calling party.") end
if (match.kpath_string (instance, 'completed', 'YES/Timer')) then error ("LHO instance wrong completed state.") end
if (match.kpath_string (instance, 'active_timeout_reasons', 'I')) then error ("LHO instance wrong active timeout reasons.") end
if (match.kpath_exists (instance, 'warnings.[0]', false)) then error ("LHO instance unexpected warnings.") end

---------------------------------------------------------------------------
-- Check Logic INSTANCE (MAIN CALL)
---------------------------------------------------------------------------
if (not instance.scc_context) then error ("LHO instance missing main call context.") end

local instance = manage.resource_row_instance ('Logic', instance.scc_context)
match.kpath_string (instance, 'script_key', 'decline_602')
if (match.kpath_exists (instance, 'warnings.[0]', false)) then error ("Logic instance unexpected warnings.") end

return

Setup & Context

Before performing a test transaction/dialog, it is necessary to define the endpoints for the transaction, and to construct a context defining the Call-ID, counters, and state variables.

There is also a registration method for the scenario where the test script is waiting to receive an inbound SIP transaction/dialog which will then be tested.

Refer to the Setup & Context methods.

OPTIONS (Outbound)

The test library supports the ability to send an outbound SIP OPTIONS Request and receive a SIP Response.

This is a non-INVITE Request, outside of any dialog.

Refer to the OPTIONS (Outbound) methods.

REGISTER (Outbound)

The test library supports the ability to send an outbound SIP REGISTER Request and receive a SIP Response.

This is a non-INVITE Request, outside of any dialog.

Refer to the REGISTER (Outbound) methods.

INVITE (Outbound)

The test library supports the ability to send an outbound SIP INVITE Request and receive subsequent SIP Responses.

Refer to the INVITE (Outbound) methods.

Within this INVITE (Outbound) dialog, the following methods are also applicable.

INVITE (Inbound)

The test library supports the ability to receive an inbound SIP INVITE Request and send subsequent SIP Responses.

Refer to the INVITE (Inbound) methods.

Within this INVITE (Inbound) dialog, the following methods are also applicable.