diff --git a/lib/ops/user-store-ops.js b/lib/ops/user-store-ops.js index f8fe7c031..5353cdcaf 100644 --- a/lib/ops/user-store-ops.js +++ b/lib/ops/user-store-ops.js @@ -1,90 +1,119 @@ // @flow import { type BaseStoreOpsHandlers } from './base-ops.js'; import type { UserInfo, UserInfos } from '../types/user-types.js'; import { values } from '../utils/objects.js'; // client types export type ReplaceUserOperation = { +type: 'replace_user', +payload: UserInfo, }; export type RemoveUsersOperation = { +type: 'remove_users', +payload: { +ids: $ReadOnlyArray }, }; export type RemoveAllUsersOperation = { +type: 'remove_all_users', }; export type UserStoreOperation = | ReplaceUserOperation | RemoveUsersOperation | RemoveAllUsersOperation; // SQLite types export type ClientDBUserInfo = { +id: string, +userInfo: string, }; export type ClientDBReplaceUserOperation = { +type: 'replace_user', +payload: ClientDBUserInfo, }; export type ClientDBUserStoreOperation = | ClientDBReplaceUserOperation | RemoveUsersOperation | RemoveAllUsersOperation; function convertUserInfosToReplaceUserOps( userInfos: UserInfos, ): $ReadOnlyArray { return values(userInfos).map(userInfo => ({ type: 'replace_user', payload: userInfo, })); } +function convertUserInfoToClientDBUserInfo(user: UserInfo): ClientDBUserInfo { + return { + id: user.id, + userInfo: JSON.stringify(user), + }; +} + const userStoreOpsHandlers: BaseStoreOpsHandlers< UserInfos, UserStoreOperation, ClientDBUserStoreOperation, UserInfos, ClientDBUserInfo, > = { processStoreOperations( userInfos: UserInfos, ops: $ReadOnlyArray, ): UserInfos { if (ops.length === 0) { return userInfos; } let processedUserInfos = { ...userInfos }; for (const operation: UserStoreOperation of ops) { if (operation.type === 'replace_user') { processedUserInfos[operation.payload.id] = operation.payload; } else if (operation.type === 'remove_users') { for (const id of operation.payload.ids) { delete processedUserInfos[id]; } } else if (operation.type === 'remove_all_users') { processedUserInfos = {}; } } return processedUserInfos; }, - convertOpsToClientDBOps(): $ReadOnlyArray { - return []; + convertOpsToClientDBOps( + ops: $ReadOnlyArray, + ): $ReadOnlyArray { + return ops.map(operation => { + if ( + operation.type === 'remove_users' || + operation.type === 'remove_all_users' + ) { + return operation; + } + return { + type: 'replace_user', + payload: convertUserInfoToClientDBUserInfo(operation.payload), + }; + }); }, - translateClientDBData(): UserInfos { - return {}; + translateClientDBData(users: $ReadOnlyArray): UserInfos { + const userInfos: { [id: string]: UserInfo } = {}; + users.forEach(dbUser => { + userInfos[dbUser.id] = JSON.parse(dbUser.userInfo); + }); + + return userInfos; }, }; -export { userStoreOpsHandlers, convertUserInfosToReplaceUserOps }; +export { + userStoreOpsHandlers, + convertUserInfosToReplaceUserOps, + convertUserInfoToClientDBUserInfo, +}; diff --git a/web/database/queries/user-quries.test.js b/web/database/queries/user-quries.test.js new file mode 100644 index 000000000..b18c87ec9 --- /dev/null +++ b/web/database/queries/user-quries.test.js @@ -0,0 +1,106 @@ +// @flow + +import { + convertUserInfoToClientDBUserInfo, + userStoreOpsHandlers, +} from 'lib/ops/user-store-ops.js'; +import type { UserInfo } from 'lib/types/user-types.js'; + +import { getDatabaseModule } from '../db-module.js'; +import type { EmscriptenModule } from '../types/module.js'; +import { type SQLiteQueryExecutor } from '../types/sqlite-query-executor.js'; +import { clearSensitiveData } from '../utils/db-utils.js'; + +const FILE_PATH = 'test.sqlite'; + +const TEST_USER_1: UserInfo = { + id: '256', + username: 'Test1', + relationshipStatus: 2, + avatar: { + type: 'image', + uri: 'av1', + }, +}; +const TEST_USER_2: UserInfo = { + id: '512', + username: 'Test2', + relationshipStatus: 3, + avatar: { + type: 'image', + uri: 'av2', + }, +}; + +describe('User Store queries', () => { + let queryExecutor: ?SQLiteQueryExecutor = null; + let dbModule: ?EmscriptenModule = null; + + beforeAll(async () => { + dbModule = getDatabaseModule(); + }); + + beforeEach(() => { + if (!dbModule) { + return; + } + queryExecutor = new dbModule.SQLiteQueryExecutor(FILE_PATH); + queryExecutor?.replaceUser(convertUserInfoToClientDBUserInfo(TEST_USER_1)); + queryExecutor?.replaceUser(convertUserInfoToClientDBUserInfo(TEST_USER_2)); + }); + + afterEach(() => { + if (!dbModule || !queryExecutor) { + return; + } + clearSensitiveData(dbModule, FILE_PATH, queryExecutor); + }); + + it('should return all users', () => { + const users = queryExecutor?.getAllUsers(); + expect(users?.length).toBe(2); + }); + + it('should remove all users', () => { + queryExecutor?.removeAllUsers(); + const users = queryExecutor?.getAllUsers(); + expect(users?.length).toBe(0); + }); + + it('should update user avatar', () => { + const user2Updated: UserInfo = { + id: '512', + username: 'Test2', + avatar: { + type: 'image', + uri: 'new_av', + }, + }; + + queryExecutor?.replaceUser(convertUserInfoToClientDBUserInfo(user2Updated)); + + const dbUsers = queryExecutor?.getAllUsers(); + if (!dbUsers) { + throw new Error('users not defined'); + } + expect(dbUsers.length).toBe(2); + + const users = userStoreOpsHandlers.translateClientDBData(dbUsers); + expect(users['512']).toBeDefined(); + expect(users['512'].avatar?.uri).toBe(user2Updated.avatar?.uri); + expect(users['512'].relationshipStatus).not.toBeDefined(); + }); + + it('should remove user', () => { + queryExecutor?.removeUsers(['512']); + + const dbUsers = queryExecutor?.getAllUsers(); + if (!dbUsers) { + throw new Error('users not defined'); + } + expect(dbUsers.length).toBe(1); + + const users = userStoreOpsHandlers.translateClientDBData(dbUsers); + expect(users['256']).toBeDefined(); + }); +});