zblueslip: Implement tracking extra/lesser blueslip calls.

We change the user facing interface to allow specifying expected
number of error messages (default=1). Now an average test can look
like:

```
    // We expect 3 error messages;
    blueslip.expect('error', 'an error message', 3);
    throwError();
    throwError();
    throwError();
    blueslip.reset();
```
This commit is contained in:
Rohitt Vashishtha
2020-04-20 16:08:24 +05:30
committed by showell
parent 438a545477
commit 0def4a97ae
8 changed files with 64 additions and 20 deletions

View File

@@ -82,13 +82,12 @@ run_test('clear', () => {
}); });
run_test('undefined_keys', () => { run_test('undefined_keys', () => {
blueslip.expect('error', 'Tried to call a FoldDict method with an undefined key.'); blueslip.expect('error', 'Tried to call a FoldDict method with an undefined key.', 2);
const d = new FoldDict(); const d = new FoldDict();
assert.equal(d.has(undefined), false); assert.equal(d.has(undefined), false);
assert.strictEqual(d.get(undefined), undefined); assert.strictEqual(d.get(undefined), undefined);
assert.equal(blueslip.get_test_logs('error').length, 2);
blueslip.reset(); blueslip.reset();
}); });

View File

@@ -15,8 +15,9 @@ run_test('map', () => {
}); });
run_test('conversions', () => { run_test('conversions', () => {
blueslip.expect('error', 'not a number'); blueslip.expect('error', 'not a number', 2);
const ls = new LazySet([1, 2]); const ls = new LazySet([1, 2]);
ls.add('3'); ls.add('3');
assert(ls.has('3')); assert(ls.has('3'));
blueslip.reset();
}); });

View File

@@ -186,18 +186,16 @@ run_test('errors', () => {
display_recipient: [{id: 92714}], display_recipient: [{id: 92714}],
}; };
blueslip.expect('error', 'Unknown user_id in get_by_user_id: 92714'); blueslip.expect('error', 'Unknown user_id in get_by_user_id: 92714', 2);
blueslip.expect('error', 'Unknown user id 92714'); // From person.js blueslip.expect('error', 'Unknown user id 92714', 2); // From person.js
// Expect each to throw two blueslip errors // Expect each to throw two blueslip errors
// One from message_store.js, one from person.js // One from message_store.js, one from person.js
const emails = message_store.get_pm_emails(message); const emails = message_store.get_pm_emails(message);
assert.equal(emails, '?'); assert.equal(emails, '?');
assert.equal(blueslip.get_test_logs('error').length, 2);
const names = message_store.get_pm_full_names(message); const names = message_store.get_pm_full_names(message);
assert.equal(names, '?'); assert.equal(names, '?');
assert.equal(blueslip.get_test_logs('error').length, 4);
blueslip.reset(); blueslip.reset();

View File

@@ -81,12 +81,11 @@ run_test('blueslip', () => {
display_recipient: [], display_recipient: [],
sender_id: me.user_id, sender_id: me.user_id,
}; };
blueslip.expect('error', 'Empty recipient list in message'); blueslip.expect('error', 'Empty recipient list in message', 4);
people.pm_with_user_ids(message); people.pm_with_user_ids(message);
people.group_pm_with_user_ids(message); people.group_pm_with_user_ids(message);
people.all_user_ids_in_pm(message); people.all_user_ids_in_pm(message);
assert.equal(people.pm_perma_link(message), undefined); assert.equal(people.pm_perma_link(message), undefined);
assert.equal(blueslip.get_test_logs('error').length, 4);
blueslip.reset(); blueslip.reset();
const charles = { const charles = {

View File

@@ -292,7 +292,7 @@ run_test('subscribers', () => {
blueslip.expect( blueslip.expect(
'warn', 'warn',
'We got a is_user_subscribed call for a non-existent or inaccessible stream.'); 'We got a is_user_subscribed call for a non-existent or inaccessible stream.', 2);
sub.invite_only = true; sub.invite_only = true;
stream_data.update_calculated_fields(sub); stream_data.update_calculated_fields(sub);
assert.equal(stream_data.is_user_subscribed('Rome', brutus.user_id), undefined); assert.equal(stream_data.is_user_subscribed('Rome', brutus.user_id), undefined);

View File

@@ -72,7 +72,7 @@ run_test('server', () => {
}); });
run_test('defensive checks', () => { run_test('defensive checks', () => {
blueslip.expect('error', 'need ints for user_id'); blueslip.expect('error', 'need ints for user_id', 2);
user_status.set_away('string'); user_status.set_away('string');
user_status.revoke_away('string'); user_status.revoke_away('string');
}); });

View File

@@ -34,7 +34,7 @@ run_test('basics', () => {
// zblueslip logs all the calls made to it, and they can be used in asserts like: // zblueslip logs all the calls made to it, and they can be used in asserts like:
// Now, let's add our error to the list of expected errors. // Now, let's add our error to the list of expected errors.
blueslip.expect('error', 'world'); blueslip.expect('error', 'world', 2);
// This time, blueslip will just log the error, which is // This time, blueslip will just log the error, which is
// being verified by the assert call on the length of the log. // being verified by the assert call on the length of the log.
// We can also check for which specific error was logged, but since // We can also check for which specific error was logged, but since
@@ -42,13 +42,18 @@ run_test('basics', () => {
// only that error could have been logged, and others would raise // only that error could have been logged, and others would raise
// an error, aborting the test. // an error, aborting the test.
throw_an_error(); throw_an_error();
// The following check is redundant; blueslip.reset() already asserts that
// we got the expected number of errors.
assert.equal(blueslip.get_test_logs('error').length, 2); assert.equal(blueslip.get_test_logs('error').length, 2);
// Let's clear the array of valid errors as well as the log. Now, all errors // Let's clear the array of valid errors as well as the log. Now, all errors
// should be thrown directly by blueslip. // should be thrown directly by blueslip.
blueslip.reset(); blueslip.reset();
assert.throws(throw_an_error); assert.throws(throw_an_error);
blueslip.reset(); // This call to blueslip.reset() would complain.
assert.throws(() => {
blueslip.reset();
});
// Let's repeat the above procedue with warnings. Unlike errors, // Let's repeat the above procedue with warnings. Unlike errors,
// warnings shouldn't stop the code execution, and thus, the // warnings shouldn't stop the code execution, and thus, the
@@ -59,7 +64,14 @@ run_test('basics', () => {
} }
assert.throws(throw_a_warning); assert.throws(throw_a_warning);
blueslip.reset(); // Again, we do not expect this particular warning so blueslip.reset should complain.
assert.throws(() => {
blueslip.reset();
});
// Let's reset blueslip regardless of errors. This is only for demonstration
// purposes here; do not reset blueslip like this in actual tests.
blueslip.reset(true);
// Now, let's add our warning to the list of expected warnings. // Now, let's add our warning to the list of expected warnings.
// This time, we shouldn't throw an error. However, to confirm that we // This time, we shouldn't throw an error. However, to confirm that we
@@ -67,4 +79,12 @@ run_test('basics', () => {
blueslip.expect('warn', 'world'); blueslip.expect('warn', 'world');
throw_a_warning(); throw_a_warning();
blueslip.reset(); blueslip.reset();
// However, we detect when we have more or less of the expected errors/warnings.
blueslip.expect('warn', 'world');
assert.throws(() => {
blueslip.reset();
});
// Again, forcefully reset blueslip.
blueslip.reset(true);
}); });

View File

@@ -28,16 +28,40 @@ exports.make_zblueslip = function () {
lib.seen_messages[name] = new Set(); lib.seen_messages[name] = new Set();
} }
lib.expect = (name, message) => { lib.expect = (name, message, count = 1) => {
if (opts[name] === undefined) { if (opts[name] === undefined) {
throw Error('unexpected arg for expect: ' + name); throw Error('unexpected arg for expect: ' + name);
} }
lib.test_data[name].push(message); if (count <= 0 && Number.isInteger(count)) {
throw Error('expected count should be a positive integer');
}
const obj = {message, count, expected_count: count};
lib.test_data[name].push(obj);
}; };
lib.check_seen_messages = () => { lib.check_seen_messages = () => {
for (const name of names) { for (const name of names) {
for (const message of lib.test_data[name]) { for (const obj of lib.test_logs[name]) {
const message = obj.message;
const i = lib.test_data[name].findIndex(x => x.message === message);
if (i === -1) {
// Only throw this for message types we want to explicitly track.
// For example, we do not want to throw here for debug messages.
if (opts[name]) {
throw Error (`Unexpected '${name}' message: ${message}`);
}
continue;
}
lib.test_data[name][i].count -= 1;
}
for (const obj of lib.test_data[name]) {
const message = obj.message;
if (obj.count > 0) {
throw Error(`We saw ${obj.count} (expected ${obj.expected_count}) of '${name}': ${message}`);
} else if (obj.count < 0) {
throw Error(`We saw ${obj.expected_count - obj.count} (expected ${obj.expected_count}) of '${name}': ${message}`);
}
if (!lib.seen_messages[name].has(message)) { if (!lib.seen_messages[name].has(message)) {
throw Error('Never saw: ' + message); throw Error('Never saw: ' + message);
} }
@@ -45,8 +69,10 @@ exports.make_zblueslip = function () {
} }
}; };
lib.reset = () => { lib.reset = (skip_checks = false) => {
lib.check_seen_messages(); if (!skip_checks) {
lib.check_seen_messages();
}
for (const name of names) { for (const name of names) {
lib.test_data[name] = []; lib.test_data[name] = [];
@@ -81,7 +107,8 @@ exports.make_zblueslip = function () {
} }
lib.seen_messages[name].add(message); lib.seen_messages[name].add(message);
lib.test_logs[name].push({message, more_info, stack}); lib.test_logs[name].push({message, more_info, stack});
const exact_match_fail = !lib.test_data[name].includes(message); const matched_error_message = lib.test_data[name].find(x => x.message === message);
const exact_match_fail = !matched_error_message;
if (exact_match_fail) { if (exact_match_fail) {
const error = Error(`Invalid ${name} message: "${message}".`); const error = Error(`Invalid ${name} message: "${message}".`);
error.blueslip = true; error.blueslip = true;