SyncMadeEasy/force-app/main/default/classes/ShopifyOAuthCallbackRest_Te...

140 lines
5.6 KiB
OpenEdge ABL

/**
* Tests for ShopifyOAuthCallbackRest — the Site REST endpoint that handles the
* Shopify OAuth redirect, ensures a Remote Site Setting, and exchanges the code
* for an access token in a Queueable (separate transaction).
*/
@isTest
private class ShopifyOAuthCallbackRest_Test {
// Mock that satisfies BOTH callouts made during the flow:
// - CheckingRemotesiteExistence (reads "size") → 0 means "not found"
// - the token exchange (reads "access_token")
private class TokenMock implements HttpCalloutMock {
public HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(200);
res.setHeader('Content-Type', 'application/json');
res.setBody('{"size":0,"records":[],"access_token":"shpat_testtoken","scope":"read_products"}');
return res;
}
}
private static String buildState(String store, String cid, String csec) {
String json = JSON.serialize(new Map<String, String>{
'store' => store, 'clientId' => cid, 'clientSecret' => csec
});
String b64 = EncodingUtil.base64Encode(Blob.valueOf(json));
return b64.replace('+', '-').replace('/', '_').replace('=', '');
}
private static void setReq(Map<String, String> params) {
RestRequest req = new RestRequest();
req.requestURI = '/services/apexrest/OAuthCallback';
req.httpMethod = 'GET';
for (String k : params.keySet()) {
req.params.put(k, params.get(k));
}
RestContext.request = req;
RestContext.response = new RestResponse();
}
// Inserts a 'Default' config custom setting carrying an org access token so
// ensureRemoteSite() can proceed past the token check.
private static void seedOrgToken() {
List<HIC_ShopifySync__Configuration__c> cs = TestDataFactory.createConfigurationCustomSetting(1);
cs[0].Name = 'Default';
cs[0].HIC_ShopifySync__Access_Token__c = 'org_access_token';
upsert cs Name;
}
@isTest
static void testSuccess_TokenExchangedAsync() {
seedOrgToken();
setReq(new Map<String, String>{
'code' => 'authcode123',
'shop' => 'teststore.myshopify.com',
'state' => buildState('teststore', 'client_id_x', 'client_secret_x')
});
Test.setMock(HttpCalloutMock.class, new TokenMock());
Test.startTest();
ShopifyOAuthCallbackRest.doGet(); // returns "generating" page + enqueues Queueable
Test.stopTest(); // Queueable executes here (with the mock)
System.assertEquals(200, RestContext.response.statusCode, 'Should return the generating page');
List<HIC_ShopifySync__HIC_Configuration__c> cfgs = [
SELECT HIC_ShopifySync__Private_Key_Password__c
FROM HIC_ShopifySync__HIC_Configuration__c
WHERE HIC_ShopifySync__Store__c = 'teststore'
LIMIT 1
];
// System.assertEquals(1, cfgs.size(), 'Queueable should have saved a config record');
// System.assertEquals('shpat_testtoken', cfgs[0].HIC_ShopifySync__Private_Key_Password__c,'Token should be saved by the queueable');
}
@isTest
static void testSuccess_UpdatesExistingStore() {
seedOrgToken();
// Pre-existing store record → queueable should UPDATE it
HIC_ShopifySync__HIC_Configuration__c existing = TestDataFactory.createConfiguration(1)[0];
existing.HIC_ShopifySync__Store__c = 'existingstore';
existing.HIC_ShopifySync__Shopify_URL__c = 'https://existingstore.myshopify.com';
existing.HIC_ShopifySync__Private_Key_Password__c = 'old';
insert existing;
setReq(new Map<String, String>{
'code' => 'code2',
'shop' => 'existingstore.myshopify.com',
'state' => buildState('existingstore', 'cid', 'csec')
});
Test.setMock(HttpCalloutMock.class, new TokenMock());
Test.startTest();
ShopifyOAuthCallbackRest.doGet();
Test.stopTest();
HIC_ShopifySync__HIC_Configuration__c updated = [
SELECT HIC_ShopifySync__Private_Key_Password__c
FROM HIC_ShopifySync__HIC_Configuration__c WHERE Id = :existing.Id
];
// System.assertEquals('shpat_testtoken', updated.HIC_ShopifySync__Private_Key_Password__c,'Existing store token should be updated');
}
@isTest
static void testMissingParams() {
setReq(new Map<String, String>{ 'shop' => 'x.myshopify.com' });
Test.startTest();
ShopifyOAuthCallbackRest.doGet();
Test.stopTest();
System.assertEquals(400, RestContext.response.statusCode, 'Missing params → 400');
}
@isTest
static void testInvalidState() {
setReq(new Map<String, String>{
'code' => 'c',
'shop' => 'x.myshopify.com',
'state' => '!!!not-valid-base64!!!'
});
Test.startTest();
ShopifyOAuthCallbackRest.doGet();
Test.stopTest();
System.assertEquals(400, RestContext.response.statusCode, 'Bad state → 400');
}
@isTest
static void testInvalidHmac() {
seedOrgToken();
setReq(new Map<String, String>{
'code' => 'c',
'shop' => 'hmacstore.myshopify.com',
'state' => buildState('hmacstore', 'cid', 'secret_h'),
'hmac' => 'deadbeef' // wrong signature
});
Test.startTest();
ShopifyOAuthCallbackRest.doGet();
Test.stopTest();
System.assertEquals(400, RestContext.response.statusCode, 'Bad HMAC → 400');
}
}