Resolve "Blockchain Adapter: Add Chaingraph Adapter"
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
// TODO: We want to create a uniform set of tests for our BaseBlockchain interface.
|
||||
// We should repeat the tests for each provider that we have.
|
||||
// The below is a **VERY ROUGH** example of how we might be able to do this.
|
||||
// However, Chaingraph backend is currently broken for many queries, so let's not do this yet.
|
||||
|
||||
import { beforeAll, describe, it, expect } from 'vitest';
|
||||
import {
|
||||
BaseBlockchain,
|
||||
BlockchainChaingraph,
|
||||
BlockchainElectrum,
|
||||
} from '../../src/index.js';
|
||||
|
||||
const adapters: Record<
|
||||
string,
|
||||
() => Promise<BaseBlockchain<unknown, unknown>>
|
||||
> = {
|
||||
chaingraph: async () => {
|
||||
return await BlockchainChaingraph.from();
|
||||
},
|
||||
electrum: async () => {
|
||||
return await BlockchainElectrum.from();
|
||||
},
|
||||
};
|
||||
|
||||
const testAddress = 'bitcoincash:qq974d8zzqxmkzk0xxlm6ryx3nqyq53u25w6mwm0rm';
|
||||
const testLockscript = '76a91454eaf78abae543d9d8c1cf87109cb261101714ea88ac';
|
||||
const testTxId =
|
||||
'7b63d5923f76be4e49820c41ed3557a55d5ce33e429519c6b9773b3214be5cb6';
|
||||
const testBlockHeight = 800000;
|
||||
|
||||
describe.each(Object.entries(adapters))('%s Adapter', (name, createAdapter) => {
|
||||
let blockchain: BaseBlockchain<unknown, unknown>;
|
||||
|
||||
beforeAll(async () => {
|
||||
blockchain = await createAdapter();
|
||||
});
|
||||
|
||||
it(`${name}.fetchUnspents()`, async () => {
|
||||
const result = await blockchain.fetchUnspents(testAddress);
|
||||
|
||||
expect(Array.isArray(result.toArray())).toBeTruthy();
|
||||
if (result.size > 0) {
|
||||
const firstUTXO = result.toArray()[0];
|
||||
|
||||
expect(firstUTXO.utxo.outpoint).toHaveProperty('outpointTransactionHash');
|
||||
expect(firstUTXO.utxo.outpoint).toHaveProperty('outpointIndex');
|
||||
expect(firstUTXO.utxo.output).toHaveProperty('valueSatoshis');
|
||||
}
|
||||
});
|
||||
|
||||
it(`${name}.fetchTransaction() `, async () => {
|
||||
const result = await blockchain.fetchTransaction(testTxId);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(Array.isArray(result.getInputs())).toBeTruthy();
|
||||
expect(result.getInputs()[0]).toHaveProperty('outpointTransactionHash');
|
||||
expect(Array.isArray(result.getOutputs())).toBeTruthy();
|
||||
expect(result.getOutputs()[0]).toHaveProperty('valueSatoshis');
|
||||
});
|
||||
|
||||
it(`${name}.fetchBlockHeader() `, async () => {
|
||||
const result = await blockchain.fetchBlockHeader(testBlockHeight);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.toHex()).toBeTypeOf('string');
|
||||
expect(result.getTimestamp()).toBeTypeOf('number');
|
||||
});
|
||||
|
||||
it(`${name}.fetchChainTip() `, async () => {
|
||||
const result = await blockchain.fetchChainTip();
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.height).toBeTypeOf('number');
|
||||
expect(result.blockHeader.getHashHex()).toBeTypeOf('string');
|
||||
});
|
||||
|
||||
it(`${name}.subscribeLockscript()`, async () => {
|
||||
const subscriptionPromise = new Promise((resolve, reject) => {
|
||||
blockchain
|
||||
.subscribeLockscript(testLockscript, () => {})
|
||||
.then((unsubscribeCallback) => resolve(unsubscribeCallback))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// Should return and unsuscription callback after a successful suscription.
|
||||
await expect(subscriptionPromise).resolves.toBeTypeOf('function');
|
||||
});
|
||||
|
||||
it(`${name}.subscribeAddress(), `, async () => {
|
||||
const subscriptionPromise = new Promise((resolve, reject) => {
|
||||
blockchain
|
||||
.subscribeAddress(testAddress, () => {})
|
||||
.then((unsubscribeCallback) => resolve(unsubscribeCallback))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// Should return and unsuscription callback after a successful suscription.
|
||||
await expect(subscriptionPromise).resolves.toBeTypeOf('function');
|
||||
});
|
||||
|
||||
it.sequential(`${name}.subscribeTransaction()`, async () => {
|
||||
const subscriptionPromise = new Promise((resolve, reject) => {
|
||||
blockchain
|
||||
.subscribeTransaction(testTxId, () => {})
|
||||
.then((unsubscribeCallback) => resolve(unsubscribeCallback))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// Should return and unsuscription callback after a successful suscription.
|
||||
await expect(subscriptionPromise).resolves.toBeTypeOf('function');
|
||||
});
|
||||
|
||||
it(`${name}.unsubscribeLockscript()`, async () => {
|
||||
const unsubscriptionPromise = new Promise((resolve, reject) => {
|
||||
blockchain
|
||||
.unsubscribeLockscript(testLockscript, () => {})
|
||||
.then((result) => resolve(result))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// Should return true after a successful unsubscription.
|
||||
await expect(unsubscriptionPromise).resolves.toBeTypeOf('boolean');
|
||||
});
|
||||
|
||||
it(`${name}.unsubscribeAddress()`, async () => {
|
||||
const unsubscriptionPromise = new Promise((resolve, reject) => {
|
||||
blockchain
|
||||
.unsubscribeAddress(testAddress, () => {})
|
||||
.then((result) => resolve(result))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// Should return true after a successful unsubscription.
|
||||
await expect(unsubscriptionPromise).resolves.toBeTypeOf('boolean');
|
||||
});
|
||||
|
||||
it.sequential(`${name}.unsubscribeTransaction()`, async () => {
|
||||
const unsubscriptionPromise = new Promise((resolve, reject) => {
|
||||
blockchain
|
||||
.unsubscribeTransaction(testTxId, () => {})
|
||||
.then((result) => resolve(result))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// Should return true after a successful unsubscription.
|
||||
await expect(unsubscriptionPromise).resolves.toBeTypeOf('boolean');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,184 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { GraphQLClient } from '../../src/utils/index.js';
|
||||
import {
|
||||
monitorSyncTipsSubscriptionQuery,
|
||||
monitorSyncTipsQuery,
|
||||
monitorSyncTipsSchema,
|
||||
} from '../../src/blockchain/blockchain-chaingraph-helpers.js';
|
||||
|
||||
const chaingraphServer = 'https://demo.chaingraph.cash/v1/graphql';
|
||||
|
||||
describe('GraphQLClient', () => {
|
||||
const graphQLClient = new GraphQLClient(chaingraphServer);
|
||||
|
||||
it("should succesfully subscribe to 'MonitorSyncTips' messages and receive a valid response from server", async () => {
|
||||
/**
|
||||
* Query in SDL:
|
||||
*
|
||||
`subscription MonitorSyncTips ($nameFilter: String = "bchn-mainnet") {
|
||||
node (where: { name: { _like: $nameFilter } }) {
|
||||
name
|
||||
user_agent
|
||||
latest_tip: accepted_blocks(
|
||||
limit: 1,
|
||||
order_by: { block_internal_id: desc }
|
||||
) {
|
||||
accepted_at
|
||||
block {
|
||||
hash
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
*/
|
||||
const response = await graphQLClient.subscribe(
|
||||
'MonitorSyncTipsSubscriptionTest',
|
||||
monitorSyncTipsSubscriptionQuery,
|
||||
true, // wait For response on subscribe
|
||||
{
|
||||
chainFilter: 'bchn-chipnet',
|
||||
},
|
||||
);
|
||||
|
||||
expect(response).toBeTypeOf('object');
|
||||
expect(response).not.toBeInstanceOf(Error);
|
||||
expect(response).not.toBeNull();
|
||||
|
||||
await graphQLClient.unsubscribe(['MonitorSyncTipsSubscriptionTest']);
|
||||
});
|
||||
|
||||
it("should succesfully query latest 'MonitorSyncTips'", async () => {
|
||||
/**
|
||||
* Query in SDL:
|
||||
*
|
||||
`query MonitorSyncTips ( $chainFilter: String = "bchn-mainnet" ) {
|
||||
node ( where: { name: { _like: $chainFilter } } ) {
|
||||
name
|
||||
user_agent
|
||||
latest_tip: accepted_blocks(
|
||||
limit: 1,
|
||||
order_by: { block_internal_id: desc }
|
||||
) {
|
||||
accepted_at
|
||||
block {
|
||||
hash
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
*/
|
||||
const response = await graphQLClient.request(
|
||||
'monitorSyncTipsQuery',
|
||||
monitorSyncTipsQuery,
|
||||
{ chainFilter: 'bchn-mainnet' },
|
||||
);
|
||||
|
||||
// Validate the response notification payload.
|
||||
monitorSyncTipsSchema.parse(response);
|
||||
|
||||
expect(response).toBeTypeOf('object');
|
||||
expect(response).not.toBeInstanceOf(Error);
|
||||
expect(response).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should succesfully request 'GetJonathan100CashAccount' and receive a valid response from server", async () => {
|
||||
/**
|
||||
* Query in SDL:
|
||||
*
|
||||
`query GetJonathan100CashAccount {
|
||||
search_output_prefix(
|
||||
args: { locking_bytecode_prefix_hex: "6a0401010101084a6f6e617468616e" },
|
||||
where: {
|
||||
transaction: { block_inclusions: { block: { height: { _eq: 563720 } } } }
|
||||
}
|
||||
) {
|
||||
transaction {
|
||||
hash
|
||||
data_carrier_outputs {
|
||||
locking_bytecode
|
||||
output_index
|
||||
}
|
||||
block_inclusions {
|
||||
block {
|
||||
hash
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
*/
|
||||
const getJonathan100CashAccount = {
|
||||
query: {
|
||||
__name: 'GetJonathan100CashAccount',
|
||||
search_output_prefix: {
|
||||
__args: {
|
||||
args: {
|
||||
locking_bytecode_prefix_hex: '6a0401010101084a6f6e617468616e',
|
||||
},
|
||||
where: {
|
||||
transaction: {
|
||||
block_inclusions: {
|
||||
block: {
|
||||
height: {
|
||||
_eq: 563720,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
transaction: {
|
||||
hash: true,
|
||||
data_carrier_outputs: {
|
||||
locking_bytecode: true,
|
||||
output_index: true,
|
||||
},
|
||||
block_inclusions: {
|
||||
block: {
|
||||
hash: true,
|
||||
height: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const expectedResponse = {
|
||||
data: {
|
||||
search_output_prefix: [
|
||||
{
|
||||
transaction: {
|
||||
hash: '\\x590d1fdf7e04af0ee08f9194bb9e8d1971bdcbf55d29303d5bf32d4eae5e7136',
|
||||
data_carrier_outputs: [
|
||||
{
|
||||
locking_bytecode:
|
||||
'\\x6a0401010101084a6f6e617468616e1501ebdeb6430f3d16a9c6758d6c0d7a400c8e6bbee4',
|
||||
output_index: '0',
|
||||
},
|
||||
],
|
||||
block_inclusions: [
|
||||
{
|
||||
block: {
|
||||
hash: '\\x000000000000000002abbeff5f6fb22a0b3b5c2685c6ef4ed2d2257ed54e9dcb',
|
||||
height: '563720',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const response = await graphQLClient.request(
|
||||
'GetJonathan100CashAccount',
|
||||
getJonathan100CashAccount,
|
||||
);
|
||||
|
||||
expect(response).toEqual(expectedResponse);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,557 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
jsonToGraphQLQuery,
|
||||
EnumType,
|
||||
VariableType,
|
||||
} from 'json-to-graphql-query';
|
||||
|
||||
describe('should convert json formated queries to GraphQL Schema Definition Language(SDL)', () => {
|
||||
it('subscription query', () => {
|
||||
const expectedQuery = `subscription
|
||||
MonitorSyncTips ( $chainFilter: String = "bchn-mainnet" ) {
|
||||
node ( where: { name: { _like: $chainFilter } } ) {
|
||||
name
|
||||
user_agent
|
||||
latest_tip: accepted_blocks(
|
||||
limit: 1,
|
||||
order_by: { block_internal_id: desc }
|
||||
) {
|
||||
accepted_at
|
||||
block {
|
||||
hash
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const monitorSyncTipsSubscriptionQuery = {
|
||||
subscription: {
|
||||
__name: 'MonitorSyncTips',
|
||||
__variables: {
|
||||
chainFilter: 'String = "bchn-mainnet"', // Default to 'bchn-mainnet'
|
||||
},
|
||||
node: {
|
||||
__args: {
|
||||
where: {
|
||||
name: {
|
||||
_like: new VariableType('chainFilter'),
|
||||
},
|
||||
},
|
||||
},
|
||||
name: true,
|
||||
user_agent: true,
|
||||
latest_tip: {
|
||||
__aliasFor: 'accepted_blocks',
|
||||
__args: {
|
||||
limit: 1,
|
||||
order_by: { block_internal_id: new EnumType('desc') },
|
||||
},
|
||||
accepted_at: true,
|
||||
block: {
|
||||
hash: true,
|
||||
height: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
normalizeString(jsonToGraphQLQuery(monitorSyncTipsSubscriptionQuery)),
|
||||
).toEqual(normalizeString(expectedQuery));
|
||||
});
|
||||
|
||||
it('request query', () => {
|
||||
const expectedQuery = `query
|
||||
GetJonathan100CashAccount {
|
||||
search_output_prefix(
|
||||
args: { locking_bytecode_prefix_hex: "6a0401010101084a6f6e617468616e" },
|
||||
where: {
|
||||
transaction: { block_inclusions: { block: { height: { _eq: 563720 } } } }
|
||||
}
|
||||
) {
|
||||
transaction {
|
||||
hash
|
||||
data_carrier_outputs {
|
||||
locking_bytecode
|
||||
output_index
|
||||
}
|
||||
block_inclusions {
|
||||
block {
|
||||
hash
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const getJonathan100CashAccountRequestQuery = {
|
||||
query: {
|
||||
__name: 'GetJonathan100CashAccount',
|
||||
search_output_prefix: {
|
||||
__args: {
|
||||
args: {
|
||||
locking_bytecode_prefix_hex: '6a0401010101084a6f6e617468616e',
|
||||
},
|
||||
where: {
|
||||
transaction: {
|
||||
block_inclusions: {
|
||||
block: {
|
||||
height: {
|
||||
_eq: 563720,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
transaction: {
|
||||
hash: true,
|
||||
data_carrier_outputs: {
|
||||
locking_bytecode: true,
|
||||
output_index: true,
|
||||
},
|
||||
block_inclusions: {
|
||||
block: {
|
||||
hash: true,
|
||||
height: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
normalizeString(
|
||||
jsonToGraphQLQuery(getJonathan100CashAccountRequestQuery),
|
||||
),
|
||||
).toEqual(normalizeString(expectedQuery));
|
||||
});
|
||||
|
||||
it('fetch unspent transaction outputs for locking bytecode hex query', () => {
|
||||
const expectedQuery = `query
|
||||
SearchOutputsByLockingBytecode( $chainFilter: String = "bchn-mainnet", $lockingBytecodeHexObject: String ) {
|
||||
search_output(
|
||||
args: {
|
||||
locking_bytecode_hex: $lockingBytecodeHexObject
|
||||
},
|
||||
where: {
|
||||
_not: { spent_by: {} },
|
||||
transaction: { block_inclusions: { block: { accepted_by: { node: { name: { _like: $chainFilter } } } } } }
|
||||
}
|
||||
) {
|
||||
locking_bytecode
|
||||
output_index
|
||||
transaction_hash
|
||||
value_satoshis
|
||||
token_category
|
||||
fungible_token_amount
|
||||
nonfungible_token_capability
|
||||
nonfungible_token_commitment
|
||||
transaction {
|
||||
block_inclusions {
|
||||
block {
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
spent_by {
|
||||
input_index
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const searchOutputsByLockingBytecodeQuery = {
|
||||
query: {
|
||||
__name: 'SearchOutputsByLockingBytecode',
|
||||
__variables: {
|
||||
chainFilter: 'String = "bchn-mainnet"',
|
||||
lockingBytecodeHexObject: 'String',
|
||||
},
|
||||
search_output: {
|
||||
__args: {
|
||||
args: {
|
||||
locking_bytecode_hex: new VariableType(
|
||||
'lockingBytecodeHexObject',
|
||||
),
|
||||
},
|
||||
where: {
|
||||
_not: { spent_by: {} },
|
||||
transaction: {
|
||||
block_inclusions: {
|
||||
block: {
|
||||
accepted_by: {
|
||||
node: {
|
||||
name: {
|
||||
_like: new VariableType('chainFilter'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
locking_bytecode: true,
|
||||
output_index: true,
|
||||
transaction_hash: true,
|
||||
value_satoshis: true,
|
||||
token_category: true,
|
||||
fungible_token_amount: true,
|
||||
nonfungible_token_capability: true,
|
||||
nonfungible_token_commitment: true,
|
||||
transaction: {
|
||||
block_inclusions: {
|
||||
block: {
|
||||
height: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
spent_by: {
|
||||
input_index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
normalizeString(jsonToGraphQLQuery(searchOutputsByLockingBytecodeQuery)),
|
||||
).toEqual(normalizeString(expectedQuery));
|
||||
});
|
||||
|
||||
it('Get Transaction Encoded Hex query', () => {
|
||||
const expectedQuery = `query
|
||||
GetTransactionHex($chainFilter: String = "bchn-mainnet", $txHash: String) {
|
||||
transaction(where: {
|
||||
hash: {
|
||||
_eq: $txHash
|
||||
},
|
||||
block_inclusions: {
|
||||
block:{
|
||||
accepted_by:{
|
||||
node: {
|
||||
name: {
|
||||
_like: $chainFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}) {
|
||||
encoded_hex
|
||||
}
|
||||
}`;
|
||||
|
||||
const getTransactionHex = {
|
||||
query: {
|
||||
__name: 'GetTransactionHex',
|
||||
__variables: {
|
||||
chainFilter: 'String = "bchn-mainnet"',
|
||||
txHash: 'String',
|
||||
},
|
||||
transaction: {
|
||||
__args: {
|
||||
where: {
|
||||
hash: { _eq: new VariableType('txHash') },
|
||||
block_inclusions: {
|
||||
block: {
|
||||
accepted_by: {
|
||||
node: {
|
||||
name: {
|
||||
_like: new VariableType('chainFilter'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
encoded_hex: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(normalizeString(jsonToGraphQLQuery(getTransactionHex))).toEqual(
|
||||
normalizeString(expectedQuery),
|
||||
);
|
||||
});
|
||||
|
||||
it('Get Block Header Hex query', () => {
|
||||
const expectedQuery = `query
|
||||
GetBlockHeaderHex($chainFilter: String = "bchn-mainnet", $blockHeight: bigint) {
|
||||
block(where: {
|
||||
height: {
|
||||
_eq: $blockHeight
|
||||
},
|
||||
accepted_by: {
|
||||
block:{
|
||||
accepted_by:{
|
||||
node: {
|
||||
name: {
|
||||
_like: $chainFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}) {
|
||||
header
|
||||
}
|
||||
}`;
|
||||
|
||||
const getBlockHeaderHex = {
|
||||
query: {
|
||||
__name: 'GetBlockHeaderHex',
|
||||
__variables: {
|
||||
chainFilter: 'String = "bchn-mainnet"',
|
||||
blockHeight: 'bigint',
|
||||
},
|
||||
block: {
|
||||
__args: {
|
||||
where: {
|
||||
height: { _eq: new VariableType('blockHeight') },
|
||||
accepted_by: {
|
||||
block: {
|
||||
accepted_by: {
|
||||
node: {
|
||||
name: {
|
||||
_like: new VariableType('chainFilter'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
header: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(normalizeString(jsonToGraphQLQuery(getBlockHeaderHex))).toEqual(
|
||||
normalizeString(expectedQuery),
|
||||
);
|
||||
});
|
||||
|
||||
it('Monitor For Payments subscription query', () => {
|
||||
const expectedQuery = `subscription
|
||||
MonitorForPayments(
|
||||
$lockscriptHexObject: _text
|
||||
) {
|
||||
search_output(
|
||||
args: {
|
||||
locking_bytecode_hex: $lockscriptHexObject
|
||||
}, where:{
|
||||
transaction: {
|
||||
_not: {block_inclusions: {}}
|
||||
}
|
||||
}
|
||||
) {
|
||||
locking_bytecode
|
||||
transaction_hash
|
||||
value_satoshis
|
||||
transaction {
|
||||
node_validations {
|
||||
validated_at
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const monitorForPayments = {
|
||||
subscription: {
|
||||
__name: 'MonitorForPayments',
|
||||
__variables: {
|
||||
lockscriptHexObject: '_text',
|
||||
},
|
||||
search_output: {
|
||||
__args: {
|
||||
args: {
|
||||
locking_bytecode_hex: new VariableType('lockscriptHexObject'),
|
||||
},
|
||||
where: {
|
||||
transaction: {
|
||||
_not: { block_inclusions: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
locking_bytecode: true,
|
||||
transaction_hash: true,
|
||||
value_satoshis: true,
|
||||
transaction: {
|
||||
node_validations: {
|
||||
validated_at: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(normalizeString(jsonToGraphQLQuery(monitorForPayments))).toEqual(
|
||||
normalizeString(expectedQuery),
|
||||
);
|
||||
});
|
||||
|
||||
it('Monitor Transaction Hash subscription query', () => {
|
||||
const expectedQuery = `subscription
|
||||
MonitorTransactionHash(
|
||||
$transactionHash: bytea
|
||||
) {
|
||||
transaction(where:{node_validations:{
|
||||
transaction:{_not:{block_inclusions:{}},
|
||||
hash:{_eq: $transactionHash}}}})
|
||||
{
|
||||
hash
|
||||
node_validations {
|
||||
validated_at
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const monitorTransactionHash = {
|
||||
subscription: {
|
||||
__name: 'MonitorTransactionHash',
|
||||
__variables: {
|
||||
transactionHash: 'bytea',
|
||||
},
|
||||
transaction: {
|
||||
__args: {
|
||||
where: {
|
||||
node_validations: {
|
||||
transaction: {
|
||||
_not: { block_inclusions: {} },
|
||||
hash: { _eq: new VariableType('transactionHash') },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hash: true,
|
||||
node_validations: {
|
||||
validated_at: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(normalizeString(jsonToGraphQLQuery(monitorTransactionHash))).toEqual(
|
||||
normalizeString(expectedQuery),
|
||||
);
|
||||
});
|
||||
|
||||
it('Get Transaction Details query', () => {
|
||||
const expectedQuery = `query
|
||||
GetLockscriptHistory(
|
||||
$lockscriptHex: bytea
|
||||
) {
|
||||
transaction(where: {
|
||||
outputs: {
|
||||
locking_bytecode: { _eq: $lockscriptHex }
|
||||
},
|
||||
inputs:{
|
||||
outpoint:{
|
||||
locking_bytecode: { _eq: $lockscriptHex }
|
||||
}
|
||||
}
|
||||
} )
|
||||
{
|
||||
hash
|
||||
is_coinbase
|
||||
block_inclusions {
|
||||
block {
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const getLockscriptHistory = {
|
||||
query: {
|
||||
__name: 'GetLockscriptHistory',
|
||||
__variables: {
|
||||
lockscriptHex: 'bytea',
|
||||
},
|
||||
transaction: {
|
||||
__args: {
|
||||
where: {
|
||||
outputs: {
|
||||
locking_bytecode: { _eq: new VariableType('lockscriptHex') },
|
||||
},
|
||||
inputs: {
|
||||
outpoint: {
|
||||
locking_bytecode: { _eq: new VariableType('lockscriptHex') },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hash: true,
|
||||
is_coinbase: true,
|
||||
block_inclusions: {
|
||||
block: {
|
||||
height: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(normalizeString(jsonToGraphQLQuery(getLockscriptHistory))).toEqual(
|
||||
normalizeString(expectedQuery),
|
||||
);
|
||||
});
|
||||
|
||||
it('Send Raw Transaction Mutation query', () => {
|
||||
const expectedQuery = `mutation
|
||||
SendRawTransaction(
|
||||
$rawTransactionHex: String!,
|
||||
$nodeInternalId: bigint!
|
||||
) {
|
||||
send_transaction(
|
||||
request: {
|
||||
node_internal_id: $nodeInternalId,
|
||||
encoded_hex: $rawTransactionHex
|
||||
}
|
||||
) {
|
||||
transaction_hash
|
||||
validation_error_message
|
||||
validation_success
|
||||
transmission_error_message
|
||||
transmission_success
|
||||
}
|
||||
}`;
|
||||
|
||||
const sendRawTransactionMutation = {
|
||||
mutation: {
|
||||
__name: 'SendRawTransaction',
|
||||
__variables: {
|
||||
rawTransactionHex: 'String!',
|
||||
nodeInternalId: 'bigint!',
|
||||
},
|
||||
send_transaction: {
|
||||
__args: {
|
||||
request: {
|
||||
node_internal_id: new VariableType('nodeInternalId'),
|
||||
encoded_hex: new VariableType('rawTransactionHex'),
|
||||
},
|
||||
},
|
||||
transaction_hash: true,
|
||||
validation_error_message: true,
|
||||
validation_success: true,
|
||||
transmission_error_message: true,
|
||||
transmission_success: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
normalizeString(jsonToGraphQLQuery(sendRawTransactionMutation)),
|
||||
).toEqual(normalizeString(expectedQuery));
|
||||
});
|
||||
});
|
||||
|
||||
function normalizeString(str: string): string {
|
||||
// Removes all whitespace (spaces, line breaks, tabs)
|
||||
return str.replace(/\s+/g, '');
|
||||
}
|
||||
Reference in New Issue
Block a user