Setup grunt/yarn etc for loki tests plus wrote first 2 simple libloki-protocol tests

This commit is contained in:
Beaudan 2018-12-05 15:45:02 +11:00
parent 94d3ae7b5d
commit c93aff7ebe
10 changed files with 239 additions and 4 deletions

View File

@ -11,6 +11,7 @@ js/libloki.js
js/util_worker.js
js/libsignal-protocol-worker.js
libtextsecure/components.js
libloki/test/test.js
libtextsecure/test/test.js
test/test.js
@ -25,4 +26,3 @@ test/blanket_mocha.js
# TypeScript generated files
ts/**/*.js

1
.gitignore vendored
View File

@ -22,6 +22,7 @@ js/libtextsecure.js
js/libloki.js
libtextsecure/components.js
libtextsecure/test/test.js
libloki/test/test.js
stylesheets/*.css
test/test.js

View File

@ -91,6 +91,14 @@ module.exports = grunt => {
src: ['libloki/libloki-protocol.js'],
dest: 'js/libloki.js',
},
lokitest: {
src: [
'node_modules/mocha/mocha.js',
'node_modules/chai/chai.js',
'libloki/test/_test.js',
],
dest: 'libloki/test/test.js',
},
libtextsecuretest: {
src: [
'node_modules/jquery/dist/jquery.js',
@ -355,6 +363,17 @@ module.exports = grunt => {
}
);
grunt.registerTask(
'loki-unit-tests',
'Run loki unit tests w/Electron',
function thisNeeded() {
const environment = grunt.option('env') || 'test-loki';
const done = this.async();
runTests(environment, done);
}
);
grunt.registerMultiTask(
'test-release',
'Test packaged releases',
@ -442,7 +461,8 @@ module.exports = grunt => {
'locale-patch',
]);
grunt.registerTask('dev', ['default', 'watch']);
grunt.registerTask('test', ['unit-tests', 'lib-unit-tests']);
grunt.registerTask('test', ['unit-tests', 'lib-unit-tests', 'loki-unit-tests']);
grunt.registerTask('test-loki', ['loki-unit-tests']);
grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
grunt.registerTask('default', [
'exec:build-protobuf',

26
libloki/test/.eslintrc Normal file
View File

@ -0,0 +1,26 @@
{
"env": {
"browser": true,
"node": false,
"mocha": true
},
"parserOptions": {
"sourceType": "script"
},
"rules": {
"strict": "off",
"more/no-then": "off"
},
"globals": {
"assert": true,
"assertEqualArrayBuffers": true,
"dcodeIO": true,
"getString": true,
"hexToArrayBuffer": true,
"MockServer": true,
"MockSocket": true,
"clearDatabase": true,
"PROTO_ROOT": true,
"stringToArrayBuffer": true
}
}

66
libloki/test/_test.js Normal file
View File

@ -0,0 +1,66 @@
/* global mocha, chai, assert, Whisper */
mocha.setup('bdd');
window.assert = chai.assert;
window.PROTO_ROOT = '../../protos';
const OriginalReporter = mocha._reporter;
const SauceReporter = function Constructor(runner) {
const failedTests = [];
runner.on('end', () => {
window.mochaResults = runner.stats;
window.mochaResults.reports = failedTests;
});
runner.on('fail', (test, err) => {
const flattenTitles = item => {
const titles = [];
while (item.parent.title) {
titles.push(item.parent.title);
// eslint-disable-next-line no-param-reassign
item = item.parent;
}
return titles.reverse();
};
failedTests.push({
name: test.title,
result: false,
message: err.message,
stack: err.stack,
titles: flattenTitles(test),
});
});
// eslint-disable-next-line no-new
new OriginalReporter(runner);
};
SauceReporter.prototype = OriginalReporter.prototype;
mocha.reporter(SauceReporter);
// Override the database id.
window.Whisper = window.Whisper || {};
window.Whisper.Database = window.Whisper.Database || {};
Whisper.Database.id = 'test';
/*
* global helpers for tests
*/
window.assertEqualArrayBuffers = (ab1, ab2) => {
assert.deepEqual(new Uint8Array(ab1), new Uint8Array(ab2));
};
window.hexToArrayBuffer = str => {
const ret = new ArrayBuffer(str.length / 2);
const array = new Uint8Array(ret);
for (let i = 0; i < str.length / 2; i += 1)
array[i] = parseInt(str.substr(i * 2, 2), 16);
return ret;
};
window.clearDatabase = async () => {
await window.Signal.Data.removeAll();
};

39
libloki/test/index.html Normal file
View File

@ -0,0 +1,39 @@
<html>
<head>
<meta charset='utf-8'>
<title>libloki test runner</title>
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css" />
</head>
<body>
<div id="mocha">
</div>
<div id="tests">
</div>
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="../../libtextsecure/test/in_memory_signal_protocol_store.js"></script>
<script type="text/javascript" src="../../libtextsecure/components.js"></script>
<script type="text/javascript" src="../../libtextsecure/helpers.js" data-cover></script>
<script type="text/javascript" src="../../libtextsecure/storage.js" data-cover></script>
<script type="text/javascript" src="../../libtextsecure/libsignal-protocol.js"></script>
<script type="text/javascript" src="../../libtextsecure/protocol_wrapper.js" data-cover></script>
<script type="text/javascript" src="../../libtextsecure/protobufs.js" data-cover></script>
<script type="text/javascript" src="../../libtextsecure/stringview.js" data-cover></script>
<script type="text/javascript" src="../libloki-protocol.js" data-cover></script>
<script type="text/javascript" src="libloki-protocol_test.js"></script>
<!-- Comment out to turn off code coverage. Useful for getting real callstacks. -->
<!-- NOTE: blanket doesn't support modern syntax and will choke until we find a replacement. :0( -->
<!-- <script type="text/javascript" src="blanket_mocha.js"></script> -->
<!-- Uncomment to start tests without code coverage enabled -->
<script type="text/javascript">
mocha.run();
</script>
</body>
</html>

View File

@ -0,0 +1,60 @@
/* global libsignal, libloki, textsecure, StringView */
'use strict';
describe('ConversationCollection', () => {
let fallbackCipher;
let identityKey;
let testKey;
let address;
const store = textsecure.storage.protocol;
beforeEach(async () => {
clearDatabase();
identityKey = await libsignal.KeyHelper.generateIdentityKeyPair();
store.put('identityKey', identityKey);
const key = libsignal.crypto.getRandomBytes(32);
const pubKeyString = StringView.arrayBufferToHex(key);
address = new libsignal.SignalProtocolAddress(
pubKeyString,
1 // sourceDevice
);
testKey = {
pubKey: libsignal.crypto.getRandomBytes(33),
privKey: libsignal.crypto.getRandomBytes(32),
};
fallbackCipher = new libloki.FallBackSessionCipher(address);
textsecure.storage.put('maxPreKeyId', 0);
textsecure.storage.put('signedKeyId', 2);
await store.storeSignedPreKey(1, testKey);
});
it('should encrypt fallback cipher messages as friend requests', async () => {
const buffer = new ArrayBuffer(10);
const { type } = await fallbackCipher.encrypt(buffer);
assert(type === textsecure.protobuf.Envelope.Type.FRIEND_REQUEST);
});
it('should should generate a new prekey bundle for a new contact', async () => {
const pubKey = libsignal.crypto.getRandomBytes(32);
const pubKeyString = StringView.arrayBufferToHex(pubKey);
const preKeyIdBefore = textsecure.storage.get('maxPreKeyId', 1);
const newBundle = await libloki.getPreKeyBundleForNumber(pubKeyString);
const preKeyIdAfter = textsecure.storage.get('maxPreKeyId', 1);
assert.strictEqual(preKeyIdAfter, preKeyIdBefore + 1);
const testKeyArray = new Uint8Array(testKey.pubKey);
assert.isDefined(newBundle);
assert.isDefined(newBundle.identityKey);
assert.isDefined(newBundle.deviceId);
assert.isDefined(newBundle.preKeyId);
assert.isDefined(newBundle.signedKeyId);
assert.isDefined(newBundle.preKey);
assert.isDefined(newBundle.signedKey);
assert.isDefined(newBundle.signature);
const signedKeyArray = new Uint8Array(newBundle.signedKey.toArrayBuffer());
assert.strictEqual(testKeyArray.byteLength, signedKeyArray.byteLength);
for (let i = 0 ; i !== testKeyArray.byteLength ; i += 1)
assert.strictEqual(testKeyArray[i], signedKeyArray[i]);
});
});

View File

@ -152,4 +152,16 @@ SignalProtocolStore.prototype = {
resolve(deviceIds);
});
},
async loadPreKeyForContactIdentityKeyString(contactIdentityKeyString) {
return new Promise(resolve => {
const key = this.get(`25519KeypreKey${contactIdentityKeyString}`);
if (!key) resolve(undefined);
resolve({
pubKey: key.publicKey,
privKey: key.privateKey,
keyId: key.id,
recipient: key.recipient,
});
});
},
};

14
main.js
View File

@ -318,6 +318,10 @@ function createWindow() {
mainWindow.loadURL(
prepareURL([__dirname, 'libtextsecure', 'test', 'index.html'])
);
} else if (config.environment === 'test-loki') {
mainWindow.loadURL(
prepareURL([__dirname, 'libloki', 'test', 'index.html'])
);
} else {
mainWindow.loadURL(prepareURL([__dirname, 'background.html']));
}
@ -341,6 +345,7 @@ function createWindow() {
if (
config.environment === 'test' ||
config.environment === 'test-lib' ||
config.environment === 'test-loki' ||
(mainWindow.readyForShutdown && windowState.shouldQuit())
) {
return;
@ -611,7 +616,11 @@ app.on('ready', async () => {
const userDataPath = await getRealPath(app.getPath('userData'));
const installPath = await getRealPath(app.getAppPath());
if (process.env.NODE_ENV !== 'test' && process.env.NODE_ENV !== 'test-lib') {
if (
process.env.NODE_ENV !== 'test' &&
process.env.NODE_ENV !== 'test-lib' &&
process.env.NODE_ENV !== 'test-loki'
) {
installFileHandler({
protocol: electronProtocol,
userDataPath,
@ -777,7 +786,8 @@ app.on('window-all-closed', () => {
if (
process.platform !== 'darwin' ||
config.environment === 'test' ||
config.environment === 'test-lib'
config.environment === 'test-lib' ||
config.environment === 'test-loki'
) {
app.quit();
}

View File

@ -27,6 +27,7 @@
"prepare-import-build": "node prepare_import_build.js",
"publish-to-apt": "NAME=$npm_package_name VERSION=$npm_package_version ./aptly.sh",
"test": "yarn test-node && yarn test-electron",
"test-loki": "NODE_ENV=test-loki yarn run start",
"test-electron": "yarn grunt test",
"test-node": "mocha --recursive test/app test/modules ts/test",
"test-node-coverage": "nyc --reporter=lcov --reporter=text mocha --recursive test/app test/modules ts/test",