fix: Missing error messages in Parse errors (#10304)

This commit is contained in:
Manuel
2026-03-24 18:56:25 +00:00
committed by GitHub
parent fe9fba61dd
commit f12804800b
5 changed files with 43 additions and 34 deletions
+24 -10
View File
@@ -956,7 +956,7 @@ describe('ParseLiveQuery', function () {
await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token'));
});
it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)('handle invalid websocket payload length', async done => {
it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)('handle invalid websocket payload length', async () => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
@@ -980,17 +980,31 @@ describe('ParseLiveQuery', function () {
// 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length
// https://tools.ietf.org/html/rfc6455#section-5.2
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
client.socket._socket.write(Buffer.from([0x89, 0xfe]));
subscription.on('update', async object => {
expect(object.get('foo')).toBe('bar');
done();
// Wait for the initial subscription 'open' event (fires 200ms after subscribe)
// before sending the invalid frame, so we don't confuse it with the reconnection 'open'
await new Promise(resolve => subscription.on('open', resolve));
// Now listen for close followed by reopen from the reconnection cycle
const reopened = new Promise(resolve => {
subscription.on('close', () => {
subscription.on('open', resolve);
});
});
// Wait for Websocket timeout to reconnect
setTimeout(async () => {
object.set({ foo: 'bar' });
await object.save();
}, 1000);
client.socket._socket.write(Buffer.from([0x89, 0xfe]));
await reopened;
// After reconnection, save an update and verify the subscription receives it
const updated = new Promise(resolve => {
subscription.on('update', object => {
expect(object.get('foo')).toBe('bar');
resolve();
});
});
object.set({ foo: 'bar' });
await object.save();
await updated;
});
it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)('should execute live query update on email validation', async done => {
+10 -10
View File
@@ -205,16 +205,16 @@ describe('rest query', () => {
'_password_changed_at',
'_password_history',
];
await Promise.all([
...internalFields.map(field =>
expectAsync(new Parse.Query(Parse.User).exists(field).find()).toBeRejectedWith(
new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${field}`)
)
),
...internalFields.map(field =>
new Parse.Query(Parse.User).exists(field).find({ useMasterKey: true })
),
]);
// Run rejection and success queries sequentially to avoid orphaned promises
// that can cause unhandled rejections when Promise.all short-circuits
for (const field of internalFields) {
await expectAsync(new Parse.Query(Parse.User).exists(field).find()).toBeRejectedWith(
new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${field}`)
);
}
for (const field of internalFields) {
await new Parse.Query(Parse.User).exists(field).find({ useMasterKey: true });
}
});
it('query protected field', async () => {
+1 -6
View File
@@ -9,12 +9,7 @@ global.currentSpec = null;
* a number of times to reduce the chance of false negatives. The test name must be the same
* as the one displayed in the CI log test output.
*/
const flakyTests = [
// Timeout
"ParseLiveQuery handle invalid websocket payload length",
// Unhandled promise rejection: TypeError: message.split is not a function
"rest query query internal field",
];
const flakyTests = [];
/** The minimum execution time in seconds for a test to be considered slow. */
const slowTestLimit = 2;
+2 -2
View File
@@ -545,7 +545,7 @@ class DatabaseController {
try {
Utils.checkProhibitedKeywords(this.options, update);
} catch (error) {
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, `${error}`));
}
try {
const { validateFileUrlsInObject } = require('../FileUrlValidator');
@@ -888,7 +888,7 @@ class DatabaseController {
try {
Utils.checkProhibitedKeywords(this.options, object);
} catch (error) {
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, `${error}`));
}
try {
const { validateFileUrlsInObject } = require('../FileUrlValidator');
+6 -6
View File
@@ -313,7 +313,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () {
try {
Utils.checkProhibitedKeywords(this.config, this.data);
} catch (error) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, error);
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `${error}`);
}
});
};
@@ -1199,15 +1199,15 @@ RestWrite.prototype.handleSession = function () {
if (this.query) {
if (this.data.user && !this.auth.isMaster && this.data.user.objectId != this.auth.user.id) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: user');
} else if (this.data.installationId) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: installationId');
} else if (this.data.sessionToken) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: sessionToken');
} else if (this.data.expiresAt && !this.auth.isMaster && !this.auth.isMaintenance) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: expiresAt');
} else if (this.data.createdWith && !this.auth.isMaster && !this.auth.isMaintenance) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: createdWith');
}
if (!this.auth.isMaster) {
this.query = {