mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 06:07:21 +01:00
* Fix bug where updated webhook options failed to save This bug was caused by Mongoose not creating getters/setters for array elements (https://mongoosejs.com/docs/faq.html#array-changes-not-saved). So, although the webhook was being updated properly, Mongoose was not actually committing it to the database. Telling Mongoose that the array of webhooks has changed via `markModified` fixes the issue. Additionally, the relevant API test case was only checking whether or not the webhook returned from the PUT endpoint matched the expected update. Since the endpoint was returning the updated webhook without querying the database again, this test case would pass. It has been updated to check both the returned webhook as well as the version of the webhook that is saved to the database against the expected. In other words: `assert returned === saved === expected` Fixes #12336 * Call markModified on webhook.options instead of user.webhooks This tells Mongoose that only the modified webhook's options changed instead of telling it that the entire user.webhooks array changed, saving a costly DB update.
180 lines
5.1 KiB
JavaScript
180 lines
5.1 KiB
JavaScript
import { v4 as generateUUID } from 'uuid';
|
|
import {
|
|
generateUser,
|
|
translate as t,
|
|
} from '../../../../helpers/api-integration/v3';
|
|
import apiError from '../../../../../website/server/libs/apiError';
|
|
|
|
describe('PUT /user/webhook/:id', () => {
|
|
let user; let
|
|
webhookToUpdate;
|
|
|
|
beforeEach(async () => {
|
|
user = await generateUser();
|
|
|
|
webhookToUpdate = await user.post('/user/webhook', {
|
|
url: 'http://some-url.com',
|
|
label: 'Original Label',
|
|
enabled: true,
|
|
type: 'taskActivity',
|
|
options: { created: true, scored: true },
|
|
});
|
|
await user.post('/user/webhook', {
|
|
url: 'http://some-other-url.com',
|
|
enabled: false,
|
|
});
|
|
|
|
await user.sync();
|
|
});
|
|
|
|
it('returns an error if webhook with id does not exist', async () => {
|
|
await expect(user.put('/user/webhook/id-that-does-not-exist')).to.eventually.be.rejected.and.eql({
|
|
code: 404,
|
|
error: 'NotFound',
|
|
message: t('noWebhookWithId', { id: 'id-that-does-not-exist' }),
|
|
});
|
|
});
|
|
|
|
it('returns an error if validation fails', async () => {
|
|
await expect(user.put(`/user/webhook/${webhookToUpdate.id}`, { url: 'foo_invalid', enabled: true })).to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: 'User validation failed',
|
|
});
|
|
});
|
|
|
|
it('updates a webhook', async () => {
|
|
const url = 'http://a-new-url.com';
|
|
const type = 'groupChatReceived';
|
|
const label = 'New Label';
|
|
const options = { groupId: generateUUID() };
|
|
|
|
await user.put(`/user/webhook/${webhookToUpdate.id}`, {
|
|
url, type, options, label,
|
|
});
|
|
|
|
await user.sync();
|
|
|
|
const webhook = user.webhooks.find(hook => webhookToUpdate.id === hook.id);
|
|
|
|
expect(webhook.url).to.equal(url);
|
|
expect(webhook.label).to.equal(label);
|
|
expect(webhook.type).to.equal(type);
|
|
expect(webhook.options).to.eql(options);
|
|
});
|
|
|
|
it('ignores protected fields', async () => {
|
|
const failures = 3;
|
|
const lastFailureAt = new Date();
|
|
|
|
await user.put(`/user/webhook/${webhookToUpdate.id}`, {
|
|
failures, lastFailureAt,
|
|
});
|
|
|
|
await user.sync();
|
|
const webhook = user.webhooks.find(hook => webhookToUpdate.id === hook.id);
|
|
|
|
expect(webhook.failures).to.eql(0);
|
|
expect(webhook.lastFailureAt).to.eql(undefined);
|
|
});
|
|
|
|
it('updates a webhook with empty label', async () => {
|
|
const url = 'http://a-new-url.com';
|
|
const type = 'groupChatReceived';
|
|
const label = '';
|
|
const options = { groupId: generateUUID() };
|
|
|
|
await user.put(`/user/webhook/${webhookToUpdate.id}`, {
|
|
url, type, options, label,
|
|
});
|
|
|
|
await user.sync();
|
|
|
|
const webhook = user.webhooks.find(hook => webhookToUpdate.id === hook.id);
|
|
|
|
expect(webhook.url).to.equal(url);
|
|
expect(webhook.label).to.equal(label);
|
|
expect(webhook.type).to.equal(type);
|
|
expect(webhook.options).to.eql(options);
|
|
});
|
|
|
|
it('returns the updated webhook', async () => {
|
|
const url = 'http://a-new-url.com';
|
|
const type = 'groupChatReceived';
|
|
const options = { groupId: generateUUID() };
|
|
|
|
const response = await user.put(`/user/webhook/${webhookToUpdate.id}`, { url, type, options });
|
|
|
|
expect(response.url).to.eql(url);
|
|
expect(response.type).to.eql(type);
|
|
expect(response.options).to.eql(options);
|
|
});
|
|
|
|
it('cannot update the id', async () => {
|
|
const id = generateUUID();
|
|
const url = 'http://a-new-url.com';
|
|
|
|
await user.put(`/user/webhook/${webhookToUpdate.id}`, { url, id });
|
|
|
|
await user.sync();
|
|
|
|
const webhook = user.webhooks.find(hook => webhookToUpdate.id === hook.id);
|
|
|
|
expect(webhook.id).to.eql(webhookToUpdate.id);
|
|
expect(webhook.url).to.eql(url);
|
|
});
|
|
|
|
it('can update taskActivity options', async () => {
|
|
const type = 'taskActivity';
|
|
const options = {
|
|
checklistScored: true,
|
|
updated: false,
|
|
scored: false,
|
|
};
|
|
const expected = {
|
|
checklistScored: true,
|
|
created: true, // starting value
|
|
updated: false,
|
|
deleted: false, // starting value
|
|
scored: false,
|
|
};
|
|
|
|
const returnedWebhook = await user.put(`/user/webhook/${webhookToUpdate.id}`, { type, options });
|
|
|
|
await user.sync();
|
|
|
|
const savedWebhook = user.webhooks.find(hook => webhookToUpdate.id === hook.id);
|
|
|
|
expect(returnedWebhook.options).to.eql(expected);
|
|
expect(savedWebhook.options).to.eql(expected);
|
|
});
|
|
|
|
it('errors if taskActivity option is not a boolean', async () => {
|
|
const type = 'taskActivity';
|
|
const options = {
|
|
created: 'not a boolean',
|
|
updated: false,
|
|
deleted: true,
|
|
};
|
|
|
|
await expect(user.put(`/user/webhook/${webhookToUpdate.id}`, { type, options })).to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: t('webhookBooleanOption', { option: 'created' }),
|
|
});
|
|
});
|
|
|
|
it('errors if groupChatRecieved groupId option is not a uuid', async () => {
|
|
const type = 'groupChatReceived';
|
|
const options = {
|
|
groupId: 'not-a-uuid',
|
|
};
|
|
|
|
await expect(user.put(`/user/webhook/${webhookToUpdate.id}`, { type, options })).to.eventually.be.rejected.and.eql({
|
|
code: 400,
|
|
error: 'BadRequest',
|
|
message: apiError('groupIdRequired'),
|
|
});
|
|
});
|
|
});
|