PHP WebShell

Текущая директория: /opt/BitGoJS/modules/blockapis/test

Просмотр файла: UtxoApi.ts

/**
 * This test suite iterates over a set of backends and test methods.
 *
 * For each backend and method, we test that the backend can fetch the resource.
 *
 * We then compare the normalized results from all backends to make sure they are the same.
 */

import 'mocha';
import * as assert from 'assert';
import { BlockchairApi, BlockstreamApi, UtxoApi } from '../src';
import { deepStrictEqualJSON, getFixture } from './fixtures';
import { getHttpClient, isHttpEnabled } from './http.util';

function getTestAddresses(coinName: string): string[] {
  switch (coinName) {
    case 'tbtc':
      return ['2NEhVzd2Lom5g7ZF5K1qYrgZLpXecniqHcE'];
  }
  throw new Error(`unsupported coin ${coinName}`);
}

const nonexistentTx = '1111111111111111111111111111111111111111111111111111111111111111';

function getTestTransactionIds(coinName: string): string[] {
  switch (coinName) {
    case 'tbtc':
      return [
        '19c6cd6b7b8a91e1a63d759eedd2818877a624771d38e00c3ffcedfdd09afc50',
        '87f4cf756eef9979f0fb6a03db2ab2e0fd46060cf97714a990623093fe227a87',
        // unconfirmed (at time of caching)
        'fe38c68afcdd60dc0af11b692f81f512b886132b26165e404c0d0a30d7d6dc54',
        // not seen at all
        nonexistentTx,
      ];
  }
  throw new Error(`unsupported coin ${coinName}`);
}

type MethodArguments = unknown[];

/**
 * A test case for a UtxoApi method.
 */
class TestCase<T> {
  /**
   * @param coinName - coin to test
   * @param methodName - method to test
   * @param args - method arguments
   */
  constructor(public coinName: string, public methodName: keyof UtxoApi, public args: unknown[]) {}

  /**
   * Call the method on the given API.
   * @param api
   */
  func(api: UtxoApi) {
    return (api[this.methodName] as any)(...this.args);
  }

  /**
   * Get the fixture for this test case.
   * @param api
   * @param defaultValue
   */
  async getFixture(api: UtxoApi, defaultValue?: T): Promise<T> {
    const filename = [
      'UtxoApi',
      api.constructor.name,
      this.coinName,
      this.methodName,
      this.args.join(','),
      'json',
    ].join('.');
    return await getFixture(`${__dirname}/fixtures/${filename}`, defaultValue);
  }

  /**
   * Get the fixture, but with the API-specific fields removed.
   * @param api
   */
  async getFixtureNormal(api: UtxoApi): Promise<T> {
    if (this.methodName === 'getTransactionStatus') {
      // remove api-specific fields
      const blockInfo = (await this.getFixture(api)) as unknown as Record<string, unknown>;
      return {
        found: blockInfo.found,
        confirmed: blockInfo.confirmed,
        blockHeight: blockInfo.blockHeight,
      } as unknown as T;
    }
    return await this.getFixture(api);
  }

  /** Certain calls produce non-200 responses, and we don't cache those. */
  expectError(api: UtxoApi): boolean {
    if (this.args[0] === nonexistentTx) {
      if (this.methodName === 'getTransactionStatus') {
        // Blockstream returns a 404 for missing transactions
        return api.constructor.name === 'BlockstreamApi' && !isHttpEnabled();
      }
      return true;
    }
    return false;
  }

  /**
   * @return a human-readable title for this test case.
   */
  title(): string {
    function elide(s: string, len: number) {
      return s.length > len ? `${s.slice(0, len)}...` : s;
    }
    return `${this.methodName}(${this.args.map((v) => elide(String(v), 4))})`;
  }
}

/**
 * @param coinName
 * @return a list of APIs to test.
 */
function getApis(coinName: string): UtxoApi[] {
  if (coinName === 'tbtc') {
    return [
      // BitGoApi.forCoin(coinName),
      BlockchairApi.forCoin(coinName, { httpClient: getHttpClient('blockchair') }),
      BlockstreamApi.forCoin(coinName, { httpClient: getHttpClient('blockstream') }),
    ];
  }
  return [];
}

/**
 * @param coinName
 * @return a list of test cases for the given coin.
 */
function getTestCases(coinName: string): TestCase<unknown>[] {
  function getArguments(coinName: string, methodName: keyof UtxoApi): MethodArguments[] {
    switch (methodName) {
      case 'getTransactionHex':
      case 'getTransactionInputs':
      case 'getTransactionIO':
      case 'getTransactionSpends':
      case 'getTransactionStatus':
        return getTestTransactionIds(coinName).map((v) => [v]);
      case 'getUnspentsForAddresses':
        return getTestAddresses(coinName).map((v) => [[v]]);
    }
    throw new Error(`not implemented`);
  }

  const methods: (keyof UtxoApi)[] = [
    'getTransactionHex',
    'getTransactionInputs',
    'getTransactionIO',
    'getTransactionStatus',
    'getTransactionSpends',
    'getUnspentsForAddresses',
  ];

  return methods.flatMap((methodName) =>
    getArguments(coinName, methodName).map((args) => new TestCase(coinName, methodName, args))
  );
}

/**
 * Set up fetch tests for the given API.
 *
 * @param api
 * @param coinName
 */
function runTestFetch(api: UtxoApi, coinName: string) {
  getTestCases(coinName).forEach((testCase) => {
    describe(`${api.constructor.name} ${testCase.title()}`, function () {
      it('fetches resource', async function () {
        this.timeout(10_000);
        if (testCase.expectError(api)) {
          await assert.rejects(() => testCase.func(api));
          return;
        }
        const resource = await testCase.func(api);
        deepStrictEqualJSON(resource, await testCase.getFixture(api, resource));
      });
    });
  });
}

/**
 * Set up comparison tests for the given API.
 *
 * @param api
 * @param coinName
 */
function runTestCompare(api: UtxoApi, coinName: string) {
  getTestCases(coinName).forEach((testCase) => {
    describe(`method ${testCase.title()}`, function () {
      getApis(coinName)
        .filter((otherApi) => api.constructor !== otherApi.constructor)
        .forEach((otherApi) => {
          if (testCase.expectError(api) || testCase.expectError(otherApi)) {
            return;
          }
          it(`compare ${api.constructor.name} and ${otherApi.constructor.name}`, async function () {
            deepStrictEqualJSON(await testCase.getFixtureNormal(api), await testCase.getFixtureNormal(otherApi));
          });
        });
    });
  });
}

['tbtc'].forEach((coinName) => {
  getApis(coinName).forEach((api) => {
    runTestFetch(api, coinName);
  });
  getApis(coinName).forEach((api) => {
    runTestCompare(api, coinName);
  });
});

Выполнить команду


Для локальной разработки. Не используйте в интернете!