diff --git a/.travis.yml b/.travis.yml index ec208c6a..1a817bc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_install: -e BIGCHAINDB_KEYPAIR_PRIVATE=5C5Cknco7YxBRP9AgB1cbUVTL4FAcooxErLygw1DeG2D -e BIGCHAINDB_DATABASE_BACKEND=mongodb -e BIGCHAINDB_DATABASE_HOST=172.17.0.1 - bigchaindb/bigchaindb:0.10.2 + bigchaindb/bigchaindb:master start script: yarn test diff --git a/package.json b/package.json index 6d533b1e..43ca2c65 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,12 @@ "babel-runtime": "^6.22.0", "cross-env": "^5.0.1", "eslint": "^3.14.1", - "eslint-config-ascribe": "^3.0.1", + "eslint-config-ascribe": "^3.0.4", "eslint-plugin-import": "^2.2.0", "husky": "^0.13.4", "release-it": "^2.7.3", "rimraf": "^2.5.4", + "sinon": "^2.3.4", "webpack": "^2.2.1" }, "dependencies": { diff --git a/src/transaction/makeOutput.js b/src/transaction/makeOutput.js index d8eb1f32..820f3c3b 100644 --- a/src/transaction/makeOutput.js +++ b/src/transaction/makeOutput.js @@ -3,10 +3,13 @@ * Create an Output from a Condition. * Note: Assumes the given Condition was generated from a single public key (e.g. a Ed25519 Condition) * @param {object} condition Condition (e.g. a Ed25519 Condition from `makeEd25519Condition()`) - * @param {number} amount Amount of the output + * @param {string} amount Amount of the output * @returns {object} An Output usable in a Transaction */ -export default function makeOutput(condition, amount = 1) { +export default function makeOutput(condition, amount = '1') { + if (typeof amount !== 'string') { + throw new TypeError('`amount` must be of type string') + } return { 'amount': amount, condition, diff --git a/src/transaction/makeTransferTransaction.js b/src/transaction/makeTransferTransaction.js index d28dae8c..6e9207fc 100644 --- a/src/transaction/makeTransferTransaction.js +++ b/src/transaction/makeTransferTransaction.js @@ -14,25 +14,27 @@ import makeTransaction from './makeTransaction' * For `TRANSFER` Transactions, this should usually just be a list of * Outputs wrapping Ed25519 Conditions generated from the public keys of * the recipients. - * @param {...number} fulfilledOutputs Indices of the Outputs in `unspentTransaction` that this + * @param {...number} OutputIndices Indices of the Outputs in `unspentTransaction` that this * Transaction fulfills. - * Note that the public keys listed in the fulfilled Outputs - * must be used (and in the same order) to sign the Transaction + * Note that listed public keys listed must be used (and in + * the same order) to sign the Transaction * (`signTransaction()`). * @returns {object} Unsigned transaction -- make sure to call signTransaction() on it before * sending it off! */ +// TODO: +// - Make `metadata` optional argument export default function makeTransferTransaction( unspentTransaction, metadata, outputs, - ...fulfilledOutputs + ...outputIndices ) { - const inputs = fulfilledOutputs.map((outputIndex) => { + const inputs = outputIndices.map((outputIndex) => { const fulfilledOutput = unspentTransaction.outputs[outputIndex] const transactionLink = { 'output': outputIndex, - 'txid': unspentTransaction.id, + 'transaction_id': unspentTransaction.id, } return makeInputTemplate(fulfilledOutput.public_keys, transactionLink) diff --git a/test/transaction/test_transaction.js b/test/transaction/test_transaction.js new file mode 100644 index 00000000..6ef3f6d2 --- /dev/null +++ b/test/transaction/test_transaction.js @@ -0,0 +1,132 @@ +import test from 'ava' +import sinon from 'sinon' + +import { Transaction, Ed25519Keypair } from '../../src' +import * as makeTransaction from '../../src/transaction/makeTransaction' // eslint-disable-line +import makeInputTemplate from '../../src/transaction/makeInputTemplate' + + +// TODO: Find out if ava has something like conftest, if so put this there. +const alice = new Ed25519Keypair() +const aliceCondition = Transaction.makeEd25519Condition(alice.publicKey) +const aliceOutput = Transaction.makeOutput(aliceCondition) +const assetMessage = { assetMessage: 'assetMessage' } +const metaDataMessage = { metaDataMessage: 'metaDataMessage' } +const createTx = Transaction.makeCreateTransaction( + assetMessage, + metaDataMessage, + [aliceOutput], + alice.publicKey +) +const transferTx = Transaction.makeTransferTransaction( + createTx, + metaDataMessage, + [aliceOutput], + 0 +) + + +test('Create valid output with default amount', t => { + const condition = { + details: { + public_key: 'abc' + } + } + const expected = { + amount: '1', + condition, + public_keys: ['abc'] + } + const res = Transaction.makeOutput(condition) + t.deepEqual(res, expected) +}) + + +test('Create valid output with custom amount', t => { + const condition = { + details: { + public_key: 'abc' + } + } + const customAmount = '1337' + const expected = { + amount: customAmount, + condition, + public_keys: ['abc'] + } + const res = Transaction.makeOutput(condition, customAmount) + t.deepEqual(res, expected) +}) + +test('Pass condition not based on public_keys to makeOutput', t => { + const condition = { + details: { + idea: 'just pretend this is e.g. a hashlock' + } + } + const expected = { + amount: '1', + condition, + public_keys: [] + } + const res = Transaction.makeOutput(condition) + t.deepEqual(res, expected) +}) + + +test('makeOutput throws TypeError with incorrect amount type', t => { + t.throws(() => Transaction.makeOutput({}, 1337), TypeError) +}) + + +test('Create TRANSFER transaction based on CREATE transaction', t => { + sinon.spy(makeTransaction, 'default') + + Transaction.makeTransferTransaction( + createTx, + metaDataMessage, + [aliceOutput], + 0 + ) + const expected = [ + 'TRANSFER', + { id: createTx.id }, + metaDataMessage, + [aliceOutput], + [makeInputTemplate( + [alice.publicKey], + { output: 0, transaction_id: createTx.id } + )] + ] + + // NOTE: `src/transaction/makeTransaction` is `export default`, hence we + // can only mock `makeTransaction.default` with a hack: + // See: https://stackoverflow.com/a/33676328/1263876 + t.truthy(makeTransaction.default.calledWith(...expected)) + makeTransaction.default.restore() +}) + + +test('Create TRANSFER transaction based on TRANSFER transaction', t => { + sinon.spy(makeTransaction, 'default') + + Transaction.makeTransferTransaction( + transferTx, + metaDataMessage, + [aliceOutput], + 0 + ) + const expected = [ + 'TRANSFER', + { id: transferTx.asset.id }, + metaDataMessage, + [aliceOutput], + [makeInputTemplate( + [alice.publicKey], + { output: 0, transaction_id: transferTx.id } + )] + ] + + t.truthy(makeTransaction.default.calledWith(...expected)) + makeTransaction.default.restore() +})