Skip to content

Commit

Permalink
feat: refactored tests setup
Browse files Browse the repository at this point in the history
  • Loading branch information
tabaktoni committed Oct 30, 2023
1 parent ecfbe19 commit a6be207
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 100 deletions.
81 changes: 16 additions & 65 deletions __tests__/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import fs from 'node:fs';
import path from 'node:path';

import { Account, ProviderInterface, RpcProvider, SequencerProvider, json } from '../src';
import { BaseUrl } from '../src/constants';
import {
CairoVersion,
CompiledSierra,
CompiledSierraCasm,
LegacyCompiledContract,
Expand Down Expand Up @@ -48,35 +46,12 @@ export const compiledC1v2Casm = readContractSierraCasm('cairo/helloCairo2/compil
export const compiledC210 = readContractSierra('cairo/cairo210/cairo210.sierra');
export const compiledC210Casm = readContractSierraCasm('cairo/cairo210/cairo210');

/* User defined config or default one */
const BASE_URL =
process.env.TEST_PROVIDER_BASE_URL || process.env.GS_DEFAULT_TEST_PROVIDER_URL || '';
const RPC_URL = process.env.TEST_RPC_URL;

/* Detect user defined node or sequencer, if none default to sequencer if both default to node */
const PROVIDER_URL = RPC_URL || BASE_URL;

/** explicit is local devnet (undefined,'','1','true','TRUE') = true else false */
const LOCAL_DEVNET = ['1', 'TRUE', ''].includes((process.env.LOCAL_DEVNET || '').toUpperCase());

/* Detect is localhost devnet, it can be also localhost RPC node */
export const IS_LOCALHOST_DEVNET =
LOCAL_DEVNET && (PROVIDER_URL.includes('localhost') || PROVIDER_URL.includes('127.0.0.1'));

export const IS_DEVNET_RPC = IS_LOCALHOST_DEVNET && PROVIDER_URL.includes('rpc');
export const IS_DEVNET_SEQUENCER = IS_LOCALHOST_DEVNET && !PROVIDER_URL.includes('rpc');

/* Definitions */
export const IS_RPC = !!RPC_URL;
export const IS_SEQUENCER = !RPC_URL;
export const IS_SEQUENCER_GOERLI = PROVIDER_URL.includes(BaseUrl.SN_GOERLI);

export const getTestProvider = (): ProviderInterface => {
const provider = RPC_URL
? new RpcProvider({ nodeUrl: RPC_URL })
: new SequencerProvider({ baseUrl: BASE_URL });
const provider = process.env.TEST_RPC_URL
? new RpcProvider({ nodeUrl: process.env.TEST_RPC_URL })
: new SequencerProvider({ baseUrl: process.env.TEST_PROVIDER_BASE_URL || '' });

if (IS_LOCALHOST_DEVNET) {
if (process.env.IS_LOCALHOST_DEVNET) {
// accelerate the tests when running locally
const originalWaitForTransaction = provider.waitForTransaction.bind(provider);
provider.waitForTransaction = (
Expand All @@ -90,46 +65,22 @@ export const getTestProvider = (): ProviderInterface => {
return provider;
};

// test account with fee token balance
export const getTestAccount = (provider: ProviderInterface) => {
let testAccountAddress = process.env.TEST_ACCOUNT_ADDRESS;
let testAccountPrivateKey = process.env.TEST_ACCOUNT_PRIVATE_KEY;

if (!IS_LOCALHOST_DEVNET) {
if (!testAccountPrivateKey) {
throw new Error('TEST_ACCOUNT_PRIVATE_KEY is not set');
}
if (!testAccountAddress) {
throw new Error('TEST_ACCOUNT_ADDRESS is not set');
}
} else if (!testAccountAddress || !testAccountPrivateKey) {
// use defaults for devnet only if they are not set

// TODO: refactor to retrieve from devnet's /predeployed_accounts endpoint
[testAccountAddress, testAccountPrivateKey] = IS_RPC
? [
'0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691',
'0x71d7bb07b9a64f6f78ac4c816aff4da9',
]
: [
'0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a',
'0xe3e70682c2094cac629f6fbed82c07cd',
];
}
const cairoVersion = (process.env.ACCOUNT_CAIRO_VERSION as CairoVersion) || '0';

return new Account(provider, toHex(testAccountAddress), testAccountPrivateKey, cairoVersion);
return new Account(
provider,
toHex(process.env.TEST_ACCOUNT_ADDRESS || ''),
process.env.TEST_ACCOUNT_PRIVATE_KEY || ''
);
};

const describeIf = (condition: boolean) => (condition ? describe : describe.skip);
export const describeIfSequencer = describeIf(IS_SEQUENCER);
export const describeIfRpc = describeIf(IS_RPC);
export const describeIfNotRpc = describeIf(!IS_RPC);
export const describeIfNotDevnet = describeIf(!IS_LOCALHOST_DEVNET);
export const describeIfDevnet = describeIf(IS_LOCALHOST_DEVNET);
export const describeIfDevnetRpc = describeIf(IS_DEVNET_RPC);
export const describeIfDevnetSequencer = describeIf(IS_DEVNET_SEQUENCER);
export const describeIfSequencerGoerli = describeIf(IS_SEQUENCER_GOERLI);
export const describeIfSequencer = describeIf(process.env.IS_SEQUENCER === 'true');
export const describeIfRpc = describeIf(process.env.IS_RPC === 'true');
export const describeIfNotDevnet = describeIf(process.env.IS_LOCALHOST_DEVNET === 'false');
export const describeIfDevnet = describeIf(process.env.IS_LOCALHOST_DEVNET === 'true');
export const describeIfDevnetRpc = describeIf(process.env.IS_RPC_DEVNET === 'true');
export const describeIfDevnetSequencer = describeIf(process.env.IS_SEQUENCER_DEVNET === 'true');
export const describeIfSequencerGoerli = describeIf(process.env.IS_SEQUENCER_GOERLI === 'true');

export const erc20ClassHash = '0x54328a1075b8820eb43caf0caa233923148c983742402dcfc38541dd843d01a';
export const wrongClassHash = '0x000000000000000000000000000000000000000000000000000000000000000';
229 changes: 197 additions & 32 deletions __tests__/jestGlobalSetup.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,231 @@
/* eslint-disable no-console */
/**
* Asynchronous Global Test Setup
* Run only once
* ref: order of execution jestGlobalSetup.ts -> jest.setup.ts -> fixtures.ts
*/

import { BaseUrl } from '../src/constants';

type DevnetStrategy = {
isDevnet: boolean;
isRS: boolean;
};
type ProviderType = {
sequencer: boolean;
rpc: boolean;
};

/**
* Global Setup Fixtures
*/

/* Default test config based on run `starknet-devnet --seed 0` */
const GS_DEFAULT_TEST_PROVIDER_URL = 'http://127.0.0.1:5050/';

/**
* We need to detect intention of the test runner
* Does test run on local devnet?
* Does it run Sequencer or RPC tests ?
*/
const localDevnetDetectionStrategy = async () => {
const setup = (strategy: DevnetStrategy) => {
// Setup ENV
process.env.IS_LOCALHOST_DEVNET = 'false';
process.env.IS_RPC_DEVNET = 'false';
process.env.IS_SEQUENCER_DEVNET = 'false';

if (strategy.isDevnet) {
process.env.IS_LOCALHOST_DEVNET = 'true';
if (strategy.isRS) {
process.env.IS_RPC_DEVNET = 'true';
} else {
process.env.IS_SEQUENCER_DEVNET = 'true';
}
}
return strategy;
};

const strategy: DevnetStrategy = {
isDevnet: false,
isRS: false,
};
// if is_alive work it is local devnet
try {
const response = await fetch(`${GS_DEFAULT_TEST_PROVIDER_URL}is_alive`);
const body = await response.text();
if (body === 'Alive!!!') {
strategy.isDevnet = true;
} else {
return setup(strategy);
}
} catch (error) {
return setup(strategy);
}
// if on base url RPC endpoint work it is devnet-rs else it devnet-py
try {
const response = await fetch(`${GS_DEFAULT_TEST_PROVIDER_URL}`, {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'starknet_syncing' }),
});
const json = await response.json();
strategy.isRS = json.jsonrpc === '2.0';
} catch (error) {
return setup(strategy);
}

const sequencerOrRpc = () => {
return setup(strategy);
};

const sequencerOrRpc = async (devnetStrategy?: DevnetStrategy) => {
const setup = (providerType: ProviderType) => {
process.env.IS_SEQUENCER = providerType.sequencer ? 'true' : 'false';
process.env.IS_RPC = providerType.rpc ? 'true' : 'false';
process.env.IS_SEQUENCER_GOERLI = (
process.env.TEST_PROVIDER_BASE_URL ||
process.env.TEST_RPC_URL ||
''
).includes(BaseUrl.SN_GOERLI)
? 'true'
: 'false';
return providerType;
};
let result: ProviderType = { sequencer: false, rpc: false };
if (process.env.TEST_PROVIDER_BASE_URL) {
return { sequencer: true, rpc: false };
return setup({ ...result, sequencer: true });
}
if (process.env.TEST_RPC_URL) {
return { sequencer: false, rpc: true };
return setup({ ...result, rpc: true });
}
// nor sequencer nor rpc provided, try with local devnet strategy
if (devnetStrategy && devnetStrategy.isDevnet) {
result = { sequencer: !devnetStrategy.isRS, rpc: devnetStrategy.isRS };
if (result.sequencer) {
process.env.TEST_PROVIDER_BASE_URL = GS_DEFAULT_TEST_PROVIDER_URL;
} else if (result.rpc) {
process.env.TEST_RPC_URL = GS_DEFAULT_TEST_PROVIDER_URL;
}
}
// nor sequencer nor rpc provided, try with local devnet detection
return { sequencer: false, rpc: false };
return setup(result);
};

const isAccountProvided = () => {
const setAccount = async (devnetStrategy: DevnetStrategy) => {
const fetchAccount = async (URL: string) => {
const response = await fetch(`${URL}predeployed_accounts`);
const accounts = await response.json();
process.env.TEST_ACCOUNT_ADDRESS = accounts[0].address;
process.env.TEST_ACCOUNT_PRIVATE_KEY = accounts[0].private_key;
process.env.INITIAL_BALANCE = accounts[0].initial_balance;
};

if (process.env.TEST_ACCOUNT_ADDRESS && process.env.TEST_ACCOUNT_PRIVATE_KEY) {
return true;
}
if (process.env.TEST_ACCOUNT_ADDRESS || process.env.TEST_ACCOUNT_PRIVATE_KEY) {
throw new Error(
'If you are providing test Account you need to provide both TEST_ACCOUNT_ADDRESS & TEST_ACCOUNT_PRIVATE_KEY'
'If you are providing one of you need to provide both: TEST_ACCOUNT_ADDRESS & TEST_ACCOUNT_PRIVATE_KEY'
);
}
return false;
const providedURL = process.env.TEST_PROVIDER_BASE_URL || process.env.TEST_RPC_URL;
if (devnetStrategy.isDevnet) {
// get account from devnet
try {
await fetchAccount(GS_DEFAULT_TEST_PROVIDER_URL);
return true;
} catch (error) {
console.error('Fetching account from devnet failed');
}
} else if (providedURL) {
// try to get it from remote devnet
try {
await fetchAccount(providedURL);
return true;
} catch (error) {
console.error(`Fetching account from provided url ${providedURL} failed`);
}
}

// if not provided try to get it from BASE_URL
// if unsecesfull throw an account error
throw new Error(
'Setting Account using all known strategies failed, please provide TEST_ACCOUNT_ADDRESS & TEST_ACCOUNT_PRIVATE_KEY'
);
};

const defineStrategy = async () => {
// TODO: implement strategy detection
// did user provide TEST_PROVIDER_BASE_URL or TEST_RPC_URL if so we know it is RPC or Sequencer test
// if not we need to aute detect it
// did user provide TEST_ACCOUNT_ADDRESS and TEST_ACCOUNT_PRIVATE_KEY
// - if yes, use provided account
// - if only one throw error
// - if no run local devnet strategy
// did user provide TEST_PROVIDER_BASE_URL or TEST_RPC_URL
// - is local devnet running?
// - if yes, use local devnet
// get local devnet version and accounts
sequencerOrRpc();
isAccountProvided();
const verifySetup = (final?: boolean) => {
if (!process.env.TEST_ACCOUNT_ADDRESS) {
if (final) {
throw new Error('TEST_ACCOUNT_ADDRESS env is not set');
} else return false;
}
if (!process.env.TEST_ACCOUNT_PRIVATE_KEY) {
if (final) {
throw new Error('TEST_ACCOUNT_PRIVATE_KEY env is not set');
} else return false;
}
if (!process.env.TEST_PROVIDER_BASE_URL && !process.env.TEST_RPC_URL) {
if (final) {
throw new Error('One of TEST_PROVIDER_BASE_URL or TEST_RPC_URL env is not set');
} else return false;
}

console.table({
TEST_ACCOUNT_ADDRESS: process.env.TEST_ACCOUNT_ADDRESS,
TEST_ACCOUNT_PRIVATE_KEY: '****',
INITIAL_BALANCE: process.env.INITIAL_BALANCE,
TEST_PROVIDER_BASE_URL: process.env.TEST_PROVIDER_BASE_URL,
TEST_RPC_URL: process.env.TEST_RPC_URL,
});

console.table({
IS_LOCALHOST_DEVNET: process.env.IS_LOCALHOST_DEVNET,
IS_RPC_DEVNET: process.env.IS_RPC_DEVNET,
IS_SEQUENCER_DEVNET: process.env.IS_SEQUENCER_DEVNET,
IS_RPC: process.env.IS_RPC,
IS_SEQUENCER: process.env.IS_SEQUENCER,
IS_SEQUENCER_GOERLI: process.env.IS_SEQUENCER_GOERLI,
});

console.log('Global Test Environment is Ready');
return true;
};

const executeStrategy = async () => {
// 1. Assume setup is provided and ready;
let ready = verifySetup();
if (ready) {
console.log('Using Provided Test Setup');
return true;
}

// 2. Try to detect setup
console.log('Global Test Environment Auto Setup Started');
const devnetStrategy = await localDevnetDetectionStrategy();
if (devnetStrategy.isDevnet) {
if (devnetStrategy.isRS) {
console.log('Detected Devnet-RS');
} else {
console.log('Detected Devnet-PY');
}
}

const providerType = await sequencerOrRpc(devnetStrategy);
if (providerType.sequencer) {
console.log('Detected Sequencer');
} else if (providerType.rpc) {
console.log('Detected RPC');
}

const isAccountSet = await setAccount(devnetStrategy);
if (isAccountSet) {
console.log('Detected Account');
}

ready = await verifySetup(true);
if (ready) {
return true;
}

return false;
};

module.exports = async function (_globalConfig: any, _projectConfig: any) {
const devnetAccs = await defineStrategy();
console.log(devnetAccs);
export default async (_globalConfig: any, _projectConfig: any) => {
const isSet = await executeStrategy();
if (!isSet) console.error('Test Setup Environment is NOT Ready');
// SET GLOBALS
process.env.GS_DEFAULT_TEST_PROVIDER_URL = GS_DEFAULT_TEST_PROVIDER_URL;
};
4 changes: 2 additions & 2 deletions __tests__/rpcProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ describeIfRpc('RPCProvider', () => {
expect(stateUpdate).toMatchSchemaRef('StateUpdateResponse');
});

test('getSpecVersion - pathfinder not implemented', async () => {
test('getSpecVersion', async () => {
const spec = await rpcProvider.getSpecVersion();
console.log(spec);
expect(typeof spec).toBe('string');
});

test('getCode - not implemented', async () => {
Expand Down
Loading

0 comments on commit a6be207

Please sign in to comment.