-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: client sdk - e2e test setup with random wallet (#173)
- Loading branch information
Showing
35 changed files
with
611 additions
and
306 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
CLIENT_TEST_WALLET_PRIVATE_KEY= | ||
TESTING_ENV_URL= | ||
TESTING_HEADER_KEY= | ||
TESTING_HEADER_VALUE= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { invariant } from '@lens-protocol/shared-kernel'; | ||
import * as dotenv from 'dotenv'; | ||
|
||
import { Environment } from '../consts/environments'; | ||
|
||
dotenv.config(); | ||
|
||
export const buildTestEnvironment = (): Environment => { | ||
invariant(process.env.TESTING_ENV_URL, 'TESTING_ENV_URL is not defined in .env file'); | ||
|
||
return new Environment('testing', process.env.TESTING_ENV_URL); | ||
}; |
226 changes: 226 additions & 0 deletions
226
packages/client/src/__helpers__/describeAuthenticatedScenario.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
/* eslint-disable no-console */ | ||
import { invariant, never } from '@lens-protocol/shared-kernel'; | ||
import { Wallet } from 'ethers'; | ||
|
||
import { Authentication } from '../authentication'; | ||
import { Profile } from '../profile'; | ||
import { isRelayerResult, Transaction } from '../transaction'; | ||
import { buildTestEnvironment } from './buildTestEnvironment'; | ||
import { signAndBroadcast } from './signAndBroadcast'; | ||
|
||
const testConfig = { | ||
environment: buildTestEnvironment(), | ||
}; | ||
|
||
export type TestSetup = { | ||
authentication: Authentication; | ||
profileId: string; | ||
wallet: Wallet; | ||
walletAddress: string; | ||
}; | ||
|
||
export type SetupOptions = { | ||
withNewProfile?: boolean; | ||
withDispatcher?: boolean; | ||
}; | ||
|
||
const defaultOptions: SetupOptions = { | ||
withNewProfile: false, | ||
withDispatcher: false, | ||
}; | ||
|
||
type GetTestSetupFn = () => TestSetup; | ||
|
||
export const describeAuthenticatedScenario = | ||
(options?: SetupOptions) => (callback: (f: GetTestSetupFn) => void) => { | ||
const { withNewProfile, withDispatcher } = { ...defaultOptions, ...options }; | ||
|
||
invariant( | ||
!(withDispatcher && !withNewProfile), | ||
'Wrong SetupOptions: dispatcher can only be added together with profile', | ||
); | ||
|
||
const testHandle = Date.now().toString(); | ||
const wallet = Wallet.createRandom(); | ||
|
||
const authentication = new Authentication(testConfig); | ||
const profile = new Profile(testConfig, authentication); | ||
const transaction = new Transaction(testConfig, authentication); | ||
|
||
let _walletAddress: string; | ||
let _testProfileId: string; | ||
|
||
beforeAll(async () => { | ||
// authenticate | ||
const address = await wallet.getAddress(); | ||
const challenge = await authentication.generateChallenge(address); | ||
const signature = await wallet.signMessage(challenge); | ||
await authentication.authenticate(address, signature); | ||
|
||
// create a new profile | ||
if (withNewProfile) { | ||
await createProfile({ | ||
handle: testHandle, | ||
walletAddress: address, | ||
profile, | ||
transaction, | ||
}); | ||
} | ||
|
||
// find test profileId | ||
const testProfileId = withNewProfile | ||
? await findProfileId({ | ||
handle: testHandle, | ||
walletAddress: address, | ||
profile, | ||
}) | ||
: ''; | ||
|
||
if (withDispatcher && testProfileId) { | ||
await enableDispatcher({ | ||
profileId: testProfileId, | ||
wallet, | ||
profile, | ||
transaction, | ||
}); | ||
} | ||
|
||
// store all at the end | ||
_walletAddress = address; | ||
_testProfileId = testProfileId || ''; | ||
}); | ||
|
||
afterAll(async () => { | ||
if (!_testProfileId) { | ||
return; | ||
} | ||
|
||
await burnProfile({ | ||
profileId: _testProfileId, | ||
handle: testHandle, | ||
wallet, | ||
walletAddress: _walletAddress, | ||
profile, | ||
transaction, | ||
}); | ||
}); | ||
|
||
describe(buildDescribeName(options), () => | ||
callback(() => ({ | ||
authentication, | ||
profileId: _testProfileId, | ||
wallet, | ||
walletAddress: _walletAddress, | ||
})), | ||
); | ||
}; | ||
|
||
function buildDescribeName(options?: SetupOptions): string { | ||
if (!options) { | ||
return 'and the instance is authenticated with a random wallet'; | ||
} | ||
if (options.withNewProfile && options.withDispatcher) { | ||
return 'and the instance is authenticated with a random wallet with a profile and dispatcher'; | ||
} | ||
if (options.withNewProfile) { | ||
return 'and the instance is authenticated with a random wallet with a profile'; | ||
} | ||
|
||
never('withDispatcher cannot be used without withNewProfile'); | ||
} | ||
|
||
type CreateProfile = { | ||
handle: string; | ||
walletAddress: string; | ||
profile: Profile; | ||
transaction: Transaction; | ||
}; | ||
|
||
async function createProfile({ handle, walletAddress, profile, transaction }: CreateProfile) { | ||
console.log(`Creating a new profile for ${walletAddress} with handle ${handle}`); | ||
|
||
const profileCreateResult = await profile.create({ handle }); | ||
|
||
const value = profileCreateResult.unwrap(); | ||
if (!isRelayerResult(value)) { | ||
throw new Error(`Profile creation error: ${value.reason}`); | ||
} | ||
|
||
// wait in a loop | ||
await transaction.waitForIsIndexed(value.txId); | ||
} | ||
|
||
type FindProfileId = { | ||
handle: string; | ||
walletAddress: string; | ||
profile: Profile; | ||
}; | ||
|
||
async function findProfileId({ handle, walletAddress, profile }: FindProfileId) { | ||
const allOwnedProfiles = await profile.fetchAll({ | ||
ownedBy: [walletAddress], | ||
limit: 20, | ||
}); | ||
|
||
// console.log(allOwnedProfiles.items.map((i) => ({ id: i.id, handle: i.handle }))); | ||
|
||
const testProfile = allOwnedProfiles.items.find((item) => item.handle === `${handle}.test`); | ||
return testProfile?.id; | ||
} | ||
|
||
type EnableDispatcher = { | ||
profileId: string; | ||
wallet: Wallet; | ||
profile: Profile; | ||
transaction: Transaction; | ||
}; | ||
|
||
async function enableDispatcher({ profileId, wallet, profile, transaction }: EnableDispatcher) { | ||
console.log(`Enabling dispatcher for profileId ${profileId}`); | ||
|
||
const setDispatcherTypedDataResult = await profile.createSetDispatcherTypedData({ | ||
profileId, | ||
}); | ||
const txId = await signAndBroadcast(transaction, wallet, setDispatcherTypedDataResult); | ||
|
||
if (!txId) { | ||
throw Error('Enabling dispatcher failed'); | ||
} | ||
// wait in a loop | ||
await transaction.waitForIsIndexed(txId); | ||
} | ||
|
||
type BurnProfile = { | ||
profileId: string; | ||
handle: string; | ||
wallet: Wallet; | ||
walletAddress: string; | ||
profile: Profile; | ||
transaction: Transaction; | ||
}; | ||
|
||
async function burnProfile({ | ||
profileId, | ||
handle, | ||
wallet, | ||
walletAddress, | ||
profile, | ||
transaction, | ||
}: BurnProfile) { | ||
console.log('All tests finished, burning the test profile', { | ||
profileId, | ||
handle, | ||
}); | ||
|
||
const burnProfileTypedDataResult = await profile.createBurnProfileTypedData({ | ||
profileId, | ||
}); | ||
|
||
const txId = await signAndBroadcast(transaction, wallet, burnProfileTypedDataResult); | ||
|
||
if (!txId) { | ||
throw Error('Profile burn failed'); | ||
} | ||
|
||
console.log(`Profile ${profileId} owned by wallet ${walletAddress} is burned in a txId ${txId}`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export * from './buildTestEnvironment'; | ||
export * from './describeAuthenticatedScenario'; | ||
|
||
export const existingPublicationId = '0x01aa-0x16'; | ||
export const existingProfileId = '0x0185'; | ||
export const altProfileId = '0x0186'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { IEquatableError, Result } from '@lens-protocol/shared-kernel'; | ||
import { Wallet } from 'ethers'; | ||
|
||
import type { TypedDataResponse } from '../consts/types'; | ||
import { isRelayerResult, Transaction } from '../transaction'; | ||
|
||
export async function signAndBroadcast< | ||
T extends TypedDataResponse, | ||
E extends IEquatableError<string, string>, | ||
>(transaction: Transaction, wallet: Wallet, result: Result<T, E>) { | ||
const data = result.unwrap(); | ||
|
||
const signedTypedData = await wallet._signTypedData( | ||
data.typedData.domain, | ||
data.typedData.types, | ||
data.typedData.value, | ||
); | ||
|
||
const broadcastResult = await transaction.broadcast({ | ||
id: data.id, | ||
signature: signedTypedData, | ||
}); | ||
|
||
const value = broadcastResult.unwrap(); | ||
if (!isRelayerResult(value)) { | ||
throw new Error(`Transaction broadcast error: ${value.reason}`); | ||
} | ||
|
||
return value.txId; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { invariant } from '@lens-protocol/shared-kernel'; | ||
import * as dotenv from 'dotenv'; | ||
|
||
dotenv.config(); | ||
|
||
function buildTestHeaders() { | ||
invariant(process.env.TESTING_HEADER_KEY, 'TESTING_HEADER_KEY is not defined in .env file'); | ||
invariant(process.env.TESTING_HEADER_VALUE, 'TESTING_HEADER_VALUE is not defined in .env file'); | ||
|
||
return { | ||
[process.env.TESTING_HEADER_KEY]: process.env.TESTING_HEADER_VALUE, | ||
}; | ||
} | ||
|
||
const actual = jest.requireActual('graphql-request') as unknown; | ||
|
||
// eslint-disable-next-line | ||
// @ts-ignore | ||
class MockGraphQLClient extends actual.GraphQLClient { | ||
constructor(url: string) { | ||
// eslint-disable-next-line | ||
super(url, { headers: buildTestHeaders }); | ||
} | ||
} | ||
|
||
export const GraphQLClient = MockGraphQLClient; |
Oops, something went wrong.