Skip to content

Commit

Permalink
PorterClient: test and fix for 'tryAndCall'
Browse files Browse the repository at this point in the history
  • Loading branch information
vzotova committed Jul 24, 2024
1 parent 3552e03 commit d5faf67
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 20 deletions.
1 change: 1 addition & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"zod": "*"
},
"devDependencies": {
"@nucypher/test-utils": "workspace:*",
"@typechain/ethers-v5": "^11.1.2",
"@types/deep-equal": "^1.0.3",
"@types/qs": "^6.9.15",
Expand Down
15 changes: 12 additions & 3 deletions packages/shared/src/porter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,23 @@ export class PorterClient {

protected async tryAndCall<T, D>(config: AxiosRequestConfig<D>): Promise<AxiosResponse<T>> {
let resp!: AxiosResponse<T>;
let lastError = undefined;
for (const porterUrl of this.porterUrls) {
config.baseURL = porterUrl.toString();
resp = await axios.request(config);
const localConfig = { ...config, baseURL: porterUrl.toString() }
try {
resp = await axios.request(localConfig);
} catch (e) {
lastError = e;
continue;
}
if (resp.status === HttpStatusCode.Ok) {
return resp;
}
}
return resp;
if (lastError !== undefined) {
throw lastError;
}
throw new Error("Porter returns bad response");
}

public async getUrsulas(
Expand Down
91 changes: 91 additions & 0 deletions packages/shared/test/porter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
initialize,
GetUrsulasResult,
PorterClient,
toHexString,
Ursula,
} from '../src';
import { fakeUrsulas } from '@nucypher/test-utils';
import { beforeAll, describe, expect, SpyInstance, it, vi } from 'vitest';
import axios, { HttpStatusCode } from 'axios';

const fakePorterUris = ['https://_this_should_crash.com/', 'https://2_this_should_crash.com/', 'https://_this_should_work.com/'];

const mockGetUrsulas = (
ursulas: Ursula[] = fakeUrsulas(),
): SpyInstance => {
const fakePorterUrsulas = (
mockUrsulas: readonly Ursula[],
): GetUrsulasResult => {
return {
result: {
ursulas: mockUrsulas.map(({ encryptingKey, uri, checksumAddress }) => ({
encrypting_key: toHexString(encryptingKey.toCompressedBytes()),
uri: uri,
checksum_address: checksumAddress,
})),
},
version: '5.2.0',
};
};

return vi.spyOn(axios, 'request').mockImplementation(async (config) => {
switch (config.baseURL) {
case fakePorterUris[2]:
return Promise.resolve({ status: HttpStatusCode.Ok, data: fakePorterUrsulas(ursulas) });
case fakePorterUris[0]:
throw new Error();
default:
throw Promise.resolve({ status: HttpStatusCode.BadRequest });
}
});
};

describe('PorterClient', () => {
beforeAll(async () => {
await initialize();
});

it('Get Ursulas', async () => {
const ursulas = fakeUrsulas();
const getUrsulasSpy = mockGetUrsulas(ursulas);
const porterClient = new PorterClient(fakePorterUris);
const result = await porterClient.getUrsulas(ursulas.length);

expect(result.every((u: Ursula, index: number) => {
const expectedUrsula = ursulas[index];
return u.checksumAddress === expectedUrsula.checksumAddress &&
u.uri === expectedUrsula.uri &&
u.encryptingKey.equals(expectedUrsula.encryptingKey);
})).toBeTruthy();
const params = {
method: 'get',
url: "/get_ursulas",
params: {
exclude_ursulas: [],
include_ursulas: [],
quantity: ursulas.length,
}
};

expect(getUrsulasSpy).toBeCalledTimes(fakePorterUris.length);
expect(getUrsulasSpy).toHaveBeenNthCalledWith(1, expect.objectContaining(
{ ...params, baseURL: fakePorterUris[0] })
);
expect(getUrsulasSpy).toHaveBeenNthCalledWith(2, expect.objectContaining(
{ ...params, baseURL: fakePorterUris[1] })
);
expect(getUrsulasSpy).toHaveBeenNthCalledWith(3, expect.objectContaining(
{ ...params, baseURL: fakePorterUris[2] }));
});

it('returns error in case all porters fail', async () => {
const ursulas = fakeUrsulas();
mockGetUrsulas(ursulas);
let porterClient = new PorterClient([fakePorterUris[0], fakePorterUris[1]]);
expect(porterClient.getUrsulas(ursulas.length)).rejects.toThrowError();
porterClient = new PorterClient([fakePorterUris[1], fakePorterUris[0]]);
expect(porterClient.getUrsulas(ursulas.length)).rejects.toThrowError();
});

});
5 changes: 5 additions & 0 deletions packages/shared/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@
"skipLibCheck": true,
"resolveJsonModule": true,
},
"references": [
{
"path": "../test-utils/tsconfig.es.json",
},
],
}
17 changes: 0 additions & 17 deletions packages/test-utils/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@ import {
import {
ChecksumAddress,
DkgCoordinatorAgent,
GetUrsulasResult,
PorterClient,
RetrieveCFragsResult,
TacoDecryptResult,
toHexString,
Ursula,
zip,
} from '@nucypher/shared';
Expand Down Expand Up @@ -132,21 +130,6 @@ export const fakeUrsulas = (n = 4): Ursula[] =>
export const mockGetUrsulas = (
ursulas: Ursula[] = fakeUrsulas(),
): SpyInstance => {
// const fakePorterUrsulas = (
// mockUrsulas: readonly Ursula[],
// ): GetUrsulasResult => {
// return {
// result: {
// ursulas: mockUrsulas.map(({ encryptingKey, uri, checksumAddress }) => ({
// encrypting_key: toHexString(encryptingKey.toCompressedBytes()),
// uri: uri,
// checksum_address: checksumAddress,
// })),
// },
// version: '5.2.0',
// };
// };

return vi.spyOn(PorterClient.prototype, 'getUrsulas').mockImplementation(async () => {
return Promise.resolve(ursulas);
});
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d5faf67

Please sign in to comment.