Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring code to reduce cognitive complexity of Flags.validate function in src/flags.js #134

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Binary file added dump.rdb
Binary file not shown.
54 changes: 33 additions & 21 deletions src/flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,49 +273,61 @@ Flags.sort = async function (flagIds, sort) {
return flagIds;
};

// refactored function code below
// citation: ChatGPT was referenced and used for the refactoring changes made in the function below
Flags.validate = async function (payload) {
console.log('rathi murugan'); // console check
const [target, reporter] = await Promise.all([
Flags.getTarget(payload.type, payload.id, payload.uid),
user.getUserData(payload.uid),
]);

if (!target) {
throw new Error('[[error:invalid-data]]');
} else if (target.deleted) {
throw new Error('[[error:post-deleted]]');
} else if (!reporter || !reporter.userslug) {
throw new Error('[[error:no-user]]');
} else if (reporter.banned) {
throw new Error('[[error:user-banned]]');
}
// target validity
if (!target) throw new Error('[[error:invalid-data]]');
if (target.deleted) throw new Error('[[error:post-deleted]]');

// reporter validity
if (!reporter || !reporter.userslug) throw new Error('[[error:no-user]]');
if (reporter.banned) throw new Error('[[error:user-banned]]');

// Disallow flagging of profiles/content of privileged users
const [targetPrivileged, reporterPrivileged] = await Promise.all([
user.isPrivileged(target.uid),
user.isPrivileged(reporter.uid),
]);

// stop flagging of privileged users by non-privileged users
if (targetPrivileged && !reporterPrivileged) {
throw new Error('[[error:cant-flag-privileged]]');
}

// check flagging rules
if (payload.type === 'post') {
const editable = await privileges.posts.canEdit(payload.id, payload.uid);
if (!editable.flag && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) {
throw new Error(`[[error:not-enough-reputation-to-flag, ${meta.config['min:rep:flag']}]]`);
}
await validatePostFlag(payload, reporter);
} else if (payload.type === 'user') {
if (parseInt(payload.id, 10) === parseInt(payload.uid, 10)) {
throw new Error('[[error:cant-flag-self]]');
}
const editable = await privileges.users.canEdit(payload.uid, payload.id);
if (!editable && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) {
throw new Error(`[[error:not-enough-reputation-to-flag, ${meta.config['min:rep:flag']}]]`);
}
await validateUserFlag(payload, reporter);
} else {
throw new Error('[[error:invalid-data]]');
}
};

// helper functions
async function validatePostFlag(payload, reporter) {
const editable = await privileges.posts.canEdit(payload.id, payload.uid);
if (!editable.flag && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) {
throw new Error(`[[error:not-enough-reputation-to-flag, ${meta.config['min:rep:flag']}]]`);
}
}

async function validateUserFlag(payload, reporter) {
if (parseInt(payload.id, 10) === parseInt(payload.uid, 10)) {
throw new Error('[[error:cant-flag-self]]');
}
const editable = await privileges.users.canEdit(payload.uid, payload.id);
if (!editable && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) {
throw new Error(`[[error:not-enough-reputation-to-flag, ${meta.config['min:rep:flag']}]]`);
}
}

Flags.getNotes = async function (flagId) {
let notes = await db.getSortedSetRevRangeWithScores(`flag:${flagId}:notes`, 0, -1);
notes = await modifyNotes(notes);
Expand Down
78 changes: 78 additions & 0 deletions test/flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -1187,3 +1187,81 @@ describe('Flags', () => {
});
});
});


// additional tests to ensure full code coverage for refactored code
// citation: ChatGPT was referenced and used to formulate the below tests

describe('.validate()', () => {
it('should throw an error if the target does not exist', async () => {
const uniqueEmail = `testuser${Date.now()}@user.com`;
const flaggerId = await User.create({ username: 'testUser', password: 'abcdef', email: uniqueEmail });

try {
await Flags.validate({ type: 'post', id: 99999, uid: flaggerId });
} catch (err) {
assert.ok(err);
assert.strictEqual(err.message, '[[error:invalid-data]]');
}
});

it('should throw an error if the target is deleted', async () => {
const uniqueEmail = `deletetest${Date.now()}@user.com`;
const flaggerId = await User.create({ username: 'testUser', password: 'abcdef', email: uniqueEmail });
const postId = await Posts.create({ uid: flaggerId, content: 'test content' });
await Posts.delete(postId, flaggerId);

try {
await Flags.validate({ type: 'post', id: postId, uid: flaggerId });
} catch (err) {
assert.ok(err);
assert.strictEqual(err.message, '[[error:post-deleted]]');
}
});

it('should throw an error if the reporter does not exist or has no userslug', async () => {
try {
await Flags.validate({ type: 'post', id: 1, uid: 99999 });
} catch (err) {
assert.ok(err);
assert.strictEqual(err.message, '[[error:no-user]]');
}
});


it('should throw an error if a non-privileged user tries to flag a privileged user', async () => {
const privEmail = `privileged${Date.now()}@user.com`;
const regEmail = `regular${Date.now()}@user.com`;
const privilegedUserId = await User.create({ username: 'privilegedUser', password: 'abcdef', email: privEmail });
const regularUserId = await User.create({ username: 'regularUser', password: 'abcdef', email: regEmail });

await Groups.join('administrators', privilegedUserId);

try {
await Flags.validate({ type: 'user', id: privilegedUserId, uid: regularUserId });
} catch (err) {
assert.ok(err);
assert.strictEqual(err.message, '[[error:cant-flag-privileged]]');
}

await Groups.leave('administrators', privilegedUserId);
});
});

describe('.validate()', () => {
it('should not allow a user to flag themselves', async () => {
const uniqueEmail = `selfflag${Date.now()}@user.com`;
const userId = await User.create({ username: 'selfFlagger', password: 'abcdef', email: uniqueEmail });

try {
await Flags.validate({
type: 'user',
id: userId,
uid: userId,
});
} catch (err) {
assert.ok(err);
assert.strictEqual(err.message, '[[error:cant-flag-self]]');
}
});
});
Loading