//go:build integration // +build integration /* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package agents import ( "net" "path" "testing" "time" "github.com/cgrates/birpc" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" "github.com/cgrates/sipingo" ) var ( saonfigDIR string saCfgPath string saCfg *config.CGRConfig saRPC *birpc.Client saConn net.Conn sTestsSIP = []func(t *testing.T){ testSAitInitCfg, testSAitResetDataDb, testSAitResetStorDb, testSAitStartEngine, testSAitApierRpcConn, testSAitTPFromFolder, testSAitSIPRegister, testSAitSIPInvite, testSAitStopCgrEngine, } ) // Test start here func TestSAit(t *testing.T) { // engine.KillEngine(0) switch *utils.DBType { case utils.MetaInternal: saonfigDIR = "sipagent_internal" case utils.MetaMySQL: saonfigDIR = "sipagent_mysql" case utils.MetaMongo: saonfigDIR = "sipagent_mongo" case utils.MetaPostgres: t.SkipNow() default: t.Fatal("Unknown Database type") } for _, stest := range sTestsSIP { t.Run(saonfigDIR, stest) } } func testSAitInitCfg(t *testing.T) { saCfgPath = path.Join(*utils.DataDir, "conf", "samples", saonfigDIR) // Init config first var err error saCfg, err = config.NewCGRConfigFromPath(saCfgPath) if err != nil { t.Error(err) } if isDispatcherActive { saCfg.ListenCfg().RPCJSONListen = ":6012" } } // Remove data in both rating and accounting db func testSAitResetDataDb(t *testing.T) { if err := engine.InitDataDB(saCfg); err != nil { t.Fatal(err) } } // Wipe out the cdr database func testSAitResetStorDb(t *testing.T) { if err := engine.InitStorDb(saCfg); err != nil { t.Fatal(err) } } // Start CGR Engine func testSAitStartEngine(t *testing.T) { if _, err := engine.StartEngine(saCfgPath, *utils.WaitRater); err != nil { t.Fatal(err) } } // Connect rpc client to rater func testSAitApierRpcConn(t *testing.T) { var err error saRPC, err = newRPCClient(saCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed if err != nil { t.Fatal(err) } if saConn, err = net.Dial(utils.TCP, "127.0.0.1:5060"); err != nil { t.Fatal(err) } } // Load the tariff plan, creating accounts and their balances func testSAitTPFromFolder(t *testing.T) { attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*utils.DataDir, "tariffplans", "tut_sip_redirect")} var loadInst utils.LoadInstance if err := saRPC.Call(context.Background(), utils.APIerSv2LoadTariffPlanFromFolder, attrs, &loadInst); err != nil { t.Error(err) } time.Sleep(time.Duration(*utils.WaitRater) * time.Millisecond) // Give time for scheduler to execute topups } func testSAitStopCgrEngine(t *testing.T) { if err := engine.KillEngine(100); err != nil { t.Error(err) } } func testSAitSIPRegister(t *testing.T) { registerMessage := "REGISTER sip:192.168.58.203 SIP/2.0\r\nCall-ID: d72a4ed6feb4167b5adb208525879db5@0:0:0:0:0:0:0:0\r\nCSeq: 1 REGISTER\r\nFrom: \"1002\" ;tag=d28739b9\r\nTo: \"1002\" \r\nVia: SIP/2.0/UDP 192.168.58.201:5060;branch=z9hG4bK-323131-311ce8716a7bf1f6094859ae516a44eb\r\nMax-Forwards: 70\r\nUser-Agent: Jitsi2.11.20200408Linux\r\nExpires: 600\r\nContact: \"1002\" ;expires=600\r\nContent-Length: 0\r\n" if saConn == nil { t.Fatal("connection not initialized") } var err error if _, err = saConn.Write([]byte(registerMessage)); err != nil { t.Fatal(err) } buffer := make([]byte, bufferSize) if _, err = saConn.Read(buffer); err != nil { t.Fatal(err) } var received sipingo.Message if received, err = sipingo.NewMessage(string(buffer)); err != nil { t.Fatal(err) } if expected := "SIP/2.0 405 Method Not Allowed"; received["Request"] != expected { t.Errorf("Expected %q, received: %q", expected, received["Request"]) } } func testSAitSIPInvite(t *testing.T) { inviteMessage := "INVITE sip:1002@192.168.58.203 SIP/2.0\r\nCall-ID: 4d4d84b0cc83fc90aca41e295cd8ff43@0:0:0:0:0:0:0:0\r\nCSeq: 2 INVITE\r\nFrom: \"1001\" ;tag=99f35805\r\nTo: \r\nMax-Forwards: 70\r\nContact: \"1001\" \r\nUser-Agent: Jitsi2.11.20200408Linux\r\nContent-Type: application/sdp\r\nVia: SIP/2.0/UDP 192.168.58.201:5060;branch=z9hG4bK-393139-939e89686023b86822cb942ede452b62\r\nProxy-Authorization: Digest username=\"1001\",realm=\"192.168.58.203\",nonce=\"XruO2167ja8uRODnSv8aXqv+/hqPJiXh\",uri=\"sip:1002@192.168.58.203\",response=\"5b814c709d1541d72ea778599c2e48a4\"\r\nContent-Length: 897\r\n\r\nv=0\r\no=1001-jitsi.org 0 0 IN IP4 192.168.58.201\r\ns=-\r\nc=IN IP4 192.168.58.201\r\nt=0 0\r\nm=audio 5000 RTP/AVP 96 97 98 9 100 102 0 8 103 3 104 101\r\na=rtpmap:96 opus/48000/2\r\na=fmtp:96 usedtx=1\r\na=ptime:20\r\na=rtpmap:97 SILK/24000\r\na=rtpmap:98 SILK/16000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:100 speex/32000\r\na=rtpmap:102 speex/16000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:103 iLBC/8000\r\na=rtpmap:3 GSM/8000\r\na=rtpmap:104 speex/8000\r\na=rtpmap:101 telephone-event/8000\r\na=extmap:1 urn:ietf:params:rtp-hdrext:csrc-audio-level\r\na=extmap:2 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=rtcp-xr:voip-metrics\r\nm=video 5002 RTP/AVP 105 99\r\na=recvonly\r\na=rtpmap:105 h264/90000\r\na=fmtp:105 profile-level-id=42E01f;packetization-mode=1\r\na=imageattr:105 send * recv [x=[1:1920],y=[1:1080]]\r\na=rtpmap:\r\n" ack := "ACK sip:1001@192.168.56.203:6060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.56.203;rport;branch=z9hG4bKQeB89BamX86UD\r\nMax-Forwards: 69\r\nFrom: \"1001\" ;tag=99f35805\r\nTo: \r\nCall-ID: 4d4d84b0cc83fc90aca41e295cd8ff43@0:0:0:0:0:0:0:0\r\nCSeq: 21984733 ACK\r\nContent-Length: 0\r\n" if saConn == nil { t.Fatal("connection not initialized") } var err error if _, err = saConn.Write([]byte(inviteMessage)); err != nil { t.Fatal(err) } buffer := make([]byte, bufferSize) if _, err = saConn.Read(buffer); err != nil { t.Fatal(err) } var received sipingo.Message if received, err = sipingo.NewMessage(string(buffer)); err != nil { t.Fatal(err) } if expected := "SIP/2.0 302 Moved Temporarily"; received["Request"] != expected { t.Errorf("Expected %q, received: %q", expected, received["Request"]) } if expected := "\"1002\" ;q=0.7; expires=3600;cgr_cost=0.3;cgr_maxusage=30000000000,\"1002\" ;q=0.2;cgr_cost=0.6;cgr_maxusage=30000000000,\"1002\" ;q=0.1;cgr_cost=0.01;cgr_maxusage=30000000000"; received["Contact"] != expected { t.Errorf("Expected %q, received: %q", expected, received["Contact"]) } time.Sleep(time.Second) buffer = make([]byte, bufferSize) if _, err = saConn.Read(buffer); err != nil { t.Fatal(err) } if received, err = sipingo.NewMessage(string(buffer)); err != nil { t.Fatal(err) } if expected := "SIP/2.0 302 Moved Temporarily"; received["Request"] != expected { t.Errorf("Expected %q, received: %q", expected, received["Request"]) } if expected := "\"1002\" ;q=0.7; expires=3600;cgr_cost=0.3;cgr_maxusage=30000000000,\"1002\" ;q=0.2;cgr_cost=0.6;cgr_maxusage=30000000000,\"1002\" ;q=0.1;cgr_cost=0.01;cgr_maxusage=30000000000"; received["Contact"] != expected { t.Errorf("Expected %q, received: %q", expected, received["Contact"]) } if expected := ""; received["P-Charge-Info"] != expected { t.Errorf("Expected %q, received: %q", expected, received["P-Charge-Info"]) } if _, err = saConn.Write([]byte(ack)); err != nil { t.Fatal(err) } buffer = make([]byte, bufferSize) saConn.SetDeadline(time.Now().Add(time.Second)) if _, err = saConn.Read(buffer); err == nil { t.Error("Expected error received nil") } else if nerr, ok := err.(net.Error); !ok { t.Errorf("Expected net.Error received:%v", err) } else if !nerr.Timeout() { t.Errorf("Expected a timeout error received:%v", err) } }