move taskWithTimeout to typescript
parent
34835ef68f
commit
09d9db38e8
@ -1,23 +0,0 @@
|
||||
{
|
||||
"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,
|
||||
"PROTO_ROOT": true,
|
||||
"stringToArrayBuffer": true,
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/* global mocha, chai, assert */
|
||||
|
||||
mocha
|
||||
.setup('bdd')
|
||||
.fullTrace()
|
||||
.timeout(10000);
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
};
|
File diff suppressed because one or more lines are too long
@ -1,56 +0,0 @@
|
||||
/* global libsignal, textsecure */
|
||||
|
||||
describe('encrypting and decrypting profile data', () => {
|
||||
const NAME_PADDED_LENGTH = 26;
|
||||
describe('encrypting and decrypting profile names', () => {
|
||||
it('pads, encrypts, decrypts, and unpads a short string', () => {
|
||||
const name = 'Alice';
|
||||
const buffer = dcodeIO.ByteBuffer.wrap(name).toArrayBuffer();
|
||||
const key = libsignal.crypto.getRandomBytes(32);
|
||||
|
||||
return textsecure.crypto.encryptProfileName(buffer, key).then(encrypted => {
|
||||
assert(encrypted.byteLength === NAME_PADDED_LENGTH + 16 + 12);
|
||||
return textsecure.crypto.decryptProfileName(encrypted, key).then(decrypted => {
|
||||
assert.strictEqual(dcodeIO.ByteBuffer.wrap(decrypted).toString('utf8'), 'Alice');
|
||||
});
|
||||
});
|
||||
});
|
||||
it('works for empty string', () => {
|
||||
const name = dcodeIO.ByteBuffer.wrap('').toArrayBuffer();
|
||||
const key = libsignal.crypto.getRandomBytes(32);
|
||||
|
||||
return textsecure.crypto.encryptProfileName(name.buffer, key).then(encrypted => {
|
||||
assert(encrypted.byteLength === NAME_PADDED_LENGTH + 16 + 12);
|
||||
return textsecure.crypto.decryptProfileName(encrypted, key).then(decrypted => {
|
||||
assert.strictEqual(decrypted.byteLength, 0);
|
||||
assert.strictEqual(dcodeIO.ByteBuffer.wrap(decrypted).toString('utf8'), '');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('encrypting and decrypting profile avatars', () => {
|
||||
it('encrypts and decrypts', () => {
|
||||
const buffer = dcodeIO.ByteBuffer.wrap('This is an avatar').toArrayBuffer();
|
||||
const key = libsignal.crypto.getRandomBytes(32);
|
||||
|
||||
return textsecure.crypto.encryptProfile(buffer, key).then(encrypted => {
|
||||
assert(encrypted.byteLength === buffer.byteLength + 16 + 12);
|
||||
return textsecure.crypto.decryptProfile(encrypted, key).then(decrypted => {
|
||||
assertEqualArrayBuffers(buffer, decrypted);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('throws when decrypting with the wrong key', () => {
|
||||
const buffer = dcodeIO.ByteBuffer.wrap('This is an avatar').toArrayBuffer();
|
||||
const key = libsignal.crypto.getRandomBytes(32);
|
||||
const badKey = libsignal.crypto.getRandomBytes(32);
|
||||
|
||||
return textsecure.crypto.encryptProfile(buffer, key).then(encrypted => {
|
||||
assert(encrypted.byteLength === buffer.byteLength + 16 + 12);
|
||||
return textsecure.crypto.decryptProfile(encrypted, badKey).catch(error => {
|
||||
assert.strictEqual(error.name, 'ProfileDecryptError');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<title>libtextsecure 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="../components.js"></script>
|
||||
<script type="text/javascript" src="../libsignal-protocol.js"></script>
|
||||
<script type="text/javascript" src="../crypto.js"></script>
|
||||
<script type="text/javascript" src="../protobufs.js" data-cover></script>
|
||||
<script type="text/javascript" src="../errors.js" data-cover></script>
|
||||
<script type="text/javascript" src="../storage.js" data-cover></script>
|
||||
|
||||
<script type="text/javascript" src="../event_target.js" data-cover></script>
|
||||
<script type="text/javascript" src="../helpers.js" data-cover></script>
|
||||
<script type="text/javascript" src="../stringview.js" data-cover></script>
|
||||
<script type="text/javascript" src="../task_with_timeout.js" data-cover></script>
|
||||
|
||||
<script type="text/javascript" src="errors_test.js"></script>
|
||||
<script type="text/javascript" src="helpers_test.js"></script>
|
||||
<script type="text/javascript" src="task_with_timeout_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>
|
@ -1,72 +0,0 @@
|
||||
/* global textsecure */
|
||||
|
||||
describe('createTaskWithTimeout', () => {
|
||||
it('resolves when promise resolves', () => {
|
||||
const task = () => Promise.resolve('hi!');
|
||||
const taskWithTimeout = textsecure.createTaskWithTimeout(task);
|
||||
|
||||
return taskWithTimeout().then(result => {
|
||||
assert.strictEqual(result, 'hi!');
|
||||
});
|
||||
});
|
||||
it('flows error from promise back', () => {
|
||||
const error = new Error('original');
|
||||
const task = () => Promise.reject(error);
|
||||
const taskWithTimeout = textsecure.createTaskWithTimeout(task);
|
||||
|
||||
return taskWithTimeout().catch(flowedError => {
|
||||
assert.strictEqual(error, flowedError);
|
||||
});
|
||||
});
|
||||
it('rejects if promise takes too long (this one logs error to console)', () => {
|
||||
let complete = false;
|
||||
const task = () =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
complete = true;
|
||||
resolve();
|
||||
}, 3000);
|
||||
});
|
||||
const taskWithTimeout = textsecure.createTaskWithTimeout(task, this.name, {
|
||||
timeout: 10,
|
||||
});
|
||||
|
||||
return taskWithTimeout().then(
|
||||
() => {
|
||||
throw new Error('it was not supposed to resolve!');
|
||||
},
|
||||
() => {
|
||||
assert.strictEqual(complete, false);
|
||||
}
|
||||
);
|
||||
});
|
||||
it('resolves if task returns something falsey', () => {
|
||||
const task = () => {};
|
||||
const taskWithTimeout = textsecure.createTaskWithTimeout(task);
|
||||
return taskWithTimeout();
|
||||
});
|
||||
it('resolves if task returns a non-promise', () => {
|
||||
const task = () => 'hi!';
|
||||
const taskWithTimeout = textsecure.createTaskWithTimeout(task);
|
||||
return taskWithTimeout().then(result => {
|
||||
assert.strictEqual(result, 'hi!');
|
||||
});
|
||||
});
|
||||
it('rejects if task throws (and does not log about taking too long)', () => {
|
||||
const error = new Error('Task is throwing!');
|
||||
const task = () => {
|
||||
throw error;
|
||||
};
|
||||
const taskWithTimeout = textsecure.createTaskWithTimeout(task, this.name, {
|
||||
timeout: 10,
|
||||
});
|
||||
return taskWithTimeout().then(
|
||||
() => {
|
||||
throw new Error('Overall task should reject!');
|
||||
},
|
||||
flowedError => {
|
||||
assert.strictEqual(flowedError, error);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
@ -0,0 +1,66 @@
|
||||
export const createTaskWithTimeout = (task: any, id: string, givenTimeout?: number) => {
|
||||
const timeout = givenTimeout || 1000 * 60 * 3; // three minutes
|
||||
|
||||
const errorForStack = new Error('for stack');
|
||||
return async () =>
|
||||
new Promise((resolve, reject) => {
|
||||
let complete = false;
|
||||
let timer: NodeJS.Timeout | null = global.setTimeout(() => {
|
||||
if (!complete) {
|
||||
const message = `${id || ''} task did not complete in time. Calling stack: ${
|
||||
errorForStack.stack
|
||||
}`;
|
||||
|
||||
window?.log?.error(message);
|
||||
reject(new Error(message));
|
||||
return;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, timeout);
|
||||
const clearTimer = () => {
|
||||
try {
|
||||
const localTimer = timer;
|
||||
if (localTimer) {
|
||||
timer = null;
|
||||
clearTimeout(localTimer);
|
||||
}
|
||||
} catch (error) {
|
||||
window?.log?.error(
|
||||
id || '',
|
||||
'task ran into problem canceling timer. Calling stack:',
|
||||
errorForStack.stack
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const success = (result: any) => {
|
||||
clearTimer();
|
||||
complete = true;
|
||||
resolve(result);
|
||||
return;
|
||||
};
|
||||
const failure = (error: any) => {
|
||||
clearTimer();
|
||||
complete = true;
|
||||
reject(error);
|
||||
return;
|
||||
};
|
||||
|
||||
let promise;
|
||||
try {
|
||||
promise = task();
|
||||
} catch (error) {
|
||||
clearTimer();
|
||||
throw error;
|
||||
}
|
||||
if (!promise || !promise.then) {
|
||||
clearTimer();
|
||||
complete = true;
|
||||
resolve(promise);
|
||||
return;
|
||||
}
|
||||
|
||||
return promise.then(success, failure);
|
||||
});
|
||||
};
|
@ -0,0 +1,81 @@
|
||||
// tslint:disable: no-implicit-dependencies max-func-body-length no-unused-expression
|
||||
|
||||
import chai from 'chai';
|
||||
|
||||
// tslint:disable-next-line: no-require-imports no-var-requires
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
import { createTaskWithTimeout } from '../../../../session/utils/TaskWithTimeout';
|
||||
chai.use(chaiAsPromised as any);
|
||||
chai.should();
|
||||
|
||||
const { assert } = chai;
|
||||
|
||||
const taskName = 'whatever';
|
||||
|
||||
describe('createTaskWithTimeout', () => {
|
||||
it('resolves when promise resolves', async () => {
|
||||
const task = () => Promise.resolve('hi!');
|
||||
const taskWithTimeout = createTaskWithTimeout(task, 'task_123');
|
||||
|
||||
await taskWithTimeout().then((result: any) => {
|
||||
assert.strictEqual(result, 'hi!');
|
||||
});
|
||||
});
|
||||
it('flows error from promise back', async () => {
|
||||
const error = new Error('original');
|
||||
const task = () => Promise.reject(error);
|
||||
const taskWithTimeout = createTaskWithTimeout(task, 'task_123');
|
||||
|
||||
await taskWithTimeout().catch((flowedError: any) => {
|
||||
assert.strictEqual(error, flowedError);
|
||||
});
|
||||
});
|
||||
it('rejects if promise takes too long (this one logs error to console)', async () => {
|
||||
let complete = false;
|
||||
const task = async () =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
complete = true;
|
||||
resolve(null);
|
||||
}, 3000);
|
||||
});
|
||||
const taskWithTimeout = createTaskWithTimeout(task, taskName, 10);
|
||||
|
||||
await taskWithTimeout().then(
|
||||
() => {
|
||||
throw new Error('it was not supposed to resolve!');
|
||||
},
|
||||
() => {
|
||||
assert.strictEqual(complete, false);
|
||||
}
|
||||
);
|
||||
});
|
||||
it('resolves if task returns something falsey', async () => {
|
||||
// tslint:disable-next-line: no-empty
|
||||
const task = () => {};
|
||||
const taskWithTimeout = createTaskWithTimeout(task, taskName);
|
||||
await taskWithTimeout();
|
||||
});
|
||||
it('resolves if task returns a non-promise', async () => {
|
||||
const task = () => 'hi!';
|
||||
const taskWithTimeout = createTaskWithTimeout(task, taskName);
|
||||
await taskWithTimeout().then((result: any) => {
|
||||
assert.strictEqual(result, 'hi!');
|
||||
});
|
||||
});
|
||||
it('rejects if task throws (and does not log about taking too long)', async () => {
|
||||
const error = new Error('Task is throwing!');
|
||||
const task = () => {
|
||||
throw error;
|
||||
};
|
||||
const taskWithTimeout = createTaskWithTimeout(task, taskName, 10);
|
||||
await taskWithTimeout().then(
|
||||
() => {
|
||||
throw new Error('Overall task should reject!');
|
||||
},
|
||||
(flowedError: any) => {
|
||||
assert.strictEqual(flowedError, error);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue