Skip to content

Commit d184b75

Browse files
committed
test: replace live API calls with Saloon fixture recording
Use MockClient + MockResponse::fixture() to record real Salesforce responses on first run and replay them offline afterward. No scratch org or credentials needed to run tests going forward.
1 parent 0f27494 commit d184b75

62 files changed

Lines changed: 1611 additions & 194 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

tests/BulkApiTest.php

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
use myoutdeskllc\SalesforcePhp\Constants\BulkApiOptions;
44
use myoutdeskllc\SalesforcePhp\Support\SalesforceJob;
5-
6-
beforeEach(function () {
7-
getAPI();
8-
});
5+
use Saloon\Http\Faking\MockClient;
6+
use Saloon\Http\Faking\MockResponse;
97

108
test('Can create a bulk API job', function () {
11-
$api = getAPI()->getBulkApi();
9+
$mockClient = new MockClient([
10+
MockResponse::fixture('bulk/create_job'),
11+
]);
12+
$api = getAPI($mockClient)->getBulkApi();
1213
$salesforceJob = new SalesforceJob($api);
1314
$salesforceJob->setObject('Account');
1415
$salesforceJob->setOperation(BulkApiOptions::INSERT);
@@ -19,7 +20,12 @@
1920
});
2021

2122
test('Can upload records to a bulk API job', function () {
22-
$api = getAPI()->getBulkApi();
23+
$mockClient = new MockClient([
24+
MockResponse::fixture('bulk/create_job_upload'),
25+
MockResponse::fixture('bulk/upload_data'),
26+
MockResponse::fixture('bulk/close_job'),
27+
]);
28+
$api = getAPI($mockClient)->getBulkApi();
2329
$salesforceJob = new SalesforceJob($api);
2430
$salesforceJob->setObject('Account');
2531
$salesforceJob->setOperation(BulkApiOptions::INSERT);
@@ -32,7 +38,11 @@
3238
});
3339

3440
test('Can get existing job status', function () {
35-
$api = getAPI()->getBulkApi();
41+
$mockClient = new MockClient([
42+
MockResponse::fixture('bulk/create_job_status'),
43+
MockResponse::fixture('bulk/get_job'),
44+
]);
45+
$api = getAPI($mockClient)->getBulkApi();
3646
$salesforceJob = new SalesforceJob($api);
3747
$salesforceJob->setObject('Account');
3848
$salesforceJob->setOperation(BulkApiOptions::INSERT);
@@ -44,7 +54,12 @@
4454
});
4555

4656
test('Can abort a job', function () {
47-
$api = getAPI()->getBulkApi();
57+
$mockClient = new MockClient([
58+
MockResponse::fixture('bulk/create_job_abort'),
59+
MockResponse::fixture('bulk/get_job_abort'),
60+
MockResponse::fixture('bulk/abort_job'),
61+
]);
62+
$api = getAPI($mockClient)->getBulkApi();
4863
$salesforceJob = new SalesforceJob($api);
4964
$salesforceJob->setObject('Account');
5065
$salesforceJob->setOperation(BulkApiOptions::INSERT);

tests/OrganizationTest.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
<?php
22

3+
use Saloon\Http\Faking\MockClient;
4+
use Saloon\Http\Faking\MockResponse;
5+
36
test('Can query organizational limits', function () {
4-
$api = getAPI();
5-
// Assert just a few keys should exist, well know the endpoint works
7+
$mockClient = new MockClient([
8+
MockResponse::fixture('organization/limits'),
9+
]);
10+
$api = getAPI($mockClient);
611
expect($api->getLimits())->toHaveKeys(['ConcurrentAsyncGetReportInstances', 'HourlyAsyncReportRuns', 'HourlyDashboardStatuses']);
712
});
813

914
test('Can query supported APIs', function () {
10-
$api = getAPI();
11-
// This will contain a lot of information on APIs available, so we'll just make sure it's not empty
15+
$mockClient = new MockClient([
16+
MockResponse::fixture('organization/api_versions'),
17+
]);
18+
$api = getAPI($mockClient);
1219
expect($api->listApiVersionsAvailable())->not()->toBeEmpty();
1320
});

tests/Pest.php

Lines changed: 28 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,43 @@
11
<?php
22

3-
/*
4-
|--------------------------------------------------------------------------
5-
| Test Case
6-
|--------------------------------------------------------------------------
7-
|
8-
| The closure you provide to your test functions is always bound to a specific PHPUnit test
9-
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
10-
| need to change it using the "uses()" function to bind a different classes or traits.
11-
|
12-
*/
13-
14-
// uses(Tests\TestCase::class)->in('Feature');
15-
16-
/*
17-
|--------------------------------------------------------------------------
18-
| Expectations
19-
|--------------------------------------------------------------------------
20-
|
21-
| When you're writing tests, you often need to check that values meet certain conditions. The
22-
| "expect()" function gives you access to a set of "expectations" methods that you can use
23-
| to assert different things. Of course, you may extend the Expectation API at any time.
24-
|
25-
*/
26-
273
use myoutdeskllc\SalesforcePhp\SalesforceApi;
4+
use myoutdeskllc\SalesforcePhp\Connectors\SalesforceApiConnector;
5+
use Saloon\MockConfig;
6+
use Saloon\Http\Faking\MockClient;
7+
use Saloon\Http\Faking\MockResponse;
288

299
expect()->extend('toBeOne', function () {
3010
return $this->toBe(1);
3111
});
3212

33-
/*
34-
|--------------------------------------------------------------------------
35-
| Functions
36-
|--------------------------------------------------------------------------
37-
|
38-
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
39-
| project that you don't want to repeat in every file. Here you can also expose helpers as
40-
| global functions to help you to reduce the number of lines of code in your test files.
41-
|
42-
*/
13+
MockConfig::setFixturePath('tests/fixtures/responses');
4314

44-
function getAPI()
15+
function getAPI(?MockClient $mockClient = null): SalesforceApi
4516
{
4617
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__.'/../');
47-
$dotenv->load();
48-
49-
$api = new SalesforceApi($_ENV['SALESFORCE_INSTANCE_URL'], $_ENV['API_VERSION']);
18+
$dotenv->safeLoad();
19+
20+
$api = new SalesforceApi(
21+
$_ENV['SALESFORCE_INSTANCE_URL'] ?? 'https://test.salesforce.com',
22+
$_ENV['API_VERSION'] ?? 'v51.0'
23+
);
24+
25+
$authenticatorFile = __DIR__.'/../.authenticator';
26+
27+
if (file_exists($authenticatorFile)) {
28+
$api->restoreExistingOAuthConnection(file_get_contents($authenticatorFile), function ($authenticator) use ($authenticatorFile) {
29+
file_put_contents($authenticatorFile, SalesforceApi::serializeAuthenticator($authenticator));
30+
});
31+
} else {
32+
// No live credentials - fixtures must already exist
33+
$connector = new SalesforceApiConnector();
34+
$connector->withTokenAuth('mock-access-token');
35+
$api->setConnector($connector);
36+
}
5037

51-
// this is questionable, but works for testing with OAuth connections
52-
$api->restoreExistingOAuthConnection(file_get_contents('.authenticator'), function ($authenticator) {
53-
file_put_contents('.authenticator', SalesforceApi::serializeAuthenticator($authenticator));
54-
});
38+
if ($mockClient !== null) {
39+
$api->getConnector()->withMockClient($mockClient);
40+
}
5541

5642
$api->recordsOnly();
5743

@@ -64,34 +50,3 @@ function toFlatArray(array $results, string $key)
6450
return $result[$key];
6551
}, $results);
6652
}
67-
68-
function destroyPestPhpSalesforceChanges()
69-
{
70-
$reportApi = getAPI()->recordsOnly()->getReportApi();
71-
72-
$reports = $reportApi->listReports();
73-
$folders = $reportApi->listFolders();
74-
75-
foreach ($reports as $report) {
76-
if (stripos($report['Name'], 'PESTPHP') !== false) {
77-
$reportApi->deleteReport($report['Id']);
78-
}
79-
}
80-
81-
foreach ($folders as $folder) {
82-
if (stripos($folder['Name'], 'PESTPHP') !== false) {
83-
if ($folder['Type'] === 'Dashboard') {
84-
$dashboardsInFolder = $reportApi->recordsOnly()->listDashboardsInFolderById($folder['Id']);
85-
foreach ($dashboardsInFolder as $dashboard) {
86-
$reportApi->deleteDashboard($dashboard['Id']);
87-
}
88-
}
89-
90-
try {
91-
$reportApi->deleteFolder($folder['Id']);
92-
} catch (\Exception $e) {
93-
// eat this error. Sometimes deletions just fail due to needing a hard delete from the bulk api
94-
}
95-
}
96-
}
97-
}

tests/ReportApiTest.php

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,119 @@
11
<?php
22

3-
beforeEach(function () {
4-
getAPI();
5-
});
6-
7-
afterAll(function () {
8-
destroyPestPhpSalesforceChanges();
9-
});
3+
use Saloon\Http\Faking\MockClient;
4+
use Saloon\Http\Faking\MockResponse;
105

116
test('it can query folders', function () {
12-
$reportApi = getAPI()->getReportApi();
7+
$mockClient = new MockClient([
8+
MockResponse::fixture('report/list_folders'),
9+
]);
10+
$reportApi = getAPI($mockClient)->getReportApi();
1311
$folders = $reportApi->listFolders();
1412

1513
expect($folders)->not()->toBeEmpty();
1614
});
1715

1816
test('it can create report folder(s)', function () {
19-
$temporaryName = 'PESTPHP'.bin2hex(random_bytes(10));
20-
$reportApi = getAPI()->getReportApi();
21-
$reportApi->createReportFolder($temporaryName);
22-
$folder = $reportApi->recordsOnly()->getFolderByName($temporaryName);
17+
$mockClient = new MockClient([
18+
MockResponse::fixture('report/create_report_folder'),
19+
MockResponse::fixture('report/get_report_folder'),
20+
]);
21+
$reportApi = getAPI($mockClient)->getReportApi();
22+
$reportApi->createReportFolder('PESTPHPTestFolder');
23+
$folder = $reportApi->getFolderByName('PESTPHPTestFolder');
2324

2425
expect($folder)->not()->toBeNull();
25-
expect($folder)->toHaveKey('Name', $temporaryName);
26+
expect($folder)->toHaveKey('Name', 'PESTPHPTestFolder');
2627
});
2728

2829
test('it can create dashboard folder(s)', function () {
29-
$temporaryName = 'PESTPHP'.bin2hex(random_bytes(10));
30-
$reportApi = getAPI()->getReportApi();
31-
$reportApi->createDashboardFolder($temporaryName);
32-
$folder = $reportApi->recordsOnly()->getDashboardFolderByName($temporaryName);
30+
$mockClient = new MockClient([
31+
MockResponse::fixture('report/create_dashboard_folder'),
32+
MockResponse::fixture('report/get_dashboard_folder'),
33+
]);
34+
$reportApi = getAPI($mockClient)->getReportApi();
35+
$reportApi->createDashboardFolder('PESTPHPDashFolder');
36+
$folder = $reportApi->getDashboardFolderByName('PESTPHPDashFolder');
3337

3438
expect($folder)->not()->toBeNull();
3539
});
3640

3741
test('it can delete folder(s)', function () {
38-
$temporaryName = 'PESTPHP'.bin2hex(random_bytes(10));
39-
$reportApi = getAPI()->getReportApi();
40-
$reportApi->createReportFolder($temporaryName);
41-
$folder = $reportApi->recordsOnly()->getFolderByName($temporaryName);
42+
$mockClient = new MockClient([
43+
MockResponse::fixture('report/create_folder_delete'),
44+
MockResponse::fixture('report/get_folder_delete'),
45+
MockResponse::fixture('report/delete_folder'),
46+
]);
47+
$reportApi = getAPI($mockClient)->getReportApi();
48+
$reportApi->createReportFolder('PESTPHPDeleteFolder');
49+
$folder = $reportApi->getFolderByName('PESTPHPDeleteFolder');
4250

4351
expect($folder)->not()->toBeNull();
4452

4553
expect($reportApi->deleteFolder($folder['Id']))->toBe(true);
4654
});
4755

4856
test('it can find folders by name', function () {
49-
$temporaryName = 'PESTPHP'.bin2hex(random_bytes(10));
50-
$reportApi = getAPI()->getReportApi();
51-
$reportApi->createReportFolder($temporaryName);
52-
$folder = $reportApi->recordsOnly()->getFolderByName($temporaryName);
53-
54-
expect($folder)->toHaveKey('Name', $temporaryName);
57+
$mockClient = new MockClient([
58+
MockResponse::fixture('report/create_folder_find'),
59+
MockResponse::fixture('report/get_folder_find'),
60+
]);
61+
$reportApi = getAPI($mockClient)->getReportApi();
62+
$reportApi->createReportFolder('PESTPHPFindFolder');
63+
$folder = $reportApi->getFolderByName('PESTPHPFindFolder');
64+
65+
expect($folder)->toHaveKey('Name', 'PESTPHPFindFolder');
5566
});
5667

5768
test('it returns null when folders are not found', function () {
58-
$reportApi = getAPI()->getReportApi();
59-
$foldersAvailable = $reportApi->recordsOnly()->getFolderByName('This is not a real folder');
69+
$mockClient = new MockClient([
70+
MockResponse::fixture('report/folder_not_found'),
71+
]);
72+
$reportApi = getAPI($mockClient)->getReportApi();
73+
$foldersAvailable = $reportApi->getFolderByName('This is not a real folder');
6074

6175
expect($foldersAvailable)->toBeNull();
6276
});
6377

6478
test('it can find reports', function () {
65-
$reportApi = getAPI()->getReportApi();
66-
$reports = $reportApi->recordsOnly()->listReports();
79+
$mockClient = new MockClient([
80+
MockResponse::fixture('report/list_reports'),
81+
]);
82+
$reportApi = getAPI($mockClient)->getReportApi();
83+
$reports = $reportApi->listReports();
6784

6885
// Just verify the API returns an array (scratch org may not have reports)
6986
expect($reports)->toBeArray();
7087
});
7188

7289
test('it returns null when a report cannot be found', function () {
73-
$reportApi = getAPI()->getReportApi();
74-
$report = $reportApi->recordsOnly()->getReportByName('Games Done Quick 2020');
90+
$mockClient = new MockClient([
91+
MockResponse::fixture('report/report_not_found'),
92+
]);
93+
$reportApi = getAPI($mockClient)->getReportApi();
94+
$report = $reportApi->getReportByName('Games Done Quick 2020');
7595

7696
expect($report)->toBeNull();
7797
});
7898

7999
test('it can create a folder for dashboards', function () {
80-
$temporaryName = 'PESTPHP'.bin2hex(random_bytes(10));
81-
$reportApi = getAPI()->getReportApi();
82-
$reportApi->createDashboardFolder($temporaryName);
83-
$folder = $reportApi->recordsOnly()->getDashboardFolderByName($temporaryName);
100+
$mockClient = new MockClient([
101+
MockResponse::fixture('report/create_dash_folder2'),
102+
MockResponse::fixture('report/get_dash_folder2'),
103+
]);
104+
$reportApi = getAPI($mockClient)->getReportApi();
105+
$reportApi->createDashboardFolder('PESTPHPDashFolder2');
106+
$folder = $reportApi->getDashboardFolderByName('PESTPHPDashFolder2');
84107

85108
expect($folder)->not()->toBe(null);
86109
});
87110

88111
test('it can get a list of dashboards', function () {
89-
$reportApi = getAPI()->getReportApi();
90-
$dashboards = $reportApi->recordsOnly()->listDashboards();
112+
$mockClient = new MockClient([
113+
MockResponse::fixture('report/list_dashboards'),
114+
]);
115+
$reportApi = getAPI($mockClient)->getReportApi();
116+
$dashboards = $reportApi->listDashboards();
91117

92118
// Just verify the API returns an array (scratch org may not have dashboards)
93119
expect($dashboards)->toBeArray();

0 commit comments

Comments
 (0)