OSDN Git Service

add the test case for node-sdk
authorZhiting Lin <zlin035@uottawa.ca>
Fri, 23 Nov 2018 08:36:27 +0000 (16:36 +0800)
committerZhiting Lin <zlin035@uottawa.ca>
Fri, 23 Nov 2018 08:36:27 +0000 (16:36 +0800)
16 files changed:
.gitignore
package-lock.json
package.json
src/client.js
src/connection.js
src/errors.js [new file with mode: 0644]
test/accessToken.js [new file with mode: 0644]
test/account.js
test/assets.js [new file with mode: 0644]
test/balances.js [new file with mode: 0644]
test/block.js [new file with mode: 0644]
test/config.js [new file with mode: 0644]
test/keys.js [new file with mode: 0644]
test/testHelpers.js [new file with mode: 0644]
test/transactions.js [new file with mode: 0644]
test/unspentOutputs.js [new file with mode: 0644]

index f06235c..a317822 100644 (file)
@@ -1,2 +1,4 @@
 node_modules
 dist
+testCompiled
+.idea/
\ No newline at end of file
index 9ee7801..12e6bd4 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bytom-sdk",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
       "dev": true,
       "optional": true
     },
+    "assertion-error": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+      "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+      "dev": true
+    },
     "async-each": {
       "version": "1.0.1",
       "resolved": "http://registry.npm.taobao.org/async-each/download/async-each-1.0.1.tgz",
         "repeat-element": "1.1.2"
       }
     },
+    "browser-stdout": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
+      "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
+      "dev": true
+    },
     "btoa": {
       "version": "1.2.1",
       "resolved": "http://registry.npm.taobao.org/btoa/download/btoa-1.2.1.tgz",
         "underscore-contrib": "0.3.0"
       }
     },
+    "chai": {
+      "version": "3.5.0",
+      "resolved": "http://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+      "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+      "dev": true,
+      "requires": {
+        "assertion-error": "1.1.0",
+        "deep-eql": "0.1.3",
+        "type-detect": "1.0.0"
+      }
+    },
+    "chai-as-promised": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-6.0.0.tgz",
+      "integrity": "sha1-GgKkM6byTa+sY7nJb6FoTbGqjaY=",
+      "dev": true,
+      "requires": {
+        "check-error": "1.0.2"
+      }
+    },
     "chalk": {
       "version": "1.1.3",
       "resolved": "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz",
         "supports-color": "2.0.0"
       }
     },
+    "check-error": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+      "dev": true
+    },
     "chokidar": {
       "version": "1.7.0",
       "resolved": "http://registry.npm.taobao.org/chokidar/download/chokidar-1.7.0.tgz",
         "ms": "2.0.0"
       }
     },
+    "deep-eql": {
+      "version": "0.1.3",
+      "resolved": "http://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+      "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+      "dev": true,
+      "requires": {
+        "type-detect": "0.1.1"
+      },
+      "dependencies": {
+        "type-detect": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+          "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+          "dev": true
+        }
+      }
+    },
     "detect-indent": {
       "version": "4.0.0",
       "resolved": "http://registry.npm.taobao.org/detect-indent/download/detect-indent-4.0.0.tgz",
         "repeating": "2.0.1"
       }
     },
+    "diff": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz",
+      "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=",
+      "dev": true
+    },
     "dom-serializer": {
       "version": "0.1.0",
       "resolved": "http://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.1.0.tgz",
       "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
       "dev": true
     },
+    "graceful-readlink": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+      "dev": true
+    },
+    "growl": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
+      "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
+      "dev": true
+    },
     "has-ansi": {
       "version": "2.0.0",
       "resolved": "http://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz",
       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true
     },
+    "he": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+      "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+      "dev": true
+    },
     "home-or-tmp": {
       "version": "2.0.0",
       "resolved": "http://registry.npm.taobao.org/home-or-tmp/download/home-or-tmp-2.0.0.tgz",
       "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
       "dev": true
     },
+    "json3": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
+      "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
+      "dev": true
+    },
     "json5": {
       "version": "0.5.1",
       "resolved": "http://registry.npm.taobao.org/json5/download/json5-0.5.1.tgz",
       "integrity": "sha1-G3eTz3JZ6jj7NmHU04syYK+K5Oc=",
       "dev": true
     },
+    "lodash._baseassign": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
+      "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=",
+      "dev": true,
+      "requires": {
+        "lodash._basecopy": "3.0.1",
+        "lodash.keys": "3.1.2"
+      }
+    },
+    "lodash._basecopy": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
+      "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
+      "dev": true
+    },
+    "lodash._basecreate": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz",
+      "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
+      "dev": true
+    },
+    "lodash._getnative": {
+      "version": "3.9.1",
+      "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+      "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
+      "dev": true
+    },
+    "lodash._isiterateecall": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
+      "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
+      "dev": true
+    },
     "lodash.clonedeep": {
       "version": "4.5.0",
       "resolved": "http://registry.npm.taobao.org/lodash.clonedeep/download/lodash.clonedeep-4.5.0.tgz",
       "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
       "dev": true
     },
+    "lodash.create": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz",
+      "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=",
+      "dev": true,
+      "requires": {
+        "lodash._baseassign": "3.2.0",
+        "lodash._basecreate": "3.0.3",
+        "lodash._isiterateecall": "3.0.9"
+      }
+    },
     "lodash.escaperegexp": {
       "version": "4.1.2",
       "resolved": "http://registry.npm.taobao.org/lodash.escaperegexp/download/lodash.escaperegexp-4.1.2.tgz",
       "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=",
       "dev": true
     },
+    "lodash.isarguments": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+      "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
+      "dev": true
+    },
+    "lodash.isarray": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
+      "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
+      "dev": true
+    },
     "lodash.isplainobject": {
       "version": "4.0.6",
       "resolved": "http://registry.npm.taobao.org/lodash.isplainobject/download/lodash.isplainobject-4.0.6.tgz",
       "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=",
       "dev": true
     },
+    "lodash.keys": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
+      "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
+      "dev": true,
+      "requires": {
+        "lodash._getnative": "3.9.1",
+        "lodash.isarguments": "3.1.0",
+        "lodash.isarray": "3.0.4"
+      }
+    },
     "lodash.mergewith": {
       "version": "4.6.1",
       "resolved": "http://registry.npm.taobao.org/lodash.mergewith/download/lodash.mergewith-4.6.1.tgz",
         "minimist": "0.0.8"
       }
     },
+    "mocha": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz",
+      "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==",
+      "dev": true,
+      "requires": {
+        "browser-stdout": "1.3.0",
+        "commander": "2.9.0",
+        "debug": "2.6.8",
+        "diff": "3.2.0",
+        "escape-string-regexp": "1.0.5",
+        "glob": "7.1.1",
+        "growl": "1.9.2",
+        "he": "1.1.1",
+        "json3": "3.3.2",
+        "lodash.create": "3.1.1",
+        "mkdirp": "0.5.1",
+        "supports-color": "3.1.2"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.9.0",
+          "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+          "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+          "dev": true,
+          "requires": {
+            "graceful-readlink": "1.0.1"
+          }
+        },
+        "debug": {
+          "version": "2.6.8",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+          "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.1",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+          "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "1.0.0",
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        },
+        "has-flag": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz",
+          "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
+          "dev": true,
+          "requires": {
+            "has-flag": "1.0.0"
+          }
+        }
+      }
+    },
     "moment": {
       "version": "2.22.1",
       "resolved": "http://registry.npm.taobao.org/moment/download/moment-2.22.1.tgz",
       "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
       "dev": true
     },
+    "type-detect": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+      "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+      "dev": true
+    },
     "underscore": {
       "version": "1.8.3",
       "resolved": "http://registry.npm.taobao.org/underscore/download/underscore-1.8.3.tgz",
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
       "dev": true
     },
+    "uuid": {
+      "version": "3.0.1",
+      "resolved": "http://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
+      "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=",
+      "dev": true
+    },
     "v8flags": {
       "version": "2.1.1",
       "resolved": "http://registry.npm.taobao.org/v8flags/download/v8flags-2.1.1.tgz",
index 8e17708..a55c066 100644 (file)
@@ -5,7 +5,7 @@
   "main": "dist/index.js",
   "repository": {
     "type": "git",
-    "url": "https://github.com/Bytom/node-sdk.git"
+    "url": "https://github.com/Bytom/bytom-node-sdk.git"
   },
   "license": "Apache-2.0",
   "engines": {
   "scripts": {
     "docs": "jsdoc -c jsdoc.conf.json",
     "build": "babel src --out-dir dist",
+    "test": "mocha testCompiled",
+    "pretest": "npm run build && babel test --out-dir testCompiled",
     "prepublish": "npm run build"
   },
   "devDependencies": {
     "babel-cli": "^6.26.0",
     "babel-preset-es2015": "^6.24.1",
     "ink-docstrap": "^1.3.2",
-    "jsdoc": "^3.5.5"
+    "jsdoc": "^3.5.5",
+    "mocha": "^3.2.0",
+    "uuid": "~3.0.0",
+    "chai": "^3.5.0",
+    "chai-as-promised": "^6.0.0"
   },
   "dependencies": {
     "axios": "^0.18.0",
index 7c6c7b7..708e877 100644 (file)
@@ -6,6 +6,8 @@ import transactionApi from './api/transactions'
 import balancesApi from './api/balances'
 import unspentOutputsAPI from './api/unspentOutputs'
 import accessTokensApi from './api/accessTokens'
+import configAPI from './api/config'
+import blockAPI from './api/block'
 
 class Client {
   constructor(baseUrl, token) {
@@ -18,6 +20,8 @@ class Client {
     this.balances = new balancesApi(this.connection)
     this.unspentOutputs = new unspentOutputsAPI(this.connection)
     this.accessTokens = new accessTokensApi(this.connection)
+    this.status = new configAPI(this.connection)
+    this.block = new blockAPI(this.connection)
   }
 }
 
index effda3c..08ea7f8 100644 (file)
@@ -1,5 +1,6 @@
 import axios from 'axios'
 import btoa from 'btoa'
+const errors = require('./errors')
 
 class Connection {
   constructor(baseUrl, token = '') {
@@ -24,13 +25,16 @@ class Connection {
 
     return axios.request(config).then(resp => {
       if (resp.data.status === 'fail') {
-        throw resp.data.msg
+        throw errors.formatErrMsg(resp.data)
       } else if (resp.data.status === 'success') {
         return resp.data.data
       }
 
       return resp.data
     })
+      .catch(error=>{
+        throw error
+      })
   }
 }
 
diff --git a/src/errors.js b/src/errors.js
new file mode 100644 (file)
index 0000000..4ea645b
--- /dev/null
@@ -0,0 +1,60 @@
+const lib = {
+  create: function(type, message, props = {}) {
+    let err
+    if (props.body) {
+      err = lib.newBatchError(props.body, props.requestId)
+    } else {
+      err = new Error(message)
+    }
+
+    err = Object.assign(err, props, {
+      bytomClientError: true,
+      type: type,
+    })
+    return err
+  },
+
+  isbytomError: function(err) {
+    return err && !!err.bytomClientError
+  },
+
+  isBatchError: function (v) {
+    return v && v.code && !v.stack
+  },
+
+  newBatchError: function (body, requestId = false) {
+    let err = new Error(lib.formatErrMsg(body, requestId))
+    err.code = body.code
+    err.bytomMessage = body.msg
+    err.detail = body.error_detail
+    return err
+  },
+
+  formatErrMsg: function(body) {
+    let tokens = []
+
+    if (typeof body.code === 'string' && body.code.length > 0) {
+      tokens.push('Code: ' + body.code)
+    }
+
+    tokens.push('Message: ' + body.msg)
+
+    if (typeof body.error_detail === 'string' && body.error_detail.length > 0) {
+      tokens.push('Detail: ' + body.error_detail)
+    }
+
+    return tokens.join(' ')
+  },
+
+  types: {
+    FETCH: 'FETCH',
+    CONNECTIVITY: 'CONNECTIVITY',
+    JSON: 'JSON',
+    UNAUTHORIZED: 'UNAUTHORIZED',
+    NOT_FOUND: 'NOT_FOUND',
+    BAD_REQUEST: 'BAD_REQUEST',
+    SERVER_ERROR: 'SERVER_ERROR',
+  }
+}
+
+module.exports = lib
diff --git a/test/accessToken.js b/test/accessToken.js
new file mode 100644 (file)
index 0000000..f25c13e
--- /dev/null
@@ -0,0 +1,70 @@
+/* eslint-env mocha */
+
+const bytom = require('../dist/index.js')
+const uuid = require('uuid')
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const url = 'http://localhost:9888'
+const accessToken = ''
+
+const client = new bytom.Client(url, accessToken)
+
+function createToken() {
+  return client.accessTokens.create({
+    id: `token-${uuid.v4()}`
+  })
+}
+
+describe('Access token', () => {
+
+  it('creation successful', () => {
+    return client.accessTokens.create({
+      id: `another-${uuid.v4()}`
+    }).then(resp => expect(resp.token).not.to.be.empty)
+  })
+
+  it('creation rejected due to duplicate ID', () => {
+    return createToken()
+      .then((token) => expect(client.accessTokens.create({
+        id: token.id
+      })).to.be.rejectedWith('BTM000'))
+  })
+
+  it('returned in list after creation', () => {
+    let tokenId
+    return createToken()
+      .then((token) => {
+        tokenId = token.id
+        return client.accessTokens.list()
+      })
+      .then(resp => expect(resp.map(item => item.id)).to.contain(tokenId))
+  })
+
+  it('deletion successful', () => {
+    return createToken()
+      .then((token) => client.accessTokens.delete(token.id))
+      .then(resp => expect(resp).to.be.empty)
+  })
+
+  it('deletion rejected due to missing ID', () => {
+    return createToken()
+      .then(() => expect(client.accessTokens.delete())
+        .to.be.rejectedWith('BTM000'))
+  })
+
+  it('removed from list after deletion', () => {
+    let tokenId
+    return createToken()
+      .then((token) => {
+        tokenId = token.id
+        return client.accessTokens.delete(tokenId)
+      })
+      .then(() => client.accessTokens.list())
+      .then(resp => expect(resp.map(item => item.id)).to.not.contain(tokenId))
+  })
+
+})
\ No newline at end of file
index e69de29..414912c 100644 (file)
@@ -0,0 +1,158 @@
+/* eslint-env mocha */
+
+const bytom = require('../dist/index.js')
+const uuid = require('uuid')
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const url = 'http://localhost:9888'
+const accessToken = ''
+
+const client = new bytom.Client(url, accessToken)
+
+const xAccountAlias = `x-${uuid.v4()}`
+const yAccountAlias = `y-${uuid.v4()}`
+
+let mockHsmKey
+
+describe('Account', () => {
+
+  before('set up API objects', () => {
+
+    // Key and account creation
+    return client.keys.listAll()
+      .then(keys => { mockHsmKey = keys[0] })
+      .then(() => {
+        return client.accounts.create({alias: xAccountAlias, root_xpubs: [mockHsmKey.xpub], quorum: 1 })
+      })
+      .then(() => {
+        return client.accounts.create({alias: yAccountAlias, root_xpubs: [mockHsmKey.xpub], quorum: 1})
+      })
+  })
+
+  describe('Single account creation', () => {
+
+    it('successful', () => {
+      return client.accounts.create({alias: `alice-${uuid.v4()}`, root_xpubs: [mockHsmKey.xpub], quorum: 1})
+        .then(resp => expect(resp.id).not.to.be.empty)
+    })
+
+    it('rejected due to missing key fields', () => {
+      return expect(client.accounts.create({alias: 'david'})).to.be.rejectedWith('BTM202')
+    })
+  })
+
+  describe('Single account alias update', () => {
+
+    it('successful', () => {
+      const newAlias = `alice-${uuid.v4()}`
+      return client.accounts.updateAlias({
+        account_alias: xAccountAlias,
+        new_alias: newAlias
+      })
+        .then(() => {
+          return client.accounts.list({
+            alias: newAlias
+          })
+        })
+        .then(resp => expect(resp[0].alias).to.include(newAlias))
+    })
+
+    it('rejected due to missing ID/Alias', () => {
+      return expect(
+        client.accounts.updateAlias({
+          // ID/Alias intentionally omitted
+          new_alias: 'new'
+        })
+      ).to.be.rejectedWith('BTM709')
+    })
+  })
+
+
+  describe('Delete Account', () => {
+
+    it('successful', () => {
+      const alias = `alice-${uuid.v4()}`
+      return client.accounts.create({alias: alias, root_xpubs: [mockHsmKey.xpub], quorum: 1})
+        .then(() =>{
+          return client.accounts.delete({
+            account_alias: alias
+          })
+        })
+        .then(resp => expect(resp).to.be.empty)
+    })
+  })
+
+  describe('Account Receiver', () => {
+
+    it('created Account Reciever', () => {
+      return client.accounts.create({alias: xAccountAlias, root_xpubs: [mockHsmKey.xpub], quorum: 1})
+        .then(() =>{
+          return client.accounts.createReceiver({account_alias: xAccountAlias})
+        })
+        .then(resp => expect(resp.address).to.not.be.empty)
+    })
+
+    it('list Account Reciever by alias', () => {
+      let address
+      const alias = `connie-${uuid.v4()}`
+
+      return client.accounts.create({alias: alias, root_xpubs: [mockHsmKey.xpub], quorum: 1})
+        .then(() =>{
+          return client.accounts.createReceiver({account_alias: alias})
+        })
+        .then((resp) =>{
+          address = resp.address
+          return client.accounts.listAddresses({
+            account_alias: alias
+          })
+        })
+        .then(resp =>
+          expect(resp.map(item => item.address)).to.include(address)
+        )
+    })
+
+    it('list Account Reciever by Id', () => {
+      let address, id
+      const alias = `connie-${uuid.v4()}`
+
+      return client.accounts.create({alias: alias, root_xpubs: [mockHsmKey.xpub], quorum: 1})
+        .then((resp) =>{
+          id = resp.id
+          return client.accounts.createReceiver({account_id: id})
+        })
+        .then((resp) =>{
+          address = resp.address
+          return client.accounts.listAddresses({
+            account_id: id
+          })
+        })
+        .then(resp =>
+          expect(resp.map(item => item.address)).to.include(address)
+        )
+    })
+
+  })
+
+  describe('list All Account', () => {
+    it('success example', () => {
+      let created
+
+      return client.accounts.create({
+        alias: `bob-${uuid.v4()}`,
+        root_xpubs: [mockHsmKey.xpub],
+        quorum: 1
+      }).then(account =>
+        created = account.id
+      ).then(() =>
+        client.accounts.listAll()
+      ).then((resp) =>
+        expect(resp.map(item => item.id)).to.include(created)
+      )
+    })
+  })
+
+})
\ No newline at end of file
diff --git a/test/assets.js b/test/assets.js
new file mode 100644 (file)
index 0000000..0b9917e
--- /dev/null
@@ -0,0 +1,126 @@
+/* eslint-env mocha */
+
+const bytom = require('../dist/index.js')
+const uuid = require('uuid')
+const chai = require('chai')
+const assert = require('assert')
+const chaiAsPromised = require('chai-as-promised')
+
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const url = 'http://localhost:9888'
+const accessToken = ''
+
+const client = new bytom.Client(url, accessToken)
+
+const xAssetAlias = `x-${uuid.v4()}`
+const yAssetAlias = `y-${uuid.v4()}`
+
+let mockHsmKey, xAssetId, yAssetId
+
+describe('Asset', () => {
+
+  before('set up API objects', () => {
+
+    // Key and asset creation
+    return client.keys.listAll()
+      .then(keys => { mockHsmKey = keys[0] })
+      .then(() => {
+        return client.assets.create(
+          {
+            alias: xAssetAlias,
+            definition: {
+              decimals: 8,
+              description: {},
+              name: "TESTASSET1",
+              symbol: "TESTASSET1"
+            },
+            root_xpubs: [mockHsmKey.xpub],
+            quorum: 1}
+            )
+          .then((resp)=>{
+            xAssetId = resp.id
+          })
+      })
+      .then(() => {
+        return client.assets.create({
+          alias: yAssetAlias,
+          definition: {
+            decimals: 8,
+            description: {},
+            name: "TESTASSET2",
+            symbol: "TESTASSET2"
+          },
+          root_xpubs: [mockHsmKey.xpub],
+          quorum: 1})
+          .then((resp)=>{
+            yAssetId = resp.id
+          })
+      })
+  })
+
+  describe('Single asset creation', () => {
+
+    it('successful', () => {
+      return client.assets.create({
+        alias: `asset-${uuid.v4()}`,
+        definition: {
+          decimals: 8,
+          description: {},
+          name: `TESTASSET-${uuid.v4()}`,
+          symbol: `TESTASSET-${uuid.v4()}`
+        },
+        root_xpubs: [mockHsmKey.xpub],
+        quorum: 1})
+        .then(resp => expect(resp.id).not.to.be.empty)
+    })
+
+    it('rejected due to missing key fields', () => {
+      return expect(client.assets.create({alias: 'asset'})).to.be.rejectedWith('BTM202')
+    })
+  })
+
+
+  describe('Single asset alias update', () => {
+
+    it('successful', () => {
+      const alias = `asset-${uuid.v4()}`
+      return client.assets.updateAlias({
+        id: xAssetId,
+        alias: alias
+      })
+        .then(() => {
+          return client.assets.list(xAssetId)
+        })
+        .then(page => {
+          assert.deepEqual(page.alias, alias.toUpperCase())
+        })
+    })
+
+    it('rejected due to missing ID/Alias', () => {
+      return expect(
+        client.assets.updateAlias({
+          // ID/Alias intentionally omitted
+          alias: `asset-${uuid.v4()}`,
+        })
+      ).to.be.rejectedWith('BTM000')
+    })
+  })
+
+  describe('listAll', () => {
+    it('success example', () => {
+      return client.assets.listAll()
+      .then((resp) => {
+        expect(resp.map(item => item.alias)).to.be.an('array').that.include(yAssetAlias.toUpperCase())
+      })
+    })
+
+    it('list success', () => {
+      return client.assets.list(xAssetId)
+      .then(page => {
+        assert.deepEqual(page.id, xAssetId)
+      })
+    })
+  })
+})
\ No newline at end of file
diff --git a/test/balances.js b/test/balances.js
new file mode 100644 (file)
index 0000000..b5a2ff8
--- /dev/null
@@ -0,0 +1,32 @@
+/* eslint-env mocha */
+
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const {
+  client
+} = require('./testHelpers')
+
+describe('Balance', () => {
+
+  describe('list by specific account', () => {
+    it('simple example', () =>
+      client.balances.list({
+        account_alias: 'default',
+      }).then(items =>
+        expect(items[0].amount).not.to.be.empty
+      )
+    )
+  })
+
+  describe('listAll', () => {
+    it('simple example', () => {
+      return client.balances.listAll().then((resp) => {
+        expect(resp.find(b => b.account_alias == 'default').amount).not.to.be.empty
+      })
+    })
+  })
+
+})
\ No newline at end of file
diff --git a/test/block.js b/test/block.js
new file mode 100644 (file)
index 0000000..9155e7a
--- /dev/null
@@ -0,0 +1,87 @@
+/* eslint-env mocha */
+
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const {
+  client
+} = require('./testHelpers')
+
+describe('Block', () => {
+
+  describe('block count', () => {
+    it('simple example', () =>
+      client.block.getBlockCount()
+        .then(items =>
+          expect(items.block_count).not.to.be.empty
+        )
+    )
+  })
+
+  describe('block hash', () => {
+    it('simple example', () => {
+      return client.block.getBlockHash()
+        .then((resp) => {
+          expect(resp.block_hash).not.to.be.empty
+        })
+    })
+  })
+
+  describe('get Block', () => {
+    it('simple example', () => {
+      let blockHash
+      return client.block.getBlockHash()
+        .then((resp) => {
+          blockHash = resp.block_hash
+          return client.block.getBlock({block_hash: resp.block_hash})
+        })
+        .then((resp) =>
+          expect(resp.hash).to.equal(blockHash)
+        )
+    })
+  })
+
+  describe('get Block Header', () => {
+    it('simple example', () => {
+      let blockHash
+      return client.block.getBlockHash()
+        .then((resp) => {
+          blockHash = resp.block_hash
+          return client.block.getBlockHeader({block_hash: resp.block_hash})
+        })
+        .then((resp) =>
+          expect(resp.block_header).not.to.be.empty
+        )
+    })
+  })
+
+  describe('get Difficulty', () => {
+    it('simple example', () => {
+      let blockHash
+      return client.block.getBlockHash()
+        .then((resp) => {
+          blockHash = resp.block_hash
+          return client.block.getDifficulty({block_hash: resp.block_hash})
+        })
+        .then((resp) =>
+          expect(resp.hash).to.equal(blockHash)
+        )
+    })
+  })
+
+  describe('get Hash Rate', () => {
+    it('simple example', () => {
+      let blockHash
+      return client.block.getBlockHash()
+        .then((resp) => {
+          blockHash = resp.block_hash
+          return client.block.getHashRate({block_hash: resp.block_hash})
+        })
+        .then((resp) =>
+          expect(resp.hash).to.equal(blockHash)
+        )
+    })
+  })
+})
\ No newline at end of file
diff --git a/test/config.js b/test/config.js
new file mode 100644 (file)
index 0000000..54fe676
--- /dev/null
@@ -0,0 +1,32 @@
+/* eslint-env mocha */
+
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const {
+  client
+} = require('./testHelpers')
+
+describe('Core Status', () => {
+
+  describe('gasRate', () => {
+    it('simple example', () =>
+      client.status.gasRate()
+        .then(items =>
+        expect(items.gas_rate).not.to.be.empty
+      )
+    )
+  })
+
+  describe('netInfo', () => {
+    it('simple example', () => {
+      return client.status.netInfo()
+        .then((resp) => {
+        expect(resp).not.to.be.empty
+      })
+    })
+  })
+
+})
\ No newline at end of file
diff --git a/test/keys.js b/test/keys.js
new file mode 100644 (file)
index 0000000..9bd8c7e
--- /dev/null
@@ -0,0 +1,96 @@
+/* eslint-env mocha */
+
+const bytom = require('../dist/index.js')
+const uuid = require('uuid')
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const url = 'http://localhost:9888'
+const accessToken = ''
+
+const client = new bytom.Client(url, accessToken)
+
+const keyAlias = `key-${uuid.v4()}`
+const password = '12345'
+
+let keyXpub
+
+before('set up API objects', () => {
+
+  // Key creation
+  return client.keys.create({
+    alias: keyAlias,
+    password: password,
+  })
+    .then((resp)=>{
+      keyXpub = resp.xpub
+    })
+})
+
+describe('key', () => {
+
+  it('successfully creates key', () => {
+    return client.keys.create({
+      alias: `key-${uuid.v4()}`,
+      password: password,
+    })
+      .then((resp) => expect(resp).not.to.be.empty)
+  })
+
+  it('rejects key creation due to duplicate alias', () => {
+    return expect(client.keys.create({ alias: keyAlias, password: password })).to.be.rejectedWith('BTM800')
+  })
+
+  it('returns key in list after key creation, listAll', () => {
+    let keyAlias
+    return client.keys.create({ alias: `key-${uuid.v4()}` , password: password })
+      .then((key) => {
+        keyAlias = key.alias
+        return client.keys.listAll()
+      })
+      .then(resp => {
+        return expect(resp.map(item => item.alias)).to.contain(keyAlias)
+      })
+  })
+
+  describe('password', () => {
+    it('successfully check correct password', () => {
+      return client.keys.checkPassword({
+          xpub: keyXpub,
+          password:password
+        })
+      .then((resp) => {
+        expect(resp.check_result).to.be.true
+      })
+    })
+
+    it('successfully check password wrong', () => {
+      return client.keys.checkPassword({
+        xpub: keyXpub,
+        password:'random'
+      })
+        .then((resp) => {
+          expect(resp.check_result).to.be.false
+        })
+    })
+
+    it('successfully reset password', () => {
+      const newPassword = '11111'
+      return client.keys.resetPassword({
+        xpub: keyXpub,
+        old_password: password,
+        new_password: newPassword
+      })
+        .then((resp) => {
+          expect(resp.changed).to.be.true
+        })
+    })
+
+
+
+  })
+
+})
\ No newline at end of file
diff --git a/test/testHelpers.js b/test/testHelpers.js
new file mode 100644 (file)
index 0000000..eff5e2f
--- /dev/null
@@ -0,0 +1,70 @@
+const bytom = require('../dist/index.js')
+const uuid = require('uuid')
+const url = 'http://localhost:9888'
+const accessToken = ''
+
+const client = new bytom.Client(url, accessToken)
+
+const balanceByAssetAlias = (balances) => {
+  let res = {}
+  return Promise.resolve(balances)
+    .then((balance) => {
+      balance.forEach((item) => {
+        res[item.sumBy.assetAlias] = item.amount
+      })
+      return res
+    })
+}
+
+const createAccount = (account = 'account') => {
+  return client.keys.listAll()
+    .then((keys) => {
+      return client.accounts.create({
+        alias: `${account}-${uuid.v4()}`,
+        root_xpubs: [keys[0].xpub],
+        quorum: 1
+      })
+    })
+}
+
+const createAccountReciever = (accountAlias = 'account') => {
+  return client.accounts.createReceiver({
+    account_alias: accountAlias
+  })
+}
+
+const createAsset = (asset = 'asset') => {
+  return client.keys.listAll()
+    .then((keys) => {
+      return client.assets.create({
+        alias: `${asset}-${uuid.v4()}`,
+        definition: {
+          decimals: 8,
+          description: {},
+          name: `${asset}-${uuid.v4()}`,
+          symbol: `${asset}-${uuid.v4()}`
+        },
+        root_xpubs: [keys[0].xpub],
+        quorum: 1
+      })
+    })
+}
+
+const buildSignSubmit = (buildFunc, optClient, password) => {
+  const c = optClient || client
+  return c.transactions.build(buildFunc)
+    .then(tpl => c.transactions.sign({
+      transaction: tpl,
+      password
+    }))
+    .then(tpl => c.transactions.submit(tpl.transaction.raw_transaction))
+}
+
+module.exports = {
+  // balanceByAssetAlias,
+  client,
+  createAccount,
+  createAsset,
+  createAccountReciever,
+  buildSignSubmit,
+}
\ No newline at end of file
diff --git a/test/transactions.js b/test/transactions.js
new file mode 100644 (file)
index 0000000..4be9f3b
--- /dev/null
@@ -0,0 +1,197 @@
+
+/* eslint-env mocha */
+
+const assert = require('assert')
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const { balanceByAssetAlias, client, createAccount, createAsset, createAccountReciever } = require('./testHelpers')
+
+const btmAlias = 'BTM'
+
+describe('Transaction', () => {
+  describe('Transfer btm', () => {
+    let aliceAlias, bobAlias, aliceAddress, bobAddress, txId
+
+    before(() => {
+      return Promise.all([
+        createAccount('alice'),
+        createAccount('bob')
+      ])
+        .then((objects) => {
+          aliceAlias = objects[0].alias
+          bobAlias = objects[1].alias
+          return Promise.all([
+            createAccountReciever(aliceAlias),
+            createAccountReciever(bobAlias)
+          ])
+        })
+        .then((objects) => {
+          aliceAddress = objects[0].address
+          bobAddress = objects[1].address
+        })
+        .then(() => client.transactions.build(builder => {
+          builder.spendFromAccount({
+            account_alias: 'default',
+            asset_alias: btmAlias,
+            amount: 310000000
+          })
+          builder.controlWithAddress({
+            address: aliceAddress,
+            asset_alias: btmAlias,
+            amount: 100000000
+          })
+          builder.controlWithAddress({
+            address: bobAddress,
+            asset_alias: btmAlias,
+            amount: 200000000
+          })
+        }))
+        .then((issuance) =>{
+          return client.transactions.sign({transaction: issuance, password: '12345'})})
+        .then((signed) => {
+          return client.transactions.submit(signed.transaction.raw_transaction)
+        })
+        .then((tx) => txId= tx.tx_id)
+    })
+
+    it('transfer 1 BTM to alice', () => {
+      return client.transactions.list({id: txId, unconfirmed:true})
+        .then((resp) => {
+          expect(resp[0].outputs.map(item => item.account_alias)).include(aliceAlias)
+        })
+    })
+
+    it('transfer 1 BTM to bob', () => {
+      return client.transactions.list({id: txId, unconfirmed:true})
+        .then((resp) => {
+          expect(resp[0].outputs.map(item => item.account_alias)).include(bobAlias)
+        })
+    })
+  })
+
+  describe('Issuance', () => {
+    let goldAlias, silverAlias, aliceAlias, bobAlias,
+      aliceAddress, bobAddress, txId
+
+    before(() => {
+      return Promise.all([
+        createAsset('gold'),
+        createAsset('silver'),
+        createAccount('alice'),
+        createAccount('bob')
+      ])
+        .then((objects) => {
+          goldAlias = objects[0].alias
+          silverAlias = objects[1].alias
+          aliceAlias = objects[2].alias
+          bobAlias = objects[3].alias
+          return Promise.all([
+            createAccountReciever(aliceAlias),
+            createAccountReciever(bobAlias)
+          ])
+        })
+        .then((objects) => {
+          aliceAddress = objects[0].address
+          bobAddress = objects[1].address
+        })
+        .then(() => client.transactions.build(builder => {
+          builder.spendFromAccount({
+            account_alias: 'default',
+            asset_alias: btmAlias,
+            amount: 10000000
+          })
+          builder.issue({
+            asset_alias: goldAlias,
+            amount: 1000000
+          })
+          builder.issue({
+            asset_alias: silverAlias,
+            amount: 2000000
+          })
+          builder.controlWithAddress({
+            address: aliceAddress,
+            asset_alias: goldAlias,
+            amount: 1000000
+          })
+          builder.controlWithAddress({
+            address: bobAddress,
+            asset_alias: silverAlias,
+            amount: 2000000
+          })
+        }))
+        .then((issuance) => client.transactions.sign({transaction: issuance, password: '12345'}))
+        .then((signed) => client.transactions.submit(signed.transaction.raw_transaction))
+        .then((tx) => txId= tx.tx_id)
+    })
+
+    it('issues 100 units of gold to alice', () => {
+      return client.transactions.list({id: txId, unconfirmed:true})
+        .then((resp) => {
+          expect(resp[0].outputs.map(item => item.account_alias)).include(aliceAlias)
+        })
+    })
+
+    it('issues 200 units of silver to bob', () => {
+      return client.transactions.list({id: txId, unconfirmed:true})
+        .then((resp) => {
+          expect(resp[0].outputs.map(item => item.account_alias)).include(bobAlias)
+        })
+    })
+  })
+
+
+
+  describe('listAll', () => {
+    it('success example', () => {
+      let created, accountAlias
+
+      return createAccount().then((account) => {
+          accountAlias = account.alias
+          return createAccountReciever(accountAlias)
+        }
+      ).then((resp) =>
+        client.transactions.build(builder => {
+          builder.spendFromAccount({
+            account_alias: 'default',
+            asset_alias: btmAlias,
+            amount: 100000000
+          })
+          builder.controlWithAddress({
+            address: resp.address,
+            asset_alias: btmAlias,
+            amount: 90000000
+          })
+        })
+      ).then(txtpl =>
+        client.transactions.sign({
+          transaction: txtpl,
+          password: '12345'
+        })
+      ).then(signed =>
+        client.transactions.submit(signed.transaction.raw_transaction)
+      ).then(tx =>
+        created = tx.tx_id
+      ).then(() =>
+        client.transactions.listAll()
+      ).then(resp =>{
+        expect(resp.map(item => item.tx_id)).to.include(created)
+      }
+      )
+    })
+  })
+
+  describe('Builder function errors', () => {
+    it('rejects via promise', () =>
+      expect(
+        client.transactions.build(() => {
+          throw new Error("test error")
+        })
+      ).to.be.rejectedWith("test error")
+    )
+  })
+
+})
\ No newline at end of file
diff --git a/test/unspentOutputs.js b/test/unspentOutputs.js
new file mode 100644 (file)
index 0000000..56ab733
--- /dev/null
@@ -0,0 +1,22 @@
+/* eslint-env mocha */
+
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+chai.use(chaiAsPromised)
+const expect = chai.expect
+
+const {
+  client,
+} = require('./testHelpers')
+
+describe('Unspent output', () => {
+
+  describe('listAll', () => {
+    it('simple example', () => {
+      return client.unspentOutputs.listAll().then((resp) => {
+        expect(resp).not.to.be.empty
+      })
+    })
+  })
+
+})
\ No newline at end of file