/* 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 config import ( "reflect" "testing" "time" "github.com/cgrates/cgrates/utils" "github.com/cgrates/rpcclient" ) func TestHttpAgentCfgsloadFromJsonCfgCase1(t *testing.T) { cfgJSON := &[]*HttpAgentJsonCfg{ { ID: utils.StringPointer("RandomID"), URL: utils.StringPointer("/randomURL"), SessionSConns: &[]string{"*internal"}, ReplyPayload: utils.StringPointer(utils.MetaXml), RequestPayload: utils.StringPointer(utils.MetaUrl), RequestProcessors: &[]*ReqProcessorJsnCfg{ { ID: utils.StringPointer("OutboundAUTHDryRun"), Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{utils.MetaDryRun}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("Allow"), Path: utils.StringPointer("response.Allow"), Type: utils.StringPointer(utils.MetaConstant), Value: utils.StringPointer("1"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, }, }, }, } expected := HTTPAgentCfgs{ { ID: "RandomID", URL: "/randomURL", SessionSConns: []string{"*internal:*sessions"}, RequestPayload: "*url", ReplyPayload: "*xml", RequestProcessors: []*RequestProcessor{{ ID: "OutboundAUTHDryRun", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{utils.MetaDryRun: {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{{ Tag: "Allow", Path: "response.Allow", Type: "*constant", Value: NewRSRParsersMustCompile("1", utils.InfieldSep), Mandatory: true, Layout: time.RFC3339, }}, }}, }, } expected[0].RequestProcessors[0].ReplyFields[0].ComputePath() jsnCfg := NewDefaultCGRConfig() if err := jsnCfg.httpAgentCfg.loadFromJSONCfg(cfgJSON, jsnCfg.generalCfg.RSRSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(&expected, &jsnCfg.httpAgentCfg) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(jsnCfg.httpAgentCfg)) } } func TestHttpAgentCfgsloadFromJsonCfgCase2(t *testing.T) { cfgJSON := &[]*HttpAgentJsonCfg{ { ID: utils.StringPointer("conecto1"), URL: utils.StringPointer("/conecto"), SessionSConns: &[]string{utils.MetaLocalHost}, RequestPayload: utils.StringPointer(utils.MetaUrl), ReplyPayload: utils.StringPointer(utils.MetaXml), RequestProcessors: &[]*ReqProcessorJsnCfg{ { ID: utils.StringPointer("OutboundAUTHDryRun"), Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{utils.MetaDryRun}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("Allow"), Path: utils.StringPointer("response.Allow"), Type: utils.StringPointer(utils.MetaConstant), Value: utils.StringPointer("1"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, }, { ID: utils.StringPointer("mtcall_cdr"), Filters: &[]string{"*string:*req.request_type:MTCALL_CDR"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{utils.MetaCDRs}, Request_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("RequestType"), Path: utils.StringPointer("RequestType"), Type: utils.StringPointer(utils.MetaConstant), Value: utils.StringPointer("*pseudoprepaid"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, Reply_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("CDR_ID"), Path: utils.StringPointer("CDR_RESPONSE.CDR_ID"), Type: utils.StringPointer(utils.MetaComposed), Value: utils.StringPointer("~*req.CDR_ID"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, }}, }, { ID: utils.StringPointer("conecto_xml"), URL: utils.StringPointer("/conecto_xml"), SessionSConns: &[]string{utils.MetaLocalHost}, RequestPayload: utils.StringPointer("*xml"), ReplyPayload: utils.StringPointer("*xml"), RequestProcessors: &[]*ReqProcessorJsnCfg{ { ID: utils.StringPointer("cdr_from_xml"), Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{utils.MetaCDRs}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{}, }, }, }, } expected := HTTPAgentCfgs{ &HTTPAgentCfg{ ID: "conecto1", URL: "/conecto", SessionSConns: []string{utils.MetaLocalHost}, RequestPayload: utils.MetaUrl, ReplyPayload: utils.MetaXml, RequestProcessors: []*RequestProcessor{ { ID: "OutboundAUTHDryRun", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{utils.MetaDryRun: {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{{ Tag: "Allow", Path: "response.Allow", Type: utils.MetaConstant, Value: NewRSRParsersMustCompile("1", utils.InfieldSep), Mandatory: true, Layout: time.RFC3339, }}, }, { ID: "mtcall_cdr", Filters: []string{"*string:*req.request_type:MTCALL_CDR"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{utils.MetaCDRs: {}}, RequestFields: []*FCTemplate{{ Tag: "RequestType", Path: "RequestType", Type: utils.MetaConstant, Value: NewRSRParsersMustCompile("*pseudoprepaid", utils.InfieldSep), Mandatory: true, Layout: time.RFC3339, }}, ReplyFields: []*FCTemplate{{ Tag: "CDR_ID", Path: "CDR_RESPONSE.CDR_ID", Type: utils.MetaComposed, Value: NewRSRParsersMustCompile("~*req.CDR_ID", utils.InfieldSep), Mandatory: true, Layout: time.RFC3339, }}, }}, }, &HTTPAgentCfg{ ID: "conecto_xml", URL: "/conecto_xml", SessionSConns: []string{utils.MetaLocalHost}, RequestPayload: utils.MetaXml, ReplyPayload: utils.MetaXml, RequestProcessors: []*RequestProcessor{{ ID: "cdr_from_xml", Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{utils.MetaCDRs: {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{}, }}, }} expected[0].RequestProcessors[0].ReplyFields[0].ComputePath() expected[0].RequestProcessors[1].ReplyFields[0].ComputePath() expected[0].RequestProcessors[1].RequestFields[0].ComputePath() cfgJsn := NewDefaultCGRConfig() if err := cfgJsn.httpAgentCfg.loadFromJSONCfg(cfgJSON, cfgJsn.generalCfg.RSRSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(expected, cfgJsn.httpAgentCfg) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(cfgJsn.httpAgentCfg)) } } func TestHttpAgentCfgloadFromJsonCfgCase3(t *testing.T) { jsnhttpCfg := &HttpAgentJsonCfg{ ID: utils.StringPointer("conecto1"), URL: utils.StringPointer("/conecto"), SessionSConns: &[]string{utils.MetaLocalHost}, RequestPayload: utils.StringPointer("*url"), ReplyPayload: utils.StringPointer("*xml"), RequestProcessors: &[]*ReqProcessorJsnCfg{ { ID: utils.StringPointer("OutboundAUTHDryRun"), Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{"*dryrun"}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{}, }, }, } expected := HTTPAgentCfg{ ID: "conecto1", URL: "/conecto", SessionSConns: []string{utils.MetaLocalHost}, RequestPayload: "*url", ReplyPayload: "*xml", RequestProcessors: []*RequestProcessor{{ ID: "OutboundAUTHDryRun", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{"*dryrun": {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{}, }}, } var httpcfg HTTPAgentCfg if err := httpcfg.loadFromJSONCfg(jsnhttpCfg, utils.InfieldSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(expected, httpcfg) { t.Errorf("Expected: %+v \n, received: %+v", utils.ToJSON(expected), utils.ToJSON(httpcfg)) } } func TestHttpAgentCfgloadFromJsonCfgCase4(t *testing.T) { cfgJSON := &[]*HttpAgentJsonCfg{ { ID: utils.StringPointer("conecto1"), URL: utils.StringPointer("/conecto"), SessionSConns: &[]string{utils.MetaLocalHost}, RequestPayload: utils.StringPointer(utils.MetaUrl), ReplyPayload: utils.StringPointer(utils.MetaXml), RequestProcessors: &[]*ReqProcessorJsnCfg{ { ID: utils.StringPointer("OutboundAUTHDryRun"), Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{utils.MetaDryRun}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("Allow"), Path: utils.StringPointer("response.Allow"), Type: utils.StringPointer(utils.MetaConstant), Value: utils.StringPointer("1"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, }, { ID: utils.StringPointer("mtcall_cdr"), Filters: &[]string{"*string:*req.request_type:MTCALL_CDR"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{utils.MetaCDRs}, Request_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("RequestType"), Path: utils.StringPointer("RequestType"), Type: utils.StringPointer(utils.MetaConstant), Value: utils.StringPointer("*pseudoprepaid"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, Reply_fields: &[]*FcTemplateJsonCfg{ { Tag: utils.StringPointer("CDR_ID"), Path: utils.StringPointer("CDR_RESPONSE.CDR_ID"), Type: utils.StringPointer(utils.MetaComposed), Value: utils.StringPointer("~*req.CDR_ID"), Mandatory: utils.BoolPointer(true), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00"), }, }, }}, }, { ID: utils.StringPointer("conecto_xml"), URL: utils.StringPointer("/conecto_xml"), SessionSConns: &[]string{utils.MetaLocalHost}, RequestPayload: utils.StringPointer("*xml"), ReplyPayload: utils.StringPointer("*xml"), RequestProcessors: &[]*ReqProcessorJsnCfg{ { ID: utils.StringPointer("cdr_from_xml"), Tenant: utils.StringPointer("a{*"), Flags: &[]string{utils.MetaCDRs}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{}, }, }, }, } expected := "invalid converter terminator in rule: " jsonCfg := NewDefaultCGRConfig() if err := jsonCfg.httpAgentCfg.loadFromJSONCfg(cfgJSON, jsonCfg.generalCfg.RSRSep); err == nil || err.Error() != expected { t.Errorf("Expected %+v, received %+v", expected, err) } } func TestHttpAgentCfgloadFromJsonCfgCase5(t *testing.T) { cfgJSON := &[]*HttpAgentJsonCfg{ { RequestProcessors: nil, }, } jsonCfg := NewDefaultCGRConfig() if err := jsonCfg.httpAgentCfg.loadFromJSONCfg(cfgJSON, jsonCfg.generalCfg.RSRSep); err != nil { t.Error(err) } } func TestHttpAgentCfgloadFromJsonCfgCase6(t *testing.T) { jsonCfg := NewDefaultCGRConfig() httpAgentCfg := new(HTTPAgentCfg) if err := httpAgentCfg.loadFromJSONCfg(nil, jsonCfg.generalCfg.RSRSep); err != nil { t.Error(err) } } func TestHttpAgentCfgloadFromJsonCfgCase7(t *testing.T) { cfgJSONStr := `{ "http_agent": [ { "id": "RandomID", }, ], }` cfgJSON := &[]*HttpAgentJsonCfg{ { ID: utils.StringPointer("RandomID"), }, } expected := HTTPAgentCfgs{ { ID: "RandomID", }, } if jsnCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil { t.Error(err) } else if err = jsnCfg.httpAgentCfg.loadFromJSONCfg(cfgJSON, jsnCfg.generalCfg.RSRSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(&expected, &jsnCfg.httpAgentCfg) { t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(jsnCfg.httpAgentCfg)) } } func TestHttpAgentCfgappendHttpAgntProcCfgs(t *testing.T) { initial := &HTTPAgentCfg{ ID: "conecto1", URL: "/conecto", SessionSConns: []string{utils.MetaLocalHost}, RequestPayload: "*url", ReplyPayload: "*xml", RequestProcessors: []*RequestProcessor{{ ID: "OutboundAUTHDryRun", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{"*dryrun": {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{{ Tag: "Allow", Path: "response.Allow", Type: "*constant", Value: NewRSRParsersMustCompile("1", utils.InfieldSep), Mandatory: true, }}, }}, } initial.RequestProcessors[0].ReplyFields[0].ComputePath() proceses := &[]*ReqProcessorJsnCfg{{ ID: utils.StringPointer("OutboundAUTHDryRun1"), Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{"*dryrun"}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{{ Tag: utils.StringPointer("Allow"), Path: utils.StringPointer("response.Allow"), Type: utils.StringPointer("*constant"), Value: utils.StringPointer("1"), Mandatory: utils.BoolPointer(true), }}, }, { ID: utils.StringPointer("OutboundAUTHDryRun"), Filters: &[]string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: utils.StringPointer("cgrates.org"), Flags: &[]string{"*dryrun"}, Request_fields: &[]*FcTemplateJsonCfg{}, Reply_fields: &[]*FcTemplateJsonCfg{{ Tag: utils.StringPointer("Allow"), Path: utils.StringPointer("response.Allow"), Type: utils.StringPointer("*constant"), Value: utils.StringPointer("1"), Mandatory: utils.BoolPointer(false), }}, }, } expected := &HTTPAgentCfg{ ID: "conecto1", URL: "/conecto", SessionSConns: []string{utils.MetaLocalHost}, RequestPayload: "*url", ReplyPayload: "*xml", RequestProcessors: []*RequestProcessor{{ ID: "OutboundAUTHDryRun", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{"*dryrun": {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{{ Tag: "Allow", Path: "response.Allow", Type: "*constant", Value: NewRSRParsersMustCompile("1", utils.InfieldSep), Mandatory: false, Layout: time.RFC3339, }}, }, { ID: "OutboundAUTHDryRun1", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{"*dryrun": {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{{ Tag: "Allow", Path: "response.Allow", Type: "*constant", Value: NewRSRParsersMustCompile("1", utils.InfieldSep), Mandatory: true, Layout: time.RFC3339, }}, }}, } expected.RequestProcessors[0].ReplyFields[0].ComputePath() expected.RequestProcessors[1].ReplyFields[0].ComputePath() if err := initial.appendHTTPAgntProcCfgs(proceses, utils.InfieldSep); err != nil { t.Error(err) } else if !reflect.DeepEqual(expected, initial) { t.Errorf("Expected: %+v , received: %+v", utils.ToJSON(expected), utils.ToJSON(initial)) } } func TestHttpAgentCfgAsMapInterface(t *testing.T) { cfgJSONStr := `{ "http_agent": [ { "id": "conecto1", "url": "/conecto", "sessions_conns": ["*birpc_internal", "*localhost","*internal"], "stats_conns": ["*internal", "*localhost","*internal"], "thresholds_conns": ["*internal", "*localhost","*internal"], "request_payload": "*url", "reply_payload": "*xml", "request_processors": [ { "id": "OutboundAUTHDryRun", "filters": ["*string:~*req.request_type:OutboundAUTH","*string:~*req.Msisdn:497700056231"], "tenant": "cgrates.org", "flags": ["*dryrun"], "timezone": "", "request_fields":[], "reply_fields":[ {"tag": "Allow", "path": "*rep.response.Allow", "type": "*constant", "value": "1", "mandatory": true}, {"tag": "Concatenated1", "path": "*rep.response.Concatenated", "type": "*composed", "value": "~*req.MCC;/", "mandatory": true}, {"tag": "Concatenated2", "path": "*rep.response.Concatenated", "type": "*composed", "value": "Val1"}, {"tag": "MaxDuration", "path": "*rep.response.MaxDuration", "type": "*constant", "value": "1200", "blocker": true}, {"tag": "Unused", "path": "*rep.response.Unused", "type": "*constant", "value": "0"}, ], }, ], }, ], }` eMap := []map[string]any{ { utils.IDCfg: "conecto1", utils.URLCfg: "/conecto", utils.SessionSConnsCfg: []string{rpcclient.BiRPCInternal, "*localhost", "*internal"}, utils.StatSConnsCfg: []string{rpcclient.InternalRPC, "*localhost", "*internal"}, utils.ThresholdSConnsCfg: []string{rpcclient.InternalRPC, "*localhost", "*internal"}, utils.RequestPayloadCfg: "*url", utils.ReplyPayloadCfg: "*xml", utils.RequestProcessorsCfg: []map[string]any{ { utils.IDCfg: "OutboundAUTHDryRun", utils.FiltersCfg: []string{"*string:~*req.request_type:OutboundAUTH", "*string:~*req.Msisdn:497700056231"}, utils.TenantCfg: "cgrates.org", utils.FlagsCfg: []string{"*dryrun"}, utils.TimezoneCfg: "", utils.RequestFieldsCfg: []map[string]any{}, utils.ReplyFieldsCfg: []map[string]any{ {utils.TagCfg: "Allow", utils.PathCfg: "*rep.response.Allow", utils.TypeCfg: "*constant", utils.ValueCfg: "1", utils.MandatoryCfg: true}, {utils.TagCfg: "Concatenated1", utils.PathCfg: "*rep.response.Concatenated", utils.TypeCfg: "*composed", utils.ValueCfg: "~*req.MCC;/", utils.MandatoryCfg: true}, {utils.TagCfg: "Concatenated2", utils.PathCfg: "*rep.response.Concatenated", utils.TypeCfg: "*composed", utils.ValueCfg: "Val1"}, {utils.TagCfg: "MaxDuration", utils.PathCfg: "*rep.response.MaxDuration", utils.TypeCfg: "*constant", utils.ValueCfg: "1200", utils.BlockerCfg: true}, {utils.TagCfg: "Unused", utils.PathCfg: "*rep.response.Unused", utils.TypeCfg: "*constant", utils.ValueCfg: "0"}, }, }, }, }, } if cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil { t.Error(err) } else if rcv := cgrCfg.httpAgentCfg.AsMapInterface(cgrCfg.generalCfg.RSRSep); !reflect.DeepEqual(eMap, rcv) { t.Errorf("Expected %+v, recieved %+v", eMap, rcv) } } func TestHTTPAgentCfgsClone(t *testing.T) { ban := HTTPAgentCfgs{ { ID: "RandomID", URL: "/randomURL", SessionSConns: []string{"*internal:*sessions", "*conn1"}, RequestPayload: "*url", ReplyPayload: "*xml", RequestProcessors: []*RequestProcessor{{ ID: "OutboundAUTHDryRun", Filters: []string{"*string:*req.request_type:OutboundAUTH", "*string:*req.Msisdn:497700056231"}, Tenant: NewRSRParsersMustCompile("cgrates.org", utils.InfieldSep), Flags: utils.FlagsWithParams{utils.MetaDryRun: {}}, RequestFields: []*FCTemplate{}, ReplyFields: []*FCTemplate{{ Tag: "Allow", Path: "response.Allow", Type: "*constant", Value: NewRSRParsersMustCompile("1", utils.InfieldSep), Mandatory: true, Layout: time.RFC3339, }}, }}, }, } rcv := ban.Clone() if !reflect.DeepEqual(ban, rcv) { t.Errorf("Expected: %+v\nReceived: %+v", utils.ToJSON(ban), utils.ToJSON(rcv)) } if rcv[0].SessionSConns[1] = ""; ban[0].SessionSConns[1] != "*conn1" { t.Errorf("Expected clone to not modify the cloned") } if rcv[0].RequestProcessors[0].ID = ""; ban[0].RequestProcessors[0].ID != "OutboundAUTHDryRun" { t.Errorf("Expected clone to not modify the cloned") } }