diff --git a/app/queries/servers/preference.test.ts b/app/queries/servers/preference.test.ts new file mode 100644 index 000000000..3a9a7abe4 --- /dev/null +++ b/app/queries/servers/preference.test.ts @@ -0,0 +1,408 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Preferences} from '@constants'; +import {CATEGORIES_TO_KEEP} from '@constants/preferences'; +import DatabaseManager from '@database/manager'; + +import { + prepareMyPreferences, + queryPreferencesByCategoryAndName, + getThemeForCurrentTeam, + deletePreferences, + differsFromLocalNameFormat, + getHasCRTChanged, + queryDisplayNamePreferences, + querySavedPostsPreferences, + queryThemePreferences, + querySidebarPreferences, + queryEmojiPreferences, + queryAdvanceSettingsPreferences, +} from './preference'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type {Database} from '@nozbe/watermelondb'; + +describe('Preference Queries', () => { + const serverUrl = 'baseHandler.test.com'; + let database: Database; + let operator: ServerDataOperator; + + beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + database = serverDatabaseAndOperator.database; + operator = serverDatabaseAndOperator.operator; + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + }); + + describe('prepareMyPreferences', () => { + it('should prepare preference records', async () => { + const preferences = [ + { + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: 'name_format', + value: 'username', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.THEME, + name: 'theme', + value: '{"type":"dark"}', + }, + ]; + + const records = await prepareMyPreferences(operator, preferences); + expect(records.length).toBe(2); + expect(records[0].name).toBe('name_format'); + expect(records[0]._preparedState).toBe('create'); + + expect(records[1].name).toBe('theme'); + expect(records[1]._preparedState).toBe('create'); + }); + + it('should handle sync flag', async () => { + const oldValue = 'username'; + const newValue = 'username-new'; + + const preferences = [{ + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: 'name_format', + value: newValue, + }]; + + await operator.handlePreferences({ + preferences: [ + { + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: 'name_format', + value: oldValue, + }, + + // with sync, this record will be deleted since it's not in the preferences array + { + user_id: 'user1', + category: Preferences.CATEGORIES.THEME, + name: 'theme', + value: '{"type":"dark"}', + }, + ], + prepareRecordsOnly: false, + }); + + await database.write(async () => { + const records = await prepareMyPreferences(operator, preferences, true); + + expect(records[0]._preparedState).toBe('update'); + expect(records[1]._preparedState).toBe('destroyPermanently'); + + // without this, the test will fail because of this error: + // Diagnostic error: record.prepareUpdate was called on Preference#xxxx but wasn't sent to batch() synchronously -- this is bad! + await database.batch(...records); + }); + + const storedPrefs = await queryDisplayNamePreferences(database).fetch(); + expect(storedPrefs.length).toBe(1); + expect(storedPrefs[0].value).toEqual(newValue); + + const themePrefs = await queryThemePreferences(database).fetch(); + expect(themePrefs.length).toBe(0); + }); + }); + + describe('queryPreferencesByCategoryAndName', () => { + it('should query by category only', async () => { + await operator.handlePreferences({ + preferences: [ + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test1', + value: 'value1', + }, + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test2', + value: 'value2', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: 'name_format', + value: 'username', + }, + ], + prepareRecordsOnly: false, + }); + + const prefs = await queryPreferencesByCategoryAndName(database, CATEGORIES_TO_KEEP.ADVANCED_SETTINGS).fetch(); + expect(prefs.length).toBe(2); + }); + + it('should query by category and name', async () => { + await operator.handlePreferences({ + preferences: [ + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test1', + value: 'value1', + }, + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test2', + value: 'value2', + }, + ], + prepareRecordsOnly: false, + }); + + const prefs = await queryPreferencesByCategoryAndName(database, CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, 'test1').fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].name).toBe('test1'); + }); + + it('should query by category, name and value', async () => { + await operator.handlePreferences({ + preferences: [ + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test1', + value: 'value1', + }, + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test1', + value: 'value2', + }, + ], + prepareRecordsOnly: false, + }); + + const prefs = await queryPreferencesByCategoryAndName(database, CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, 'test1', 'value1').fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].value).toBe('value1'); + }); + }); + + describe('getThemeForCurrentTeam', () => { + it('should return undefined if no theme exists', async () => { + const theme = await getThemeForCurrentTeam(database); + expect(theme).toBeUndefined(); + }); + + it('should return theme for current team', async () => { + const teamId = 'team1'; + const themeValue = { + type: 'dark', + sidebarBg: '#000000', + }; + + await operator.handleSystem({ + systems: [{id: 'currentTeamId', value: teamId}], + prepareRecordsOnly: false, + }); + + await operator.handlePreferences({ + preferences: [{ + user_id: 'user1', + category: Preferences.CATEGORIES.THEME, + name: teamId, + value: JSON.stringify(themeValue), + }], + prepareRecordsOnly: false, + }); + + const theme = await getThemeForCurrentTeam(database); + expect(theme).toEqual(themeValue); + }); + }); + + describe('deletePreferences', () => { + it('should delete preferences', async () => { + const pref1 = { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test1', + value: 'value1', + }; + + await operator.handlePreferences({ + preferences: [ + pref1, + { + user_id: 'user1', + category: CATEGORIES_TO_KEEP.ADVANCED_SETTINGS, + name: 'test2', + value: 'value2', + }, + ], + prepareRecordsOnly: false, + }); + + const prefsToDelete = [pref1]; + + const success = await deletePreferences({database, operator}, prefsToDelete); + expect(success).toBe(true); + + const remaining = await queryPreferencesByCategoryAndName(database, CATEGORIES_TO_KEEP.ADVANCED_SETTINGS).fetch(); + expect(remaining.length).toBe(1); + expect(remaining[0].name).toBe('test2'); + }); + + it('should handle empty prefs gracefully', async () => { + const success = await deletePreferences({database, operator}, []); + expect(success).toBe(true); + }); + }); + + describe('differsFromLocalNameFormat', () => { + it('should detect when name format differs', async () => { + await operator.handlePreferences({ + preferences: [{ + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: Preferences.NAME_NAME_FORMAT, + value: 'username', + }], + prepareRecordsOnly: false, + }); + + const newPrefs = [{ + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: Preferences.NAME_NAME_FORMAT, + value: 'nickname', + }]; + + const differs = await differsFromLocalNameFormat(database, newPrefs); + expect(differs).toBe(true); + }); + + it('should handle empty preferences', async () => { + const differs = await differsFromLocalNameFormat(database, []); + expect(differs).toBe(false); + }); + }); + + describe('getHasCRTChanged', () => { + it('should detect CRT changes', async () => { + await operator.handlePreferences({ + preferences: [{ + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: Preferences.COLLAPSED_REPLY_THREADS, + value: 'off', + }], + prepareRecordsOnly: false, + }); + + const newPrefs = [{ + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: Preferences.COLLAPSED_REPLY_THREADS, + value: 'on', + }]; + + const changed = await getHasCRTChanged(database, newPrefs); + expect(changed).toBe(true); + }); + + it('should handle no CRT preference', async () => { + const changed = await getHasCRTChanged(database, []); + expect(changed).toBe(false); + }); + }); + + describe('category-specific queries', () => { + beforeEach(async () => { + await operator.handlePreferences({ + preferences: [ + { + user_id: 'user1', + category: Preferences.CATEGORIES.DISPLAY_SETTINGS, + name: 'display_test', + value: 'display_value', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.SAVED_POST, + name: 'post1', + value: 'saved', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.THEME, + name: 'theme1', + value: '{"type":"dark"}', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.SIDEBAR_SETTINGS, + name: 'sidebar_test', + value: 'sidebar_value', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.EMOJI, + name: 'emoji_test', + value: 'emoji_value', + }, + { + user_id: 'user1', + category: Preferences.CATEGORIES.ADVANCED_SETTINGS, + name: 'advanced_test', + value: 'advanced_value', + }, + ], + prepareRecordsOnly: false, + }); + }); + + it('should query display name preferences', async () => { + const prefs = await queryDisplayNamePreferences(database).fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].category).toBe(Preferences.CATEGORIES.DISPLAY_SETTINGS); + }); + + it('should query saved posts preferences', async () => { + const prefs = await querySavedPostsPreferences(database).fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].category).toBe(Preferences.CATEGORIES.SAVED_POST); + }); + + it('should query theme preferences', async () => { + const prefs = await queryThemePreferences(database).fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].category).toBe(Preferences.CATEGORIES.THEME); + }); + + it('should query sidebar preferences', async () => { + const prefs = await querySidebarPreferences(database).fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].category).toBe(Preferences.CATEGORIES.SIDEBAR_SETTINGS); + }); + + it('should query emoji preferences', async () => { + const prefs = await queryEmojiPreferences(database, 'emoji_test').fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].category).toBe(Preferences.CATEGORIES.EMOJI); + }); + + it('should query advanced settings preferences', async () => { + const prefs = await queryAdvanceSettingsPreferences(database).fetch(); + expect(prefs.length).toBe(1); + expect(prefs[0].category).toBe(Preferences.CATEGORIES.ADVANCED_SETTINGS); + }); + }); +});