node-stone
Version:
A library for creating, validating and verifying stones
378 lines (318 loc) • 11.8 kB
JavaScript
var test = require('tape');
var stone = require("../");
var fx = require('node-fixtures');
var moment = require('moment');
var _ = require("lodash");
var validator = require("../validate");
var utility = require("../utility");
var ursa = require('ursa');
validator.setStartTime(1453975575);
fx.stones.valid[0].meta.created_at = moment().unix();
fx.stones.valid[1].meta.created_at = moment().unix();
fx.stones.valid[2].meta.created_at = moment().unix();
fx.stones.valid[3].meta.created_at = moment().unix();
fx.stones.valid[3].embeds.data[0].meta.created_at = moment().unix();
var privateKey = fx.keys.valid[0];
var publicKey = fx.keys.valid[1];
test('.load()', function (t) {
t.test("test different invalid parameters", function(st){
var cases = [
{ param: "", expected: "cannot load empty string", msg: "cannot pass empty string" },
{ param: '{ "x": }', expected: "failed to load. JSON string is malformed", msg: "cannot pass malformed json" },
{ param: 123, expected: "unsupported parameter", msg: "parameter type is not supported" }
];
for (var i=0; i < cases.length; i++) {
var result = stone.load(cases[i].param);
st.equal(result instanceof Error, true, "error is expected")
st.equal(result.message, cases[i].expected, cases[i].msg);
}
st.end()
});
t.test("successfully load json object and json string", function(st){
var data = _.clone(fx.stones.valid[0]);
var cases = [
{ param: data, expected: stone.Stone, msg: "successfully loaded a json object" },
{ param: JSON.stringify(data), expected: stone.Stone, msg: "successfully loaded a json string" },
];
for (var i=0; i < cases.length; i++) {
var result = stone.load(cases[i].param);
st.equal(result instanceof cases[i].expected, true, cases[i].msg)
}
st.end()
})
});
test('.create()', function (t) {
t.test("call without private key", function(st){
var result = stone.create({}, null).catch(function(err){
var expected = "private key is required for signing"
st.equal(err.message, expected, "private key is required");
st.end()
});
});
t.test("call with invalid meta information", function(st){
var result = stone.create({}, privateKey).catch(function(err){
var expected = "`meta` block is missing `id` property"
st.equal(err.message, expected, "private key is required");
st.end()
});
});
});
test('.sign()', function(t) {
t.test('fail to sign: private is not passed', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.sign("meta", null).catch(function(err){
st.equal(err.message, 'private key is required for signing', "must provide a private key");
st.end();
});
});
t.test('fail to sign: private key is invalid', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.sign("meta", publicKey).catch(function(err){
st.equal(err.message, 'private key is invalid', "private key is invalid");
st.end();
});
});
t.test('failed to sign: sign unknown block', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.sign("xxx", privateKey).catch(function(e){
st.equal(e.message, 'block unknown');
st.end();
});
});
t.test('failed to sign: block is empty', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.sign("embeds", privateKey).catch(function(e){
st.equal(e.message, 'cannot sign empty block', 'cannot sign empty block');
st.end();
});
});
t.test('sign meta block', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
st.equal(stn.signatures.meta, undefined, "should not have signatures.meta set");
stn.sign("meta", privateKey).then(function(signature){
st.notEqual(stn.signatures.meta, undefined, "should have a signatures.meta set");
st.equal(stn.signatures.meta, signature, "signature from sign() should be equal to signatures.meta");
st.end();
});
});
});
test('.verify()', function(t) {
t.test('fail to sign: unknown block name', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.verify("unknown_block", publicKey).catch(function(err){
st.equal(err.message, 'block unknown', "cannot pass unknown block name");
st.end();
});
});
t.test('fail to sign: public key is not passed', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.verify("meta", null).catch(function(err){
st.equal(err.message, 'public key is required for verifying', "must provide a public key");
st.end();
});
});
t.test('fail to sign: public key is invalid', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.verify("meta", privateKey).catch(function(err){
st.equal(err.message, 'public key is invalid', "must provide a valid public key");
st.end();
});
});
t.test('fail to sign: block has no signature', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.verify("meta", publicKey).catch(function(err){
st.equal(err.message, 'block `meta` has no signature', "cannot verify a block that has no signature");
st.end();
});
});
t.test('successfully verify a block', function(st){
var data = _.clone(fx.stones.valid[0]);
var stn = stone.load(data);
stn.sign("meta", privateKey).then(function(err){
stn.verify("meta", publicKey).then(function(verified){
st.equal(verified, true, "successfully verified block");
st.end();
});
});
});
});
test('.toJSON()', function (t) {
t.test("succeed in converting to json", function(st){
var s = stone.load(fx.stones.valid[0])
var stoneJSON = s.toJSON();
st.equal(_.isPlainObject(stoneJSON), true, "type must be json object");
st.notEqual(stoneJSON.meta, undefined, "must have meta property");
st.notEqual(stoneJSON.attributes, undefined, "must have attributes property");
st.notEqual(stoneJSON.ownership, undefined, "must have ownership property");
st.notEqual(stoneJSON.embeds, undefined, "must have embeds property");
st.notEqual(stoneJSON.signatures, undefined, "must have signatures property")
st.deepEqual(s, stone.load(stoneJSON), "both objects must be equal");
st.end()
});
});
test('.isValid()', function (t) {
t.test("succeed in checking validity", function(st){
var s = stone.load(_.cloneDeep(fx.stones.valid[0]))
st.equal(s.isValid(), true, "should be true");
delete s.meta.created_at;
st.equal(s.isValid(), false, "should be false since meta block is invalid");
st.end()
});
});
test('.encode()', function (t) {
t.test("successfully encode stone signatures", function(st){
var s = stone.load(fx.stones.valid[0]);
var enc = s.encode();
st.equal(typeof enc, "string", "should be a string");
st.end()
});
});
test('.decode()', function (t) {
t.test("failed because encoded signature is invalid", function(st){
var result = stone.decode("xxx");
st.equal(result.message, "failed to decode", "cannot decoded an invalid encoded stone signature");
st.end()
});
t.test("failed because signatures.meta is not a valid JWS signature", function(st){
var s = stone.load(fx.stones.valid[0]);
s.sign("meta", privateKey).then(function(signature){
s.signatures.meta = "xxx.xxx";
var enc = s.encode();
var result = stone.decode(enc);
st.equal(result.message, "parameter is not a valid JWS signature", "cannot decoded encoded signature with invalid signatures.meta signature");
st.end()
});
});
t.test("successfully encode stone signatures", function(st){
var s = stone.load(fx.stones.valid[0]);
s.sign("meta", privateKey).then(function(signature){
var enc = s.encode();
var decodedStone = stone.decode(enc);
st.equal(decodedStone instanceof stone.Stone, true, "should be an instance of stone.Stone");
st.equal(s.signatures.meta, decodedStone.signatures.meta)
st.end()
});
});
});
test('.clone()', function (t) {
t.test("succeed in cloning a stone instance", function(st){
var s = stone.load(fx.stones.valid[0]);
var clone = s.clone();
st.equal(clone instanceof stone.Stone, true, "should be a stone instance");
st.deepEqual(clone, s, "original stone content should be equal to clone's content");
s.meta.id = "xxx"
st.notDeepEqual(clone, s, "update to original object should not affect cloned object");
st.end()
});
});
test('.hasOwnership()', function (t) {
t.test("succeed in checking availability of ownership information", function(st){
var s = stone.load(fx.stones.valid[1]);
var s2 = s.clone();
delete s2.ownership;
st.equal(s.hasOwnership(), true, "should have ownership information");
st.equal(s2.hasOwnership(), false, "should not have ownership information");
st.end()
});
});
test('.hasAttributes()', function (t) {
t.test("succeed in checking availability of attributes information", function(st){
var s = stone.load(fx.stones.valid[2]);
var s2 = s.clone();
delete s2.attributes;
st.equal(s.hasAttributes(), true, "should have ownership information");
st.equal(s2.hasAttributes(), false, "should not have ownership information");
st.end()
});
});
test('.hasEmbeds()', function (t) {
t.test("succeed in checking availability of embeds information", function(st){
var s = stone.load(fx.stones.valid[3]);
var s2 = s.clone();
delete s2.embeds;
st.equal(s.hasEmbeds(), true, "should have embeds information");
st.equal(s2.hasEmbeds(), false, "should not have embeds information");
st.end()
});
});
test(".add* methods", function(t){
var meta = {
id: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
type: "coupon",
created_at: parseInt(Date.now() / 1000)
}
t.test("addMeta: successfully add block", function(st){
var m = _.clone(meta)
var result = stone.create(m, privateKey).then(function(stn){
var newMeta = _.clone(m);
newMeta.type = "couponx";
stn.addMeta(newMeta, privateKey).then(function(sig){
st.notEqual(newMeta.type, m.type)
st.notEqual(stn.signatures["meta"], undefined)
st.end()
})
});
});
t.test("addOwnership: successfully add block", function(st){
var m = _.clone(meta)
var result = stone.create(m, privateKey).then(function(stn){
var ownership = {
ref_id: m.id,
type: "sole",
sole: {
address_id: "abc"
}
}
st.deepEqual(stn.ownership, {})
stn.addOwnership(ownership, privateKey).then(function(sig){
st.notDeepEqual(stn.ownership, {})
st.equal(stn.signatures.ownership, sig)
st.end()
})
});
});
t.test("addOwnership: fail because meta.id is not set", function(st){
var m = _.clone(meta)
var result = stone.create(m, privateKey).then(function(stn){
stn.meta.id = null;
stn.addOwnership({}, privateKey).catch(function(err){
st.equal(err.message, "meta.id is not set")
st.end()
})
});
});
t.test("addAttributes: successfully add block", function(st){
var m = _.clone(meta)
var result = stone.create(m, privateKey).then(function(stn){
var attributes = {
ref_id: m.id,
data: "some_data"
}
st.deepEqual(stn.attributes, {})
stn.addAttributes(attributes, privateKey).then(function(sig){
st.notDeepEqual(stn.attributes, {})
st.equal(stn.signatures.attributes, sig)
st.end()
})
});
});
t.test("addAttributes: fail because meta.id is not set", function(st){
var m = _.clone(meta)
var result = stone.create(m, privateKey).then(function(stn){
stn.meta.id = null;
stn.addOwnership({}, privateKey).catch(function(err){
st.equal(err.message, "meta.id is not set")
st.end()
})
});
});
});