180 lines
7.6 KiB
JavaScript
180 lines
7.6 KiB
JavaScript
const EtcTree = require('./etctree.js');
|
|
|
|
const tests = {};
|
|
|
|
let cur_test = '';
|
|
|
|
const expect = (a, b) =>
|
|
{
|
|
if (!EtcTree.eq(a, b))
|
|
{
|
|
process.stderr.write(cur_test+' test:\nexpected: '+JSON.stringify(b)+'\nreal: '+JSON.stringify(a)+'\n'+new Error().stack.replace(/^.*\n.*\n/, '')+'\n');
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
tests['read/write'] = async () =>
|
|
{
|
|
const t = new EtcTree();
|
|
expect(
|
|
await t.api_txn({ success: [ { request_put: { key: '/vitastor//config/global', value: { hello: 'world' } } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_range: { key: '/vitastor/config/global' } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_range: {
|
|
kvs: [ { key: '/vitastor/config/global', mod_revision: 1, value: { hello: 'world' } } ],
|
|
} } ] }
|
|
);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_range: { key: '/vitastor/config/', range_end: '/vitastor/config0' } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_range: {
|
|
kvs: [ { key: '/vitastor/config/global', mod_revision: 1, value: { hello: 'world' } } ],
|
|
} } ] }
|
|
);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_range: { key: '/vitasto/', range_end: '/vitasto0' } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_range: { kvs: [] } } ] }
|
|
);
|
|
expect(
|
|
await t.api_txn({
|
|
compare: [ { key: '/vitastor/config/global', target: 'MOD', mod_revision: 1, result: 'LESS' } ],
|
|
success: [ { request_put: { key: '/vitastor//config/global', value: { hello: 'world' } } } ],
|
|
failure: [ { request_range: { key: '/vitastor/config/global' } } ],
|
|
}),
|
|
{ header: { revision: 1 }, succeeded: false, responses: [ { response_range: {
|
|
kvs: [ { key: '/vitastor/config/global', mod_revision: 1, value: { hello: 'world' } } ],
|
|
} } ] }
|
|
);
|
|
expect(
|
|
await t.api_txn({
|
|
compare: [ { key: '/vitastor/config/global', target: 'MOD', mod_revision: 2, result: 'LESS' } ],
|
|
success: [ { request_put: { key: '/vitastor//config/global', value: { hello: 'world2' } } } ]
|
|
}),
|
|
{ header: { revision: 2 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_range: { key: '/vitastor/config/', range_end: '/vitastor/config0' } } ] }),
|
|
{ header: { revision: 2 }, succeeded: true, responses: [ { response_range: {
|
|
kvs: [ { key: '/vitastor/config/global', mod_revision: 2, value: { hello: 'world2' } } ],
|
|
} } ] }
|
|
);
|
|
expect(
|
|
t.dump(false),
|
|
{"state":{"children":{"":{"children":{"vitastor":{"children":{"config":{"children":{"global":{"version":2,"mod_revision":2,"create_revision":1,"value":{"hello":"world2"}}}}}}}}}},"mod_revision":2,"leases":{}}
|
|
);
|
|
t.destroy();
|
|
};
|
|
|
|
tests['watch'] = async () =>
|
|
{
|
|
const t = new EtcTree();
|
|
const sent = [];
|
|
const send = (event) => sent.push(event);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_put: { key: '/vitastor//config/global', value: { hello: 'world' } } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(
|
|
t.api_create_watch({ watch_id: 1, key: '/vitastor/', range_end: '/vitastor0' }, send),
|
|
{ watch_id: 1, created: true }
|
|
);
|
|
expect(sent, []);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_put: { key: '/vitastor/osd/state/1', value: { ip: '1.2.3.4' } } } ] }),
|
|
{ header: { revision: 2 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(sent, [ { result: { header: { revision: 2 }, events: [ { type: 'PUT', kv: { key: '/vitastor/osd/state/1', value: { ip: '1.2.3.4' }, mod_revision: 2 } } ] } } ]);
|
|
t.destroy();
|
|
};
|
|
|
|
tests['lease'] = async () =>
|
|
{
|
|
const t = new EtcTree();
|
|
const sent = [];
|
|
const send = (event) => sent.push(event);
|
|
const leaseID = (await t.api_grant_lease({ TTL: 0.5 })).ID;
|
|
expect(leaseID != null, true);
|
|
expect(
|
|
await t.api_txn({ success: [ { request_put: { key: '/vitastor/osd/state/1', lease: leaseID, value: { ip: '1.2.3.4' } } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(
|
|
t.api_create_watch({ watch_id: 1, key: '/vitastor/', range_end: '/vitastor0' }, send),
|
|
{ watch_id: 1, created: true }
|
|
);
|
|
expect(sent, []);
|
|
const dump = t.dump(false);
|
|
const expires = dump.leases[leaseID].expires;
|
|
expect(dump, {"state":{"children":{"":{"children":{"vitastor":{"children":{"osd":{"children":{"state":{"children":{"1":{"lease":leaseID,"version":1,"mod_revision":1,"create_revision":1,"value":{"ip":"1.2.3.4"}}}}}}}}}}}},"mod_revision":1,"leases":{[leaseID]:{"ttl":0.5,"expires":expires}}});
|
|
await new Promise(ok => setTimeout(ok, 600));
|
|
expect(sent, [ { result: { header: { revision: 2 }, events: [ { type: 'DELETE', kv: { key: '/vitastor/osd/state/1', mod_revision: 2 } } ] } } ]);
|
|
t.pause_leases();
|
|
t.load(dump);
|
|
expect(t.dump(false), dump);
|
|
const t2 = new EtcTree();
|
|
t2.pause_leases();
|
|
t2.load(dump);
|
|
expect(t2.dump(false), dump);
|
|
t.destroy();
|
|
t2.destroy();
|
|
};
|
|
|
|
tests['update'] = async () =>
|
|
{
|
|
const t1 = new EtcTree();
|
|
const t2 = new EtcTree();
|
|
const leaseID = (await t1.api_grant_lease({ TTL: 0.5 })).ID;
|
|
expect(leaseID != null, true);
|
|
expect(
|
|
await t1.api_txn({ success: [ { request_put: { key: '/vitastor/osd/state/1', lease: leaseID, value: { ip: '1.2.3.4' } } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(
|
|
await t2.api_txn({ success: [ { request_put: { key: '/vitastor/osd/state/1', value: { ip: '1.2.3.6' } } } ] }),
|
|
{ header: { revision: 1 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
expect(
|
|
await t1.api_txn({ success: [ { request_put: { key: '/vitastor/osd/state/1', lease: leaseID, value: { ip: '1.2.3.5' } } } ] }),
|
|
{ header: { revision: 2 }, succeeded: true, responses: [ { response_put: {} } ] }
|
|
);
|
|
let dump2 = t2.dump();
|
|
t2.load(t1.dump(), true);
|
|
t1.load(dump2, true);
|
|
let dump = t2.dump(false);
|
|
let expires = dump.leases[leaseID].expires;
|
|
expect(dump, {"state":{"children":{"":{"children":{"vitastor":{"children":{"osd":{"children":{"state":{"children":{"1":{"lease":leaseID,"version":2,"mod_revision":2,"create_revision":1,"value":{"ip":"1.2.3.5"}}}}}}}}}}}},"mod_revision":2,"leases":{[leaseID]:{"ttl":0.5,"expires":expires}}});
|
|
expect(t1.dump(false), {"state":{"children":{"":{"children":{"vitastor":{"children":{"osd":{"children":{"state":{"children":{"1":{"lease":leaseID,"version":2,"mod_revision":2,"create_revision":1,"value":{"ip":"1.2.3.5"}}}}}}}}}}}},"mod_revision":2,"leases":{[leaseID]:{"ttl":0.5,"expires":expires}}});
|
|
t1.destroy();
|
|
t2.destroy();
|
|
};
|
|
|
|
tests['replicate watcher'] = async () =>
|
|
{
|
|
const t = new EtcTree();
|
|
t.set_replicate_watcher(async () =>
|
|
{
|
|
throw new Error('replication failed');
|
|
});
|
|
let thrown = false;
|
|
try
|
|
{
|
|
await t.api_txn({ success: [ { request_put: { key: '/vitastor/osd/state/1', value: { ip: '1.2.3.4' } } } ] });
|
|
}
|
|
catch (e)
|
|
{
|
|
thrown = e;
|
|
}
|
|
expect(thrown && thrown.message == 'replication failed', true);
|
|
t.destroy();
|
|
};
|
|
|
|
(async function()
|
|
{
|
|
for (cur_test in tests)
|
|
{
|
|
await tests[cur_test]();
|
|
console.log(cur_test+' test: OK');
|
|
}
|
|
})().catch(console.error);
|