Writing a test class on the helper & apex class we are using for an API call. We are running into
System.JSONException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name at [line:1, column:420] with (System Code) Class.FileFeed.doPost: line 16, column 1 Class.TestFileFeed.testDoPost: line 135, column 1.
The calls that are failing are just the post methods and I seem to be running into a loss as to what the issue might be.
File Feed Apex
@RestResource(urlMapping='/FileFeedV1')
global class FileFeed {
// public List<Accounts> accounts;
@HttpGet
global static string doGet() {
return 'This is a GET from the FileFeed service.';
}
@HttpPost
global static string doPost() {
ResponseJSON responseList = new ResponseJSON();
RestRequest req = RestContext.request;
List<Object> jsonWrapList = (List<Object>)JSON.deserializeUntyped(req.requestbody.tostring());
responseList = processFileFeed(jsonWrapList);
return 'This is a POST from the FileFeed Service.';
}
//Inner class for defining JSON response
public class ResponseJSON {
public List<Result> results = new List<Result>();
}
//Inner class for storing JSON response properties
public class Result {
public String type;
public string id;
public String email;
public String phone;
public String error;
}
//Method to process incoming Web leads from Azure
public static ResponseJSON processFileFeed(List<Object> inputJSON){
ResponseJSON sfResponse = new ResponseJSON();
List<File_Feed__c> changeList = new List<File_Feed__c>();
List<Account> addList = new List<Account>();
List<Object> jsonMapped = new List<Object>();
String recordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Employer').getRecordTypeId();
List<Account> allAccounts = null;
Map<String, Account> mappedAccounts = new Map<String, Account>();
List<Account> allEmployerAccounts = null;
Map<String, Account> mappedEmployerAccounts = new Map<String, Account>();
for(Object record : inputJSON) {
Map<String,Object> recordMapped = FileFeedHelper.GetMappings(record);
jsonMapped.add((Object) JSON.deserializeUntyped(JSON.serialize(recordMapped)));
String groupNumber = String.valueOf(recordMapped.get('Group_Number_Form__c'));
String personEmail = String.valueOf(recordMapped.get('PersonEmail')).toLowerCase();
String action = '';
if(recordMapped.containsKey('Action_Type__c')){
action = String.valueOf(recordMapped.get('Action_Type__c'));
recordMapped.remove('Action_Type__c');
} else if(recordMapped.containsKey('Termination_Date')) {
//check if termed based off
if(String.valueOf(recordMapped.get('Termination_Date')) != '') {
action = 'Termed';
}
}
if(recordMapped.containsKey('Termination_Date')) {
recordMapped.remove('Termination_Date');
}
if(action.toLowerCase() != 'add' && action.toLowerCase() != 'change') {
action = 'Termed';
}
Boolean hasEffectiveDate = (String.ValueOf(recordMapped.get('Effective_Date__c')) != '' && String.ValueOf(recordMapped.get('Effective_Date__c')) != null);
if(action.toLowerCase() == 'termed'){
recordMapped.put('Member_Status__c','Cancelled');
if(hasEffectiveDate){
recordMapped.put('Expiration_Date__c',(Object)String.ValueOf(FileFeedHelper.getEndOfMonth(recordMapped.get('Effective_Date__c'))));
}
recordMapped.put('Billing_Notes__c', 'Cancel');
recordMapped.put('Cancel_Reason__c', 'Removed via File Feed');
}
if(hasEffectiveDate){
recordMapped.put('Effective_Date__c', (Object)date.parse(String.valueOf(recordMapped.get('Effective_Date__c'))));
}
if(allAccounts == null){
allAccounts = Database.query('SELECT Id, Member_Status__c, Expiration_Date__c, Billing_Notes__c, Cancel_Reason__c, Group_Number_Form__c, ' + FileFeedHelper.getFields(recordMapped) + ' FROM Account WHERE Group_Number_Form__c != NULL');
for(Account a : allAccounts) {
if(a.Group_Number_Form__C != null && a.PersonEmail != null) {
mappedAccounts.put(a.Group_Number_Form__c.toLowerCase() + a.PersonEmail.toLowerCase(), a);
}
}
}
Account sfAccount = mappedAccounts.get(groupNumber + personEmail);
if(sfAccount != null){
for(String key : recordMapped.keySet()) {
if(key == 'Group_Number_Form__c'){
continue;
}
Object sfValue = sfAccount.get(key);
Object recordValue = recordMapped.get(key);
String sfStrVal = String.valueOf(sfValue);
String recordStrVal = String.valueOf(recordValue);
if( (sfValue == null && recordValue == null) ||
(sfvalue == null && recordValue != null && recordStrVal == '') ||
(sfvalue != null && recordValue == null && sfStrVal == '') ||
(sfValue != null && recordValue != null && sfStrVal.toLowerCase() == recordStrVal.toLowerCase())) {
System.debug('KEY MATCH = ' + key);
} else {
System.debug('KEY MISMATCH = ' + key);
System.debug('sf key value : ' + String.valueOf(sfAccount.get(key)) );
System.debug('json key value : ' + String.valueOf(recordMapped.get(key)) );
//create a FileFeedObject
File_Feed__c newChange = new File_Feed__c();
newChange.Account__c = sfAccount.Id;
newChange.Action_Type__c = String.ValueOf(action);
newChange.Field_Updating__c = key;
newChange.New_Value__c = String.ValueOf(recordMapped.get(key));
newChange.Old_Value__c = String.ValueOf(sfAccount.get(key));
changeList.add(newChange);
}
}
} else {
System.debug('SF ACCOUNT NOT FOUND');
System.debug(groupNumber);
//account does not exist
// create new account object and add to SF
if(allEmployerAccounts == null){
allEmployerAccounts = Database.query('SELECT Id, Group_Number__c FROM Account WHERE RecordTypeId = :recordTypeId and Group_Status__c = \'Active\'');
for(Account a : allEmployerAccounts){
mappedEmployerAccounts.put(String.valueOf(a.get('Group_Number__c')), a);
System.debug(a.get('Group_Number__c'));
}
}
Account sfEmployerAccount = mappedEmployerAccounts.get(groupNumber);
System.debug(sfEmployerAccount.get('Group_Number__c'));
// if this line fails that means there is no company associated to this group number
recordMapped.put('Employer__c', String.ValueOf(sfEmployerAccount.get('Id')));
if(action.toLowerCase() == 'add' || action.toLowerCase() == 'change') {
recordMapped.put('Member_Status__c', 'Active');
recordMapped.put('Original_Effective_Date__c', recordMapped.get('Effective_Date__c'));
recordMapped.put('Expiration_Date__c', (Object)date.parse('12/31/' + Date.today().year()));
recordMapped.put('Payment_Schedule__c', 'Monthly');
recordMapped.put('Billing_Type__c', 'Payroll Deduct');
recordMapped.put('Billing_Notes__c', 'New');
//generate auto incrementing unique memberid
// recordMapped.put('Member_ID__c', '');
addList.add((Account) JSON.deserialize(JSON.serialize(recordMapped), Account.class));
} else {
//check termed date and see if they are terming in the future
if(hasEffectiveDate && (Date)recordMapped.get('Effective_Date__c') >= Date.today()) {
//set to end of the month
addList.add((Account) JSON.deserialize(JSON.serialize(recordMapped), Account.class));
}
}
}
}
for(Account a : addList) {
System.debug(a);
}
insert changeList;
insert addList;
return sfResponse;
}
}
File Feed Helper Apex
public with sharing class FileFeedHelper {
static Map<String,String> jsonToSfoMappings = new Map<String,String>();
static {
//Action Mappings;
jsonToSfoMappings.put('Action','Action_Type__c');
jsonToSfoMappings.put('/Action','Action_Type__c');
jsonToSfoMappings.put('TerminationDate','Termination_Date');
jsonToSfoMappings.put('Termination_Date','Termination_Date');
//Email Mappings;
jsonToSfoMappings.put('Email_Address','PersonEmail');
jsonToSfoMappings.put('E-mail','PersonEmail');
jsonToSfoMappings.put('Email','PersonEmail');
jsonToSfoMappings.put('Business Email','PersonEmail');
jsonToSfoMappings.put('Business_Email','PersonEmail');
jsonToSfoMappings.put('E-mail Address','PersonEmail');
jsonToSfoMappings.put('E-mail_Address','PersonEmail');
jsonToSfoMappings.put('Email Address','PersonEmail');
jsonToSfoMappings.put('Email_Address','PersonEmail');
//Group Number Mappings;
jsonToSfoMappings.put('Group_Number','Group_Number_Form__c');
jsonToSfoMappings.put('Group Number','Group_Number_Form__c');
jsonToSfoMappings.put('Group #','Group_Number_Form__c');
jsonToSfoMappings.put('Group_#','Group_Number_Form__c');
//First Name Mappings;
jsonToSfoMappings.put('First_Name','FirstName');
jsonToSfoMappings.put('First Name','FirstName');
//Last Name Mappings;
jsonToSfoMappings.put('Last_Name','LastName');
jsonToSfoMappings.put('Last Name','LastName');
//Employee ID Mappings;
jsonToSfoMappings.put('Employee ID','Employee_ID__c');
jsonToSfoMappings.put('Employee_ID','Employee_ID__c');
jsonToSfoMappings.put('Member ID','Employee_ID__c');
jsonToSfoMappings.put('Member_ID','Employee_ID__c');
jsonToSfoMappings.put('Employee ID Number','Employee_ID__c');
jsonToSfoMappings.put('Employee_ID_Number','Employee_ID__c');
jsonToSfoMappings.put('Employee Number','Employee_ID__c');
jsonToSfoMappings.put('Employee_Number','Employee_ID__c');
//Middle Initial Mappings;
// jsonToSfoMappings.put('Middle_Initial','');
//Primary Phone Mappings;
jsonToSfoMappings.put('Primary Phone','Phone');
jsonToSfoMappings.put('Primary_Phone','Phone');
jsonToSfoMappings.put('Personal Phone','Phone');
jsonToSfoMappings.put('Personal_Phone','Phone');
jsonToSfoMappings.put('Phone','Phone');
jsonToSfoMappings.put('Home Phone','Phone');
jsonToSfoMappings.put('Home_Phone','Phone');
//Secondary Phone Mappings;
jsonToSfoMappings.put('Secondary Phone','PersonOtherPhone');
jsonToSfoMappings.put('Secondary_Phone','PersonOtherPhone');
jsonToSfoMappings.put('Work Phone','PersonOtherPhone');
jsonToSfoMappings.put('Work_Phone','PersonOtherPhone');
jsonToSfoMappings.put('Mobile Phone','PersonOtherPhone');
jsonToSfoMappings.put('Mobile_Phone','PersonOtherPhone');
//Home Address 1 Mappings;
jsonToSfoMappings.put('Home Address 1','BillingStreet');
jsonToSfoMappings.put('Home_Address_1','BillingStreet');
jsonToSfoMappings.put('Home Address','BillingStreet');
jsonToSfoMappings.put('Home_Address','BillingStreet');
jsonToSfoMappings.put('Address1','BillingStreet');
jsonToSfoMappings.put('Address 1','BillingStreet');
jsonToSfoMappings.put('Address_1','BillingStreet');
jsonToSfoMappings.put('Home Address1','BillingStreet');
jsonToSfoMappings.put('Home_Address1','BillingStreet');
jsonToSfoMappings.put('Address Line 1','BillingStreet');
jsonToSfoMappings.put('Address_Line_1','BillingStreet');
// //Home Address 2 Mappings;
jsonToSfoMappings.put('Home Address 2','BillingStreet2');
jsonToSfoMappings.put('Home_Address_2','BillingStreet2');
jsonToSfoMappings.put('Address2','BillingStreet2');
jsonToSfoMappings.put('Address 2','BillingStreet2');
jsonToSfoMappings.put('Address_2','BillingStreet2');
jsonToSfoMappings.put('Home Address2','BillingStreet2');
jsonToSfoMappings.put('Home_Address2','BillingStreet2');
jsonToSfoMappings.put('Address Line 2','BillingStreet2');
jsonToSfoMappings.put('Address_Line_2','BillingStreet2');
//City Mappings;
jsonToSfoMappings.put('City','BillingCity');
//State Mappings;
jsonToSfoMappings.put('State','BillingState');
jsonToSfoMappings.put('State/Province','BillingState');
//Zip Code Mappings;
jsonToSfoMappings.put('Zip Code','BillingPostalCode');
jsonToSfoMappings.put('Zip_Code','BillingPostalCode');
jsonToSfoMappings.put('Zip','BillingPostalCode');
//Plan Mappings;
jsonToSfoMappings.put('Benefit_Program','Plan__c');
jsonToSfoMappings.put('Program','Plan__c');
jsonToSfoMappings.put('Plan','Plan__c');
jsonToSfoMappings.put('Deduction/Benefit (Long)','Plan__c');
jsonToSfoMappings.put('Deduction/Benefit_(Long)','Plan__c');
//Num of Pets Mappings;
jsonToSfoMappings.put('Number_of_Pets','Number_of_Pets__c');
jsonToSfoMappings.put('Coverage','Number_of_Pets__c');
jsonToSfoMappings.put('Benefit Option Code','Number_of_Pets__c');
jsonToSfoMappings.put('Benefit_Option_Code','Number_of_Pets__c');
jsonToSfoMappings.put('Number of Pets','Number_of_Pets__c');
//Rate Mappings;
jsonToSfoMappings.put('Rate','Rate__c');
jsonToSfoMappings.put('Total Rate','Rate__c');
jsonToSfoMappings.put('Total_Rate','Rate__c');
jsonToSfoMappings.put('Monthly Employee Rate','Rate__c');
jsonToSfoMappings.put('Monthly_Employee_Rate','Rate__c');
jsonToSfoMappings.put('Employee Rate','Rate__c');
jsonToSfoMappings.put('Employee_Rate','Rate__c');
//Effective Date Mappings;
jsonToSfoMappings.put('Effective Date','Effective_Date__c');
jsonToSfoMappings.put('Effective_Date','Effective_Date__c');
jsonToSfoMappings.put('Plan Effective Start Date','Effective_Date__c');
jsonToSfoMappings.put('Plan_Effective_Start_Date','Effective_Date__c');
jsonToSfoMappings.put('Effective Start Date','Effective_Date__c');
jsonToSfoMappings.put('Effective_Start_Date','Effective_Date__c');
jsonToSfoMappings.put('Coverage Start Date','Effective_Date__c');
jsonToSfoMappings.put('Coverage_Start_Date','Effective_Date__c');
//Work Location Mappings;
jsonToSfoMappings.put('Work Location','Work_Location__c');
jsonToSfoMappings.put('Work_Location','Work_Location__c');
jsonToSfoMappings.put('Location','Work_Location__c');
jsonToSfoMappings.put('Division','Work_Location__c');
jsonToSfoMappings.put('Company','Work_Location__c');
// //Vet Number Mappings;
// jsonToSfoMappings.put('Vet_Number','');
// //Pet Name Mappings;
// jsonToSfoMappings.put('Pet_Name','');
// //Pet Type Mappings;
// jsonToSfoMappings.put('Pet_Type','');
// //Pet Breed Mappings;
// jsonToSfoMappings.put('Pet_Breed','');
// //Pet Age Mappings;
// jsonToSfoMappings.put('Pet_Age','Pet_Age__c');
// //Pet Gender Mappings;
// jsonToSfoMappings.put('Pet_Gender','');
}
private static String getMap(String key){
return jsonToSfoMappings.get(key);
}
public static Map<String,Object> getMappings(Object originalRecord) {
Map<String,Object> originalMap = (Map<String,Object>) JSON.deserializeUntyped(JSON.serialize(originalRecord));
Map<String,Object> newMap = new Map<String,Object>();
for(String key : originalMap.keySet()) {
String mappedKey = getMap(key);
if(mappedKey != '' && mappedKey != null) {
newMap.put(mappedKey, originalMap.get(key));
}
}
if(newMap.containsKey('BillingStreet2')) {
String addr1 = String.valueOf(newMap.get('BillingStreet'));
String addr2 = String.valueOf(newMap.get('BillingStreet2'));
if(addr2 != '' && addr2 != null){
newMap.put('BillingStreet', addr1 + ' ' + addr2);
}
newMap.remove('BillingStreet2');
}
return newMap;
}
public static Account convertJsonToAccountObject(String json) {
Account a = new Account();
return a;
}
public static String getFields(Map<String,Object> mapping) {
String fields = '';
for(String key : mapping.keySet()) {
if(key == 'Action_Type__c' || key == 'Member_Status__c' || key == 'Expiration_Date__c' || key == 'Billing_Notes__c' || key == 'Cancel_Reason__c' || key == 'Group_Number_Form__c'){
continue;
}
if (fields != '') {
fields += ',' + key;
}else {
fields += key;
}
}
return fields;
}
public static Date getEndOfMonth(Object dateToParse){
return Date.parse(String.valueOf(dateToParse)).addMonths(1).toStartOfMonth().addDays(-1);
}
}
Test Class
@IsTest
public class TestFileFeed {
static testMethod void testDoPost(){
//Create test data
Rate_Card__c rc = new Rate_Card__c();
rc.name = 'File Feed Testing Rate';
rc.Preferred_1_Monthly__c = 10;
rc.Preferred_2_Monthly__c = 20;
rc.Preferred_3_Monthly__c = 30;
rc.Preferred_4_Monthly__c = 8;
insert rc;
Id employerAccountRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Employer').getRecordTypeId();
Account a = new Account();
a.Name = 'Test Employer';
a.Group_Number__c = '123';
a.Group_Status__c = 'Active';
a.Participating_Program__c = 'Choice;Select;Preferred;Basic';
//a.Effective_Date__c = date.valueOf(01/01/2020);
a.Rate_Card__c = rc.id;
insert a;
String JSONMsg = '{'+
' \"records\": ['+
' {'+
' \"Action\": \"Change\",'+
' \"Group_Number\": \"932\",'+
' \"Employee ID Number\": \"1234\",'+
' \"Last Name\": \"Testerson\",'+
' \"First Name\": \"test\",'+
' \"Middle Initial\": \"s\",'+
' \"Primary Phone\": \"1234567890\",'+
' \"Home Address1\": \"638 W Nido Ave\",'+
' \"City\": \"Mesa\",'+
' \"State\": \"Az\",'+
' \"Zip Code\": \"85210\",'+
' \"Email Address\": \"[email protected]\",'+
' \"Program\": \"Preferred\",'+
' \"Number of Pets\": \"1\",'+
' \"Rate\": \"10.26\",'+
// ' \"Effective Date\": \"09\/01\/2022\",'+
' },'+
' {'+
' \"Action\": \"Change\",'+
' \"Group_Number\": \"932\",'+
' \"Employee ID Number\": \"1234\",'+
' \"Last Name\": \"Testerson\",'+
' \"First Name\": \"test\",'+
' \"Middle Initial\": \"s\",'+
' \"Primary Phone\": \"1234567890\",'+
' \"Home Address1\": \"638 W Nido Ave\",'+
' \"City\": \"Mesa\",'+
' \"State\": \"Az\",'+
' \"Zip Code\": \"85210\",'+
' \"Email Address\": \"[email protected]\",'+
' \"Program\": \"Preferred\",'+
' \"Number of Pets\": \"1\",'+
' \"Rate\": \"10.26\",'+
// ' \"Effective Date\": \"09\/01\/2022\",'+
' },'+
' {'+
' \"Action\": \"Change\",'+
' \"Group_Number\": \"932\",'+
' \"Employee ID Number\": \"1234\",'+
' \"Last Name\": \"Testerson\",'+
' \"First Name\": \"test\",'+
' \"Middle Initial\": \"s\",'+
' \"Primary Phone\": \"1234567890\",'+
' \"Home Address1\": \"638 W Nido Ave\",'+
' \"City\": \"Mesa\",'+
' \"State\": \"Az\",'+
' \"Zip Code\": \"85210\",'+
' \"Email Address\": \"[email protected]\",'+
' \"Program\": \"Preferred\",'+
' \"Number of Pets\": \"1\",'+
' \"Rate\": \"10.26\",'+
// ' \"Effective Date\": \"09\/01\/2022\",'+
' },'+
' {'+
' \"Action\": \"Change\",'+
' \"Group_Number\": \"932\",'+
' \"Employee ID Number\": \"1234\",'+
' \"Last Name\": \"Testerson\",'+
' \"First Name\": \"test\",'+
' \"Middle Initial\": \"s\",'+
' \"Primary Phone\": \"1234567890\",'+
' \"Home Address1\": \"638 W Nido Ave\",'+
' \"City\": \"Mesa\",'+
' \"State\": \"Az\",'+
' \"Zip Code\": \"85210\",'+
' \"Email Address\": \"[email protected]\",'+
' \"Program\": \"Preferred\",'+
' \"Number of Pets\": \"1\",'+
' \"Rate\": \"10.26\",'+
// ' \"Effective Date\": \"09\/01\/2022\",'+
' },'+
' {'+
' \"Action\": \"Change\",'+
' \"Group_Number\": \"932\",'+
' \"Employee ID Number\": \"1234\",'+
' \"Last Name\": \"Testerson\",'+
' \"First Name\": \"test\",'+
' \"Middle Initial\": \"s\",'+
' \"Primary Phone\": \"1234567890\",'+
' \"Home Address1\": \"638 W Nido Ave\",'+
' \"City\": \"Mesa\",'+
' \"State\": \"Az\",'+
' \"Zip Code\": \"85210\",'+
' \"Email Address\": \"[email protected]\",'+
' \"Program\": \"Preferred\",'+
' \"Number of Pets\": \"1\",'+
' \"Rate\": \"10.26\",'+
// ' \"Effective Date\": \"09\/01\/2022\",'+
' }]'+
'}';
//Prepare input
RestRequest req = new RestRequest();
RestResponse res = new RestResponse();
req.requestURI = '/services/apexrest/FileFeedV1';
req.httpMethod = 'POST';
req.requestBody = Blob.valueof(JSONMsg);
RestContext.request = req;
RestContext.response= res;
Test.startTest();
FileFeed.doPost();
Test.stopTest();
}
}
UPDATE
In the test class the entire section of String JSONMsg = ...
code was replaced by the code from @sfdcfox's answer. Then one other line was changed so that JSONMsg
was replaced by output
:
req.requestBody = Blob.valueof(output);
Now after running a different error is seen:
System.TypeException: Invalid conversion from runtime type Map<String,ANY> to List