From 3349e68b1a0a9cfdf500a2422380dcadae218cd5 Mon Sep 17 00:00:00 2001 From: Shane McLaughlin Date: Wed, 4 Jan 2023 09:41:01 -0600 Subject: [PATCH] Sm/better trim (#741) * feat: better typing and easier exports from sfdc * test: more trimTo15 tests * refactor: pr feedback --- src/config/config.ts | 4 +- src/org/authInfo.ts | 8 +- src/org/connection.ts | 4 +- src/org/org.ts | 6 +- src/org/orgConfigProperties.ts | 4 +- src/org/user.ts | 6 +- src/sfProject.ts | 4 +- src/util/checkLightningDomain.ts | 4 +- src/util/sfdc.ts | 156 ++++++++++-------- .../org/scratchOrgSettingsGeneratorTest.ts | 4 +- test/unit/util/sfdcTest.ts | 79 ++++++++- 11 files changed, 181 insertions(+), 98 deletions(-) diff --git a/src/config/config.ts b/src/config/config.ts index d8c925cf97..4f41e40b57 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -12,7 +12,7 @@ import { Dictionary, ensure, isString, JsonPrimitive, Nullable } from '@salesfor import { Global } from '../global'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { sfdc } from '../util/sfdc'; +import { validateApiVersion } from '../util/sfdc'; import { SfdcUrl } from '../util/sfdcUrl'; import { ORG_CONFIG_ALLOWED_PROPERTIES, OrgConfigProperties } from '../org/orgConfigProperties'; import { ConfigFile } from './configFile'; @@ -221,7 +221,7 @@ export const SFDX_ALLOWED_PROPERTIES = [ hidden: true, input: { // If a value is provided validate it otherwise no value is unset. - validator: (value: ConfigValue): boolean => value == null || (isString(value) && sfdc.validateApiVersion(value)), + validator: (value: ConfigValue): boolean => value == null || (isString(value) && validateApiVersion(value)), failedMessage: messages.getMessage('invalidApiVersion'), }, }, diff --git a/src/org/authInfo.ts b/src/org/authInfo.ts index c04f702c96..d0240b064e 100644 --- a/src/org/authInfo.ts +++ b/src/org/authInfo.ts @@ -32,7 +32,7 @@ import { Config } from '../config/config'; import { ConfigAggregator } from '../config/configAggregator'; import { Logger } from '../logger'; import { SfError } from '../sfError'; -import { sfdc } from '../util/sfdc'; +import { matchesAccessToken, trimTo15 } from '../util/sfdc'; import { StateAggregator } from '../stateAggregator'; import { Messages } from '../messages'; import { getLoginAudienceCombos, SfdcUrl } from '../util/sfdcUrl'; @@ -443,7 +443,7 @@ export class AuthInfo extends AsyncOptionalCreatable { const devHubOrg = await Org.create({ aliasOrUsername: devHubUsername }); const conn = devHubOrg.getConnection(); const data = await conn.query>( - `select Id from ScratchOrgInfo where ScratchOrg = '${sfdc.trimTo15(scratchOrgId)}'` + `select Id from ScratchOrgInfo where ScratchOrg = '${trimTo15(scratchOrgId)}'` ); return data; } @@ -494,7 +494,7 @@ export class AuthInfo extends AsyncOptionalCreatable { this.update(authData); const username = ensure(this.getUsername()); - if (sfdc.matchesAccessToken(username)) { + if (matchesAccessToken(username)) { this.logger.debug('Username is an accesstoken. Skip saving authinfo to disk.'); return this; } @@ -711,7 +711,7 @@ export class AuthInfo extends AsyncOptionalCreatable { } // Else it will be set in initAuthOptions below. // If the username is an access token, use that for auth and don't persist - if (isString(oauthUsername) && sfdc.matchesAccessToken(oauthUsername)) { + if (isString(oauthUsername) && matchesAccessToken(oauthUsername)) { // Need to initAuthOptions the logger and authInfoCrypto since we don't call init() this.logger = await Logger.child('AuthInfo'); diff --git a/src/org/connection.ts b/src/org/connection.ts index 7a714fe6e9..35bf84fa4e 100644 --- a/src/org/connection.ts +++ b/src/org/connection.ts @@ -27,7 +27,7 @@ import { MyDomainResolver } from '../status/myDomainResolver'; import { ConfigAggregator } from '../config/configAggregator'; import { Logger } from '../logger'; import { SfError } from '../sfError'; -import { sfdc } from '../util/sfdc'; +import { validateApiVersion } from '../util/sfdc'; import { Messages } from '../messages'; import { Lifecycle } from '../lifecycleEvents'; import { AuthFields, AuthInfo } from './authInfo'; @@ -320,7 +320,7 @@ export class Connection extends JSForceConnection * @param version The API version. */ public setApiVersion(version: string): void { - if (!sfdc.validateApiVersion(version)) { + if (!validateApiVersion(version)) { throw messages.createError('incorrectAPIVersionError', [version]); } this.version = version; diff --git a/src/org/org.ts b/src/org/org.ts index ff2674e7bf..5668c607db 100644 --- a/src/org/org.ts +++ b/src/org/org.ts @@ -32,7 +32,7 @@ import { Global } from '../global'; import { Lifecycle } from '../lifecycleEvents'; import { Logger } from '../logger'; import { SfError } from '../sfError'; -import { sfdc } from '../util/sfdc'; +import { trimTo15 } from '../util/sfdc'; import { WebOAuthServer } from '../webOAuthServer'; import { Messages } from '../messages'; import { StateAggregator } from '../stateAggregator'; @@ -434,7 +434,7 @@ export class Org extends AsyncOptionalCreatable { const thisOrgAuthConfig = this.getConnection().getAuthInfoFields(); - const trimmedId: string = sfdc.trimTo15(thisOrgAuthConfig.orgId) as string; + const trimmedId = trimTo15(thisOrgAuthConfig.orgId); const DEV_HUB_SOQL = `SELECT CreatedDate,Edition,ExpirationDate FROM ActiveScratchOrg WHERE ScratchOrg='${trimmedId}'`; @@ -1092,7 +1092,7 @@ export class Org extends AsyncOptionalCreatable { } } catch { // if an error is thrown, don't panic yet. we'll try querying by orgId - const trimmedId = sfdc.trimTo15(this.getOrgId()) as string; + const trimmedId = trimTo15(this.getOrgId()); this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`); try { result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId); diff --git a/src/org/orgConfigProperties.ts b/src/org/orgConfigProperties.ts index dcd9c7bbd2..d809e69e7c 100644 --- a/src/org/orgConfigProperties.ts +++ b/src/org/orgConfigProperties.ts @@ -10,7 +10,7 @@ import { isString } from '@salesforce/ts-types'; import { ConfigValue } from '../config/configStore'; import { Messages } from '../messages'; import { SfdcUrl } from '../util/sfdcUrl'; -import { sfdc } from '../util/sfdc'; +import { validateApiVersion } from '../util/sfdc'; Messages.importMessagesDirectory(pathJoin(__dirname)); const messages = Messages.load('@salesforce/core', 'config', [ @@ -102,7 +102,7 @@ export const ORG_CONFIG_ALLOWED_PROPERTIES = [ hidden: true, input: { // If a value is provided validate it otherwise no value is unset. - validator: (value: ConfigValue): boolean => value == null || (isString(value) && sfdc.validateApiVersion(value)), + validator: (value: ConfigValue): boolean => value == null || (isString(value) && validateApiVersion(value)), failedMessage: messages.getMessage('invalidApiVersion'), }, }, diff --git a/src/org/user.ts b/src/org/user.ts index a74717c59a..542e1f4179 100644 --- a/src/org/user.ts +++ b/src/org/user.ts @@ -14,7 +14,7 @@ import { Logger } from '../logger'; import { Messages } from '../messages'; import { SecureBuffer } from '../crypto/secureBuffer'; import { SfError } from '../sfError'; -import { sfdc } from '../util/sfdc'; +import { matchesAccessToken, validateSalesforceId } from '../util/sfdc'; import { Connection } from './connection'; import { PermissionSetAssignment } from './permissionSetAssignment'; import { Org } from './org'; @@ -94,7 +94,7 @@ async function retrieveUserFields(logger: Logger, username: string): Promise { - if (!sfdc.validateSalesforceId(name)) { + if (!validateSalesforceId(name)) { const profileQuery = `SELECT Id FROM Profile WHERE name='${name}'`; const result = await connection.query<{ Id: string }>(profileQuery); if (result.records.length > 0) { diff --git a/src/sfProject.ts b/src/sfProject.ts index 10abe64f4c..719f047d53 100644 --- a/src/sfProject.ts +++ b/src/sfProject.ts @@ -17,7 +17,7 @@ import { SchemaValidator } from './schema/validator'; import { resolveProjectPath, resolveProjectPathSync, SFDX_PROJECT_JSON } from './util/internal'; import { SfError } from './sfError'; -import { sfdc } from './util/sfdc'; +import { findUpperCaseKeys } from './util/sfdc'; import { Messages } from './messages'; Messages.importMessagesDirectory(__dirname); @@ -392,7 +392,7 @@ export class SfProjectJson extends ConfigFile { private validateKeys(): void { // Verify that the configObject does not have upper case keys; throw if it does. Must be heads down camel case. - const upperCaseKey = sfdc.findUpperCaseKeys(this.toObject(), SfProjectJson.BLOCKLIST); + const upperCaseKey = findUpperCaseKeys(this.toObject(), SfProjectJson.BLOCKLIST); if (upperCaseKey) { throw coreMessages.createError('invalidJsonCasing', [upperCaseKey, this.getPath()]); } diff --git a/src/util/checkLightningDomain.ts b/src/util/checkLightningDomain.ts index 4ae600b496..d1116d5340 100644 --- a/src/util/checkLightningDomain.ts +++ b/src/util/checkLightningDomain.ts @@ -7,14 +7,14 @@ import { URL } from 'url'; import { Env, Duration } from '@salesforce/kit'; import { MyDomainResolver } from '../status/myDomainResolver'; -import { sfdc } from './sfdc'; +import { isInternalUrl } from './sfdc'; export default async function checkLightningDomain(url: string): Promise { const domain = `https://${/https?:\/\/([^.]*)/.exec(url)?.slice(1, 2).pop()}.lightning.force.com`; const quantity = new Env().getNumber('SFDX_DOMAIN_RETRY', 240) ?? 0; const timeout = new Duration(quantity, Duration.Unit.SECONDS); - if (sfdc.isInternalUrl(url) || timeout.seconds === 0) { + if (isInternalUrl(url) || timeout.seconds === 0) { return true; } diff --git a/src/util/sfdc.ts b/src/util/sfdc.ts index 9c7c7da95f..56a1797beb 100644 --- a/src/util/sfdc.ts +++ b/src/util/sfdc.ts @@ -9,82 +9,98 @@ import { findKey } from '@salesforce/kit'; import { AnyJson, asJsonMap, isJsonMap, JsonMap, Optional } from '@salesforce/ts-types'; import { SfdcUrl } from './sfdcUrl'; -export const sfdc = { - /** - * Converts an 18 character Salesforce ID to 15 characters. - * - * @param id The id to convert. - */ - trimTo15: (id?: string): Optional => { - if (id?.length && id.length > 15) { - id = id.substring(0, 15); - } - return id; - }, +/** + * Converts an 18 character Salesforce ID to 15 characters. + * + * @param id The id to convert. + */ +export function trimTo15(id: string): string; +export function trimTo15(id?: undefined): undefined; +export function trimTo15(id: string | undefined): string | undefined; +export function trimTo15(id: string | undefined): string | undefined { + if (!id) { + return undefined; + } + if (id.length && id.length > 15) { + id = id.substring(0, 15); + } + return id; +} - /** - * Tests whether an API version matches the format `i.0`. - * - * @param value The API version as a string. - */ - validateApiVersion: (value: string): boolean => value == null || /^[1-9]\d\.0$/.test(value), +/** + * Tests whether an API version matches the format `i.0`. + * + * @param value The API version as a string. + */ +export const validateApiVersion = (value: string): boolean => value == null || /^[1-9]\d\.0$/.test(value); - /** - * Tests whether an email matches the format `me@my.org` - * - * @param value The email as a string. - */ - validateEmail: (value: string): boolean => /^[^.][^@]*@[^.]+(\.[^.\s]+)+$/.test(value), +/** + * Tests whether an email matches the format `me@my.org` + * + * @param value The email as a string. + */ +export const validateEmail = (value: string): boolean => /^[^.][^@]*@[^.]+(\.[^.\s]+)+$/.test(value); + +/** + * Tests whether a given url is an internal Salesforce domain + * + * @param url + */ +export const isInternalUrl = (url: string): boolean => new SfdcUrl(url).isInternalUrl(); - /** - * Tests whether a Salesforce ID is in the correct format, a 15- or 18-character length string with only letters and numbers - * - * @param value The ID as a string. - */ - validateSalesforceId: (value: string): boolean => - /[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}/.test(value) && (value.length === 15 || value.length === 18), +/** + * Tests whether a Salesforce ID is in the correct format, a 15- or 18-character length string with only letters and numbers + * + * @param value The ID as a string. + */ +export const validateSalesforceId = (value: string): boolean => + /[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}/.test(value) && (value.length === 15 || value.length === 18); - /** - * Tests whether a path is in the correct format; the value doesn't include the characters "[", "]", "?", "<", ">", "?", "|" - * - * @param value The path as a string. - */ - validatePathDoesNotContainInvalidChars: (value: string): boolean => - // eslint-disable-next-line no-useless-escape - !/[\["\?<>\|\]]+/.test(value), - /** - * Returns the first key within the object that has an upper case first letter. - * - * @param data The object in which to check key casing. - * @param sectionBlocklist properties in the object to exclude from the search. e.g. a blocklist of `["a"]` and data of `{ "a": { "B" : "b"}}` would ignore `B` because it is in the object value under `a`. - */ - findUpperCaseKeys: (data?: JsonMap, sectionBlocklist: string[] = []): Optional => { - let key: Optional; - findKey(data, (val: AnyJson, k: string) => { - if (k.substr(0, 1) === k.substr(0, 1).toUpperCase()) { - key = k; - } else if (isJsonMap(val)) { - if (sectionBlocklist.includes(k)) { - return key; - } - key = sfdc.findUpperCaseKeys(asJsonMap(val)); +/** + * Tests whether a path is in the correct format; the value doesn't include the characters "[", "]", "?", "<", ">", "?", "|" + * + * @param value The path as a string. + */ +export const validatePathDoesNotContainInvalidChars = (value: string): boolean => + // eslint-disable-next-line no-useless-escape + !/[\["\?<>\|\]]+/.test(value); +/** + * Returns the first key within the object that has an upper case first letter. + * + * @param data The object in which to check key casing. + * @param sectionBlocklist properties in the object to exclude from the search. e.g. a blocklist of `["a"]` and data of `{ "a": { "B" : "b"}}` would ignore `B` because it is in the object value under `a`. + */ +export const findUpperCaseKeys = (data?: JsonMap, sectionBlocklist: string[] = []): Optional => { + let key: Optional; + findKey(data, (val: AnyJson, k: string) => { + if (/^[A-Z]/.test(k)) { + key = k; + } else if (isJsonMap(val)) { + if (sectionBlocklist.includes(k)) { + return key; } - return key; - }); + key = findUpperCaseKeys(asJsonMap(val)); + } return key; - }, + }); + return key; +}; - /** - * Tests whether a given string is an access token - * - * @param value - */ - matchesAccessToken: (value: string): boolean => /^(00D\w{12,15})![.\w]*$/.test(value), +/** + * Tests whether a given string is an access token + * + * @param value + */ +export const matchesAccessToken = (value: string): boolean => /^(00D\w{12,15})![.\w]*$/.test(value); - /** - * Tests whether a given url is an internal Salesforce domain - * - * @param url - */ - isInternalUrl: (url: string): boolean => new SfdcUrl(url).isInternalUrl(), +/** @deprecated import the individual functions instead of the whole object */ +export const sfdc = { + trimTo15, + validateApiVersion, + validateEmail, + isInternalUrl, + matchesAccessToken, + validateSalesforceId, + validatePathDoesNotContainInvalidChars, + findUpperCaseKeys, }; diff --git a/test/unit/org/scratchOrgSettingsGeneratorTest.ts b/test/unit/org/scratchOrgSettingsGeneratorTest.ts index 18fd4333f8..dd54f24dda 100644 --- a/test/unit/org/scratchOrgSettingsGeneratorTest.ts +++ b/test/unit/org/scratchOrgSettingsGeneratorTest.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import { expect } from 'chai'; import { Org, Connection } from '../../../src/org'; -import { sfdc } from '../../../src/util/sfdc'; +import { validateApiVersion } from '../../../src/util/sfdc'; import { ZipWriter } from '../../../src/util/zipWriter'; import { ScratchOrgInfo } from '../../../src/org/scratchOrgTypes'; import SettingsGenerator, { @@ -70,7 +70,7 @@ const fakeConnection = ( ({ setApiVersion: (apiVersion: string) => { expect(apiVersion).to.be.a('string').and.length.to.be.greaterThan(0); - expect(sfdc.validateApiVersion(apiVersion)).to.not.be.undefined; + expect(validateApiVersion(apiVersion)).to.not.be.undefined; }, deploy: (zipInput: Buffer) => { expect(zipInput).to.have.property('length').and.to.be.greaterThan(0); diff --git a/test/unit/util/sfdcTest.ts b/test/unit/util/sfdcTest.ts index 32602703f6..3b7d8e5fa7 100644 --- a/test/unit/util/sfdcTest.ts +++ b/test/unit/util/sfdcTest.ts @@ -5,77 +5,116 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import { expect } from 'chai'; -import { sfdc } from '../../../src/util/sfdc'; +import { + findUpperCaseKeys, + matchesAccessToken, + sfdc, + trimTo15, + validateApiVersion, + validateEmail, + validatePathDoesNotContainInvalidChars, + validateSalesforceId, +} from '../../../src/util/sfdc'; describe('util/sfdc', () => { - it('should trim an 18 character id to 15 characters', () => { - const id: string = sfdc.trimTo15('ABCDEFGHIJKLMNOPQR'); - const trimmed = sfdc.trimTo15(id); - expect(trimmed.length).to.eq(15); - expect(trimmed.endsWith('O')).to.be.true; + describe('trimTo15', () => { + it('should trim an 18 character id to 15 characters', () => { + const id = sfdc.trimTo15('ABCDEFGHIJKLMNOPQR'); + const trimmed = sfdc.trimTo15(id); + expect(trimmed.length).to.eq(15); + expect(trimmed.endsWith('O')).to.be.true; + }); + it('should trim an 18 character id to 15 characters', () => { + const id = trimTo15('ABCDEFGHIJKLMNOPQR'); + const trimmed = trimTo15(id); + expect(trimmed.length).to.eq(15); + expect(trimmed.endsWith('O')).to.be.true; + }); + it('returns undefined when not passed a string', () => { + expect(trimTo15()).to.be.undefined; + expect(trimTo15(undefined)).to.be.undefined; + }); + it('returns same id when 15 char or less', () => { + expect(trimTo15('ABCDEFGHIJKLMNO')).to.equal('ABCDEFGHIJKLMNO'); + expect(trimTo15('ABC')).to.equal('ABC'); + }); }); describe('validateApiVersion', () => { it('should return true for "42.0"', () => { expect(sfdc.validateApiVersion('42.0')).to.be.true; + expect(validateApiVersion('42.0')).to.be.true; }); it('should return false for "42"', () => { expect(sfdc.validateApiVersion('42')).to.be.false; + expect(validateApiVersion('42')).to.be.false; }); it('should return false for 42.0', () => { const num = 42.0; expect(sfdc.validateApiVersion(num as never)).to.be.false; + expect(validateApiVersion(num as never)).to.be.false; }); }); describe('validateEmail', () => { it('should return true for "me@my.org"', () => { expect(sfdc.validateEmail('me@my.org')).to.be.true; + expect(validateEmail('me@my.org')).to.be.true; }); it('should return false for "me@my."', () => { expect(sfdc.validateEmail('me@my.')).to.be.false; + expect(validateEmail('me@my.')).to.be.false; }); it('should return false for "@my.com"', () => { expect(sfdc.validateEmail('@my')).to.be.false; + expect(validateEmail('@my')).to.be.false; }); }); describe('validateSalesforceId', () => { it('should return true for "00DB0000003uuuuuuu"', () => { expect(sfdc.validateSalesforceId('00DB0000003uuuuuuu')).to.be.true; + expect(validateSalesforceId('00DB0000003uuuuuuu')).to.be.true; }); it('should return false for "00D"', () => { expect(sfdc.validateSalesforceId('00D')).to.be.false; + expect(validateSalesforceId('00D')).to.be.false; }); it('should return false for "00D***11100000K"', () => { expect(sfdc.validateSalesforceId('00D***11100000K')).to.be.false; + expect(validateSalesforceId('00D***11100000K')).to.be.false; }); }); describe('validatePathDoesNotContainInvalidChars', () => { it('should return true for "/this/is/my/path"', () => { expect(sfdc.validatePathDoesNotContainInvalidChars('/this/is/my/path')).to.be.true; + expect(validatePathDoesNotContainInvalidChars('/this/is/my/path')).to.be.true; }); it('should return false for "this/is/path??"', () => { expect(sfdc.validatePathDoesNotContainInvalidChars('this/is/path??')).to.be.false; + expect(validatePathDoesNotContainInvalidChars('this/is/path??')).to.be.false; }); it('should return false for "[this/is/path]"', () => { expect(sfdc.validatePathDoesNotContainInvalidChars('[this/is/path]')).to.be.false; + expect(validatePathDoesNotContainInvalidChars('[this/is/path]')).to.be.false; }); it('should return false for "/my/path > err.log"', () => { expect(sfdc.validatePathDoesNotContainInvalidChars('/my/path > err.log')).to.be.false; + expect(validatePathDoesNotContainInvalidChars('/my/path > err.log')).to.be.false; }); it('should return true for "c:\\myfile"', () => { expect(sfdc.validatePathDoesNotContainInvalidChars('c:\\myfile')).to.be.true; + expect(validatePathDoesNotContainInvalidChars('c:\\myfile')).to.be.true; }); }); @@ -87,6 +126,7 @@ describe('util/sfdc', () => { nested: { camelCase: true }, }; expect(sfdc.findUpperCaseKeys(testObj)).to.equal('UpperCase'); + expect(findUpperCaseKeys(testObj)).to.equal('UpperCase'); }); it('should return the first nested upper case key', () => { @@ -96,6 +136,7 @@ describe('util/sfdc', () => { nested: { NestedUpperCase: true }, }; expect(sfdc.findUpperCaseKeys(testObj)).to.equal('NestedUpperCase'); + expect(findUpperCaseKeys(testObj)).to.equal('NestedUpperCase'); }); it('should return undefined when no upper case key is found', () => { @@ -105,6 +146,7 @@ describe('util/sfdc', () => { nested: { camelCase: true }, }; expect(sfdc.findUpperCaseKeys(testObj)).to.be.undefined; + expect(findUpperCaseKeys(testObj)).to.be.undefined; }); it('should return the first nested upper case key unless blocklisted', () => { @@ -114,6 +156,25 @@ describe('util/sfdc', () => { nested: { NestedUpperCase: true }, }; expect(sfdc.findUpperCaseKeys(testObj, ['nested'])).to.equal(undefined); + expect(findUpperCaseKeys(testObj, ['nested'])).to.equal(undefined); + }); + + it('handles keys starting with numbers', () => { + const testObj = { + '1abc': true, + Abc: false, + nested: { '2abc': true }, + }; + expect(sfdc.findUpperCaseKeys(testObj)).to.equal('Abc'); + expect(findUpperCaseKeys(testObj)).to.equal('Abc'); + }); + it('handles keys starting with numbers', () => { + const testObj = { + '1abc': true, + nested: { '2abc': true, Upper: false }, + }; + expect(sfdc.findUpperCaseKeys(testObj)).to.equal('Upper'); + expect(findUpperCaseKeys(testObj)).to.equal('Upper'); }); }); @@ -124,9 +185,15 @@ describe('util/sfdc', () => { '00D0t000000HkBf!AQ8AQAuHh7lXOFdOA202PMQuGflRrtUkVIfSNK1BrWLlJTJuvypx3r8dLONoJdniYKap1nsTlbxRbbGDqT6r2Rze_Ii5no2y' ) ).to.equal(true); + expect( + matchesAccessToken( + '00D0t000000HkBf!AQ8AQAuHh7lXOFdOA202PMQuGflRrtUkVIfSNK1BrWLlJTJuvypx3r8dLONoJdniYKap1nsTlbxRbbGDqT6r2Rze_Ii5no2y' + ) + ).to.equal(true); }); it('should return false for an invalid access token', () => { expect(sfdc.matchesAccessToken('iamjustaregularusername@example.com')).to.equal(false); + expect(matchesAccessToken('iamjustaregularusername@example.com')).to.equal(false); }); }); });