Skip to content

Commit

Permalink
PINDP-1808: comments for unit tests (#134)
Browse files Browse the repository at this point in the history
Co-authored-by: David Chaiken <[email protected]>
  • Loading branch information
davidchaiken and David Chaiken authored Nov 1, 2024
1 parent d519d12 commit e939fd3
Show file tree
Hide file tree
Showing 38 changed files with 143 additions and 22 deletions.
7 changes: 7 additions & 0 deletions nodejs/Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# Try to keep the make targets below in sync with the other Makefiles in this repo.

# Run the unit tests
tests:
jest

# Verify that the prerequisites for the end-to-end tests are met.
e2e_setup:
@echo end to end tests set up...
../common/scripts/e2e_tests_prerequisites

# Run the end-to-end tests.
e2e_tests: e2e_setup
@echo end to end tests...
../common/scripts/e2e_tests_read
../common/scripts/e2e_tests_write
../common/scripts/e2e_tests_ads

# Run the linter to verify the code.
lint:
DEBUG=eslint:cli-engine yarn run eslint src scripts

# Run the linter to verify the code and fix any issues.
lint-fix:
yarn run eslint --fix --format summary-chart src scripts

Expand Down
15 changes: 15 additions & 0 deletions nodejs/src/access_token.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,21 @@ describe('v5 access_token tests', () => {
process.env = SAVED_ENV;
});

// test all of the ways that access tokens can be fetched

test('access token from environment', async() => {
const mock_api_config = jest.fn();
mock_api_config.app_id = 'test-app-id';
mock_api_config.app_secret = 'test-app-secret';
mock_api_config.oauth_token_dir = 'test-token-dir';

// The name of the access token is converted to upper case to find
// it in the process environment.
process.env.ACCESS_TOKEN_FROM_ENV = 'access token 42';
const access_token = new AccessToken(mock_api_config,
{ name: 'access_token_from_env' });
await access_token.fetch({});
// verify that the hash is the correct SHA256 value
// echo -n 'access token 42' | shasum -a 256
expect('553c1f363497ba07fecc989425e57e37c2b5f57ff7476c79dfd580ef0741db88')
.toEqual(access_token.hashed());
Expand All @@ -52,14 +57,17 @@ describe('v5 access_token tests', () => {
// check output
console.log = jest.fn();

// Test access token JSON file read
fs.readFileSync.mockReturnValue(access_token_json);
const access_token = new AccessToken(mock_api_config,
{ name: 'access_token_from_file' });
await access_token.fetch({});
// verify that the hash is the correct SHA256 value
// echo -n 'test access token from json' | shasum -a 256
expect('8de299eafa6932d8be18d7ff053d3bc6361c2b66ae1922f55fbf390d42de4cf6')
.toEqual(access_token.hashed());

// verify that the hash is the correct SHA256 value
// echo -n 'test refresh token from json' | shasum -a 256
expect('15569cfd5a27881329e842dfea303e05ec60c99fbdebcdaa20d2445647297072')
.toEqual(access_token.hashed_refresh_token());
Expand All @@ -71,6 +79,7 @@ describe('v5 access_token tests', () => {
callback(null, 42);
});

// Test access token JSON file write
fs.write.mockImplementation((fd, json, callback) => {
expect(42).toEqual(fd);
expect(access_token_json).toEqual(json);
Expand All @@ -84,6 +93,7 @@ describe('v5 access_token tests', () => {
]);
});

// Test the OAuth process for getting an access token
test('v5 access_token oauth user token', async() => {
const mock_api_config = jest.fn();
mock_api_config.app_id = 'test-app-id';
Expand Down Expand Up @@ -111,6 +121,7 @@ describe('v5 access_token tests', () => {

const read_scopes = [Scope.READ_USERS, Scope.READ_PINS];
await access_token.oauth({ scopes: read_scopes });
// verify the POST call and the response
expect(get_auth_code.mock.calls[0][0]).toBe(mock_api_config);
expect(get_auth_code.mock.calls[0][1]).toEqual({ scopes: read_scopes });
expect(got.post.mock.calls[0][0]).toEqual('test-api-uri/v5/oauth/token');
Expand All @@ -137,6 +148,7 @@ describe('v5 access_token tests', () => {
]);
});

// verify OAuth with client credentials
test('v5 access_token oauth client token', async() => {
const mock_api_config = jest.fn();
mock_api_config.app_id = 'test-app-id';
Expand Down Expand Up @@ -184,6 +196,7 @@ describe('v5 access_token tests', () => {
]);
});

// verify that the refresh token is not updated when it is not returned
test('v5 access_token refresh without refresh_token', async() => {
const mock_api_config = jest.fn();
mock_api_config.app_id = 'test-app-id';
Expand Down Expand Up @@ -239,6 +252,8 @@ describe('v5 access_token tests', () => {
]);
});

// verify that the refresh token is not updated when it is returned
// "chained refresh" refers to getting a new refresh token
test('v5 access_token refresh with chained refresh_token', async() => {
const mock_api_config = jest.fn();
mock_api_config.app_id = 'test-app-id';
Expand Down
2 changes: 2 additions & 0 deletions nodejs/src/ad_metrics_async_report.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('ad_metrics_async_report_common tests', () => {
jest.clearAllMocks();
});

// Test all of the common attributes that can be set on an AdMetricsAsyncReport
test('ad async report attributes', async() => {
const ad_async_report = new AdMetricsAsyncReport(
'test_api_config', 'test_access_token', 'test_advertiser_id')
Expand Down Expand Up @@ -51,6 +52,7 @@ describe('ad_metrics_async_report_common tests', () => {
);
});

// Make sure that errors in attributes throw the appropriate exceptions
test('ad metrics async report attribute errors', async() => {
const ad_async_report = new AdMetricsAsyncReport(
'test_api_config', 'test_access_token', 'test_advertiser_id')
Expand Down
1 change: 1 addition & 0 deletions nodejs/src/advertisers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('v5 advertisers tests', () => {
jest.clearAllMocks();
});

// Test the various kinds of ad objects that can be retrieved from the API
test('v5 advertisers', async() => {
const adv = new Advertisers('test_user', 'test_api_config', 'test_access_token');
expect(ApiObject.mock.instances.length).toBe(1);
Expand Down
4 changes: 4 additions & 0 deletions nodejs/src/analytics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('v5 analytics tests', () => {
jest.clearAllMocks();
});

// Test requests for synchronous user (organic) reports
test('v5 user analytics', async() => {
const analytics = new UserAnalytics(
'test_user_id', 'test_api_config', 'test_access_token')
Expand All @@ -24,6 +25,7 @@ describe('v5 analytics tests', () => {
const mock_request_data = jest.spyOn(ApiObject.prototype, 'request_data');
mock_request_data.mockResolvedValue('test_response');

// do the GET request with reference to an ad account
expect(await analytics.get('test_ad_account'))
.toEqual('test_response');

Expand Down Expand Up @@ -51,6 +53,7 @@ start_date=2021-03-01&end_date=2021-03-31\
&split_field=PIN_FORMAT');
});

// Test requests for synchronous Pin (organic) reports
test('v5 pin analytics', async() => {
const analytics = new PinAnalytics(
'test_pin_id', 'test_api_config', 'test_access_token')
Expand Down Expand Up @@ -89,6 +92,7 @@ start_date=2021-03-01&end_date=2021-03-31\
&app_types=WEB&split_field=NO_SPLIT');
});

// Test requests for synchronous Ad reports on various kinds of ad objects
test('v5 ads analytics', async() => {
const analytics = new AdAnalytics('test_api_config', 'test_access_token')
.start_date('2021-03-01')
Expand Down
1 change: 1 addition & 0 deletions nodejs/src/analytics_attributes.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AnalyticsAttributes, AdAnalyticsAttributes } from './analytics_attributes.js';

// Test all of the different kinds of analytics attributes, including error cases
describe('analytics_attributes tests', () => {
test('analytics attributes', () => {
const attributes = new AnalyticsAttributes();
Expand Down
3 changes: 3 additions & 0 deletions nodejs/src/api_common.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Common code for all API calls

// Errors to handle Pinterest API use cases
export class SpamError extends Error {
constructor(message) {
super(message);
Expand Down
4 changes: 4 additions & 0 deletions nodejs/src/api_common.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ApiCommon, RateLimitError, RequestFailedError, SpamError } from './api_common';

describe('api_common tests', () => {
// verify that typical output is correct
test('print_response', () => {
// check output
console.log = jest.fn();
Expand All @@ -15,6 +16,7 @@ describe('api_common tests', () => {
mock_response.body = 'test-response-body';
api_common.print_response(mock_response);

// verify request identifier printed at high verbosity
mock_api_config.verbosity = 3;
api_common.print_response(mock_response);

Expand All @@ -26,6 +28,7 @@ describe('api_common tests', () => {
]);
});

// verify output for different kinds of errors
test('print_and_throw_error', () => {
// check output
console.log = jest.fn();
Expand Down Expand Up @@ -85,6 +88,7 @@ describe('api_common tests', () => {
[error_message]
]);

// test situations where the schema of the response is not as expected
expect(() => {
api_common.print_and_throw_error(undefined);
}).toThrowError(new RequestFailedError('unknown error'));
Expand Down
3 changes: 3 additions & 0 deletions nodejs/src/api_config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ describe('ApiConfig test environment', () => {
process.env = SAVED_ENV;
});

// Verify the default API configuration and that the configuration
// can be set from the environment.

test('API configuration from defaults', () => {
// check output
console.log = jest.fn();
Expand Down
15 changes: 7 additions & 8 deletions nodejs/src/api_media_object.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { ApiMediaObject } from './api_media_object.js';
import fs from 'fs';

// Media upload uses the standard fs (file system) methods,
// so fs needs to be mocked to test this functionality.
jest.mock('fs');

// Need to mock the standard FormData object to test media upload.
const mockFormAppend = jest.fn();
const mockFormSubmit = jest.fn();
jest.mock('form-data', () => {
Expand All @@ -14,23 +17,18 @@ jest.mock('form-data', () => {
});
});

/*
* Tests for the generic ApiObject class that is used for all versions
* of the Pinterest API.
*
* Note: the reset_backoff and wait_backoff functions are tested with
* the classes that call these functions instead of testing them in
* this file.
*/
// Tests for functionality related to media upload.
describe('api_media_object tests', () => {
test('upload_media', async() => {
const api_media_object = new ApiMediaObject(jest.fn(), jest.fn());

// verify that the object forces upload_media to be overridden
await expect(async() => {
await api_media_object.upload_media('test_media');
}).rejects.toThrowError(new Error('upload_media() must be overridden'));
});

// Verify the different kinds of values for media id and related error cases.
test('media_to_media_id', async() => {
const api_media_object = new ApiMediaObject(jest.fn(), jest.fn());

Expand Down Expand Up @@ -72,6 +70,7 @@ describe('api_media_object tests', () => {
}).rejects.toThrowError(new Error('invalid media: -314159'));
});

// Verify that upload calls the standard fs methods as expected.
test('upload_file_multipart', async() => {
const api_config = jest.fn();
api_config.credentials_warning = jest.fn();
Expand Down
2 changes: 2 additions & 0 deletions nodejs/src/api_object.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('api_object tests', () => {
.toBe('hello?good=bye&cruel=world&and=farewell');
});

// Verify basic object creation and usage.
test('v5 api_object', async() => {
const mock_api_config = jest.fn();
mock_api_config.api_uri = 'test_uri';
Expand All @@ -78,6 +79,7 @@ describe('api_object tests', () => {
expect(response).toEqual('test_response_data');
});

// Verify that generic bookmark functionality works.
test('v5 api_object_iterator', async() => {
const mock_api_config = jest.fn();
mock_api_config.api_uri = 'test_uri';
Expand Down
2 changes: 2 additions & 0 deletions nodejs/src/async_report.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('async report tests', () => {
jest.clearAllMocks();
});

// Verify the basic request_report / wait_report process.
test('async report methods', async() => {
const mock_constructor = jest.spyOn(ApiObject.prototype, 'constructor');
const test_report1 = new AsyncReport(
Expand Down Expand Up @@ -36,6 +37,7 @@ describe('async report tests', () => {
]]);
});

// Verify that exponential backoff works when waiting for a report.
test('async report run', async() => {
const test_report2_url = '\
test_report2_url/x-y-z/metrics_report.txt?Very-long-credentials-string';
Expand Down
2 changes: 2 additions & 0 deletions nodejs/src/board.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ describe('v5 board tests', () => {
jest.clearAllMocks();
});

// Verify all of the methods of the Board class.
test('v5 board methods methods', async() => {
const test_board = new Board('test_board_id', 'test_api_config', 'test_access_token');
expect(ApiObject.mock.instances.length).toBe(1);
expect(ApiObject.mock.calls[0]).toEqual(['test_api_config', 'test_access_token']);

// see the comment in user.test.js for an explanation of why spyOn is used here
const mock_request_data = jest.spyOn(ApiObject.prototype, 'request_data');
mock_request_data.mockResolvedValue('test_response');

Expand Down
1 change: 1 addition & 0 deletions nodejs/src/delivery_metrics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('delivery_metrics tests', () => {
jest.clearAllMocks();
});

// Verify that delivery metrics are fetched and printed correctly.
test('delivery metrics', async() => {
const test_dm = new DeliveryMetrics('test_api_config', 'test_access_token');
expect(ApiObject.mock.instances.length).toBe(1);
Expand Down
9 changes: 6 additions & 3 deletions nodejs/src/pin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ describe('v5 pin tests', () => {
expect(ApiMediaObject.mock.instances.length).toBe(1);
expect(ApiMediaObject.mock.calls[0]).toEqual(['test_api_config', 'test_access_token']);

// see the comment in user.test.js for an explanation of why spyOn is used here
const mock_request_data = jest.spyOn(ApiMediaObject.prototype, 'request_data');
mock_request_data.mockResolvedValue('test_response');

// test Pin retrieval
let response = await test_pin.get();
expect(mock_request_data.mock.calls[0][0]).toEqual('/v5/pins/test_pin_id');
expect(response).toEqual('test_response');
Expand All @@ -34,7 +36,7 @@ describe('v5 pin tests', () => {
}
};

// create pin without a section
// create Pin without a section
const mock_post_data = jest.spyOn(ApiMediaObject.prototype, 'post_data');
const created_data = { id: 'created_pin_id' };
mock_post_data.mockResolvedValue(created_data);
Expand Down Expand Up @@ -198,14 +200,14 @@ describe('v5 pin tests', () => {
expect(test_pin.pin_id).toEqual('created_pin_id');

await expect(async() => {
// second call to create
// second call to create results in an exception
await test_pin.create(pin_data, 'test_board_id', { media: 'file_name' });
}).rejects.toThrowError(
new Error('media upload 12345 failed')
);

await expect(async() => {
// third call to create
// third call to create results in an exception due to no status
await test_pin.create(pin_data, 'test_board_id', { media: 'file_name' });
}).rejects.toThrowError(
new Error('media upload 314159265 not found')
Expand Down Expand Up @@ -248,6 +250,7 @@ describe('v5 pin tests', () => {
]);
});

// verify the logic used to upload a media file
test('v5 upload media', async() => {
const test_pin = new Pin('test_pin_id', 'test_api_config', 'test_access_token');

Expand Down
2 changes: 2 additions & 0 deletions nodejs/src/terms.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ describe('v5 terms tests', () => {
jest.clearAllMocks();
});

// Verify the get and print methods provided for related terms

test('v5 related terms methods', async() => {
const test_terms = new Terms('test_api_config', 'test_access_token');
expect(ApiObject.mock.instances.length).toBe(1);
Expand Down
Loading

0 comments on commit e939fd3

Please sign in to comment.