diff --git a/admin/app/controllers/authenticated/users/list.js b/admin/app/controllers/authenticated/users/list.js index 9e2436400bc..dff191c7d13 100644 --- a/admin/app/controllers/authenticated/users/list.js +++ b/admin/app/controllers/authenticated/users/list.js @@ -1,13 +1,25 @@ import Controller from '@ember/controller'; import { action } from '@ember/object'; +import { service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; const DEFAULT_PAGE_NUMBER = 1; +const DEFAULT_QUERY_TYPE = 'CONTAINS'; export default class ListController extends Controller { - queryParams = ['pageNumber', 'pageSize', 'id', 'firstName', 'lastName', 'email', 'username']; + @service intl; + + queryParams = ['pageNumber', 'pageSize', 'id', 'firstName', 'lastName', 'email', 'username', 'queryType']; + + get queryTypes() { + return [ + { value: 'CONTAINS', label: this.intl.t('pages.users-list.query.contains') }, + { value: 'EXACT_QUERY', label: this.intl.t('pages.users-list.query.exact') }, + ]; + } @tracked pageNumber = DEFAULT_PAGE_NUMBER; + @tracked queryType = DEFAULT_QUERY_TYPE; @tracked pageSize = 10; @tracked id = null; @tracked firstName = null; @@ -20,6 +32,7 @@ export default class ListController extends Controller { @tracked lastNameForm = null; @tracked emailForm = null; @tracked usernameForm = null; + @tracked queryTypeForm = DEFAULT_QUERY_TYPE; @action async refreshModel(event) { @@ -29,6 +42,7 @@ export default class ListController extends Controller { this.lastName = this.lastNameForm; this.email = this.emailForm; this.username = this.usernameForm; + this.queryType = this.queryTypeForm; this.pageNumber = DEFAULT_PAGE_NUMBER; } @@ -56,6 +70,10 @@ export default class ListController extends Controller { onChangeUsername(event) { this.usernameForm = event.target.value; } + @action + onChangeQueryType(value) { + this.queryTypeForm = value; + } @action clearSearchFields() { @@ -64,11 +82,13 @@ export default class ListController extends Controller { this.lastName = null; this.email = null; this.username = null; + this.queryType = DEFAULT_QUERY_TYPE; this.idForm = null; this.firstNameForm = null; this.lastNameForm = null; this.emailForm = null; this.usernameForm = null; + this.queryTypeForm = DEFAULT_QUERY_TYPE; } } diff --git a/admin/app/routes/authenticated/users/list.js b/admin/app/routes/authenticated/users/list.js index b933fd90162..f992477e227 100644 --- a/admin/app/routes/authenticated/users/list.js +++ b/admin/app/routes/authenticated/users/list.js @@ -7,6 +7,7 @@ export default class ListRoute extends Route { queryParams = { pageNumber: { refreshModel: true }, pageSize: { refreshModel: true }, + queryType: { refreshModel: true }, id: { refreshModel: true }, firstName: { refreshModel: true }, lastName: { refreshModel: true }, @@ -30,6 +31,7 @@ export default class ListRoute extends Route { number: params.pageNumber, size: params.pageSize, }, + queryType: params.queryType, }); return users; } catch (error) { @@ -46,6 +48,7 @@ export default class ListRoute extends Route { controller.lastName = null; controller.email = null; controller.username = null; + controller.queryType = 'CONTAINS'; } } } diff --git a/admin/app/styles/authenticated/users/list.scss b/admin/app/styles/authenticated/users/list.scss index e0191c782bd..60448071dbc 100644 --- a/admin/app/styles/authenticated/users/list.scss +++ b/admin/app/styles/authenticated/users/list.scss @@ -1,15 +1,26 @@ .user-list-form { display: flex; flex-wrap: wrap; - gap: 8px; + gap: var(--pix-spacing-2x); justify-content: flex-end; + .pix-select { + min-width: 7.5rem; + margin-right: var(--pix-spacing-2x); + } + + .pix-select .pix-icon { + width: 1.315rem; + height: 1.315rem; + + } + &__input--small { - max-width: 190px; + max-width: 9.35rem; } &__actions { display: flex; - gap: 8px; + gap: var(--pix-spacing-2x); } } diff --git a/admin/app/templates/authenticated/users/list.hbs b/admin/app/templates/authenticated/users/list.hbs index 2321fdb0717..def2ef1c675 100644 --- a/admin/app/templates/authenticated/users/list.hbs +++ b/admin/app/templates/authenticated/users/list.hbs @@ -4,6 +4,13 @@

Rechercher un(des) utilisateur(s)

+ } */ const findPaginatedFilteredUsers = async function (request, h, dependencies = { userForAdminSerializer }) { - const { filter, page } = request.query; + const { filter, page, queryType } = request.query; - const { models: users, pagination } = await usecases.findPaginatedFilteredUsers({ filter, page }); + const { models: users, pagination } = await usecases.findPaginatedFilteredUsers({ filter, page, queryType }); return dependencies.userForAdminSerializer.serialize(users, pagination); }; diff --git a/api/src/identity-access-management/application/user/user.admin.route.js b/api/src/identity-access-management/application/user/user.admin.route.js index 4836267ad3b..3eca849f58e 100644 --- a/api/src/identity-access-management/application/user/user.admin.route.js +++ b/api/src/identity-access-management/application/user/user.admin.route.js @@ -5,6 +5,7 @@ import { securityPreHandlers } from '../../../shared/application/security-pre-ha import { SUPPORTED_LOCALES } from '../../../shared/domain/constants.js'; import { AVAILABLE_LANGUAGES } from '../../../shared/domain/services/language-service.js'; import { identifiersType } from '../../../shared/domain/types/identifiers-type.js'; +import { QUERY_TYPES } from '../../domain/constants/user-query.js'; import { userAdminController } from './user.admin.controller.js'; export const userAdminRoutes = [ @@ -62,6 +63,10 @@ export const userAdminRoutes = [ number: Joi.number().integer().empty('').allow(null).optional(), size: Joi.number().integer().empty('').allow(null).optional(), }).default({}), + queryType: Joi.string() + .valid(QUERY_TYPES.CONTAINS, QUERY_TYPES.EXACT_QUERY) + .default(QUERY_TYPES.CONTAINS) + .optional(), }), }, handler: (request, h) => userAdminController.findPaginatedFilteredUsers(request, h), diff --git a/api/src/identity-access-management/domain/constants/user-query.js b/api/src/identity-access-management/domain/constants/user-query.js new file mode 100644 index 00000000000..d7dff40d1a7 --- /dev/null +++ b/api/src/identity-access-management/domain/constants/user-query.js @@ -0,0 +1,4 @@ +export const QUERY_TYPES = { + CONTAINS: 'CONTAINS', + EXACT_QUERY: 'EXACT_QUERY', +}; diff --git a/api/src/identity-access-management/domain/usecases/find-paginated-filtered-users.usecase.js b/api/src/identity-access-management/domain/usecases/find-paginated-filtered-users.usecase.js index c680bab101a..b49d5d74668 100644 --- a/api/src/identity-access-management/domain/usecases/find-paginated-filtered-users.usecase.js +++ b/api/src/identity-access-management/domain/usecases/find-paginated-filtered-users.usecase.js @@ -1,5 +1,5 @@ -const findPaginatedFilteredUsers = function ({ filter, page, userRepository }) { - return userRepository.findPaginatedFiltered({ filter, page }); +const findPaginatedFilteredUsers = function ({ filter, page, queryType, userRepository }) { + return userRepository.findPaginatedFiltered({ filter, page, queryType }); }; export { findPaginatedFilteredUsers }; diff --git a/api/src/identity-access-management/infrastructure/repositories/user.repository.js b/api/src/identity-access-management/infrastructure/repositories/user.repository.js index 05d4adf1c62..8b841407631 100644 --- a/api/src/identity-access-management/infrastructure/repositories/user.repository.js +++ b/api/src/identity-access-management/infrastructure/repositories/user.repository.js @@ -15,6 +15,7 @@ import { CertificationCenterMembership } from '../../../shared/domain/models/Cer import { Membership } from '../../../shared/domain/models/Membership.js'; import { fetchPage, isUniqConstraintViolated } from '../../../shared/infrastructure/utils/knex-utils.js'; import { NON_OIDC_IDENTITY_PROVIDERS } from '../../domain/constants/identity-providers.js'; +import { QUERY_TYPES } from '../../domain/constants/user-query.js'; import { User } from '../../domain/models/User.js'; import { UserDetailsForAdmin } from '../../domain/models/UserDetailsForAdmin.js'; import { UserLogin } from '../../domain/models/UserLogin.js'; @@ -159,9 +160,9 @@ const getUserDetailsForAdmin = async function (userId) { }); }; -const findPaginatedFiltered = async function ({ filter, page }) { +const findPaginatedFiltered = async function ({ filter, page, queryType = QUERY_TYPES.CONTAINS }) { const query = knex('users') - .where((qb) => _setSearchFiltersForQueryBuilder(filter, qb)) + .where((qb) => _setSearchFiltersForQueryBuilder(filter, qb, queryType)) .orderBy([{ column: 'firstName', order: 'asc' }, { column: 'lastName', order: 'asc' }, { column: 'id' }]); const { results, pagination } = await fetchPage(query, page); @@ -625,22 +626,24 @@ function _toDomainFromDTO({ }); } -function _setSearchFiltersForQueryBuilder(filter, qb) { - const { id, firstName, lastName, email, username } = filter; - +function _setSearchFiltersForQueryBuilder(filter, qb, queryType) { + const id = filter.id; + const fields = ['email', 'firstName', 'lastName', 'email', 'username']; if (id) { qb.where({ id }); } - if (firstName) { - qb.whereILike('firstName', `%${firstName}%`); - } - if (lastName) { - qb.whereILike('lastName', `%${lastName}%`); - } - if (email) { - qb.whereILike('email', `%${email}%`); - } - if (username) { - qb.whereILike('username', `%${username}%`); + + fields.forEach((field) => { + if (filter[field]) { + _applyQueryType(field, filter[field], qb, queryType); + } + }); +} + +function _applyQueryType(field, value, qb, queryType) { + if (queryType === QUERY_TYPES.EXACT_QUERY) { + qb.where(field, value); + } else { + qb.whereILike(field, `%${value}%`); } } diff --git a/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js b/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js index c770831e724..bc140d7a782 100644 --- a/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js +++ b/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js @@ -1,3 +1,4 @@ +import { QUERY_TYPES } from '../../../../../src/identity-access-management/domain/constants/user-query.js'; import { createServer, databaseBuilder, @@ -44,32 +45,59 @@ describe('Acceptance | Identity Access Management | Application | Route | Admin }); describe('GET /api/admin/users', function () { - context('When filters match a list of users', function () { - let requestOptions; + let requestOptions; - beforeEach(async function () { - const user = await insertUserWithRoleSuperAdmin(); - const params = '?filter[firstName]=Ann' + '&page[number]=1&page[size]=25'; - - requestOptions = { - method: 'GET', - url: `/api/admin/users${params}`, - headers: { authorization: generateValidRequestAuthorizationHeader(user.id) }, - }; + beforeEach(async function () { + await databaseBuilder.factory.buildUser({ firstName: 'Ann' }); + await databaseBuilder.factory.buildUser({ firstName: 'Anne' }); + await databaseBuilder.factory.buildUser({ firstName: 'Annie' }); + await databaseBuilder.factory.buildUser({ firstName: 'Lisa' }); + await databaseBuilder.commit(); + }); - await databaseBuilder.factory.buildUser({ firstName: 'Ann' }); - await databaseBuilder.factory.buildUser({ firstName: 'Anne' }); - await databaseBuilder.factory.buildUser({ firstName: 'Annie' }); - await databaseBuilder.commit(); + context('When EXACT_QUERY type is settled', function () { + context('When filters match a list of users', function () { + it('retrieves this list of users', async function () { + // given + const user = await insertUserWithRoleSuperAdmin(); + const params = + '?filter[firstName]=Ann' + '&page[number]=1&page[size]=25' + `&queryType=${QUERY_TYPES.EXACT_QUERY}`; + + requestOptions = { + method: 'GET', + url: `/api/admin/users${params}`, + headers: { authorization: generateValidRequestAuthorizationHeader(user.id) }, + }; + // when + const response = await server.inject(requestOptions); + + // then + const { result, statusCode } = response; + expect(statusCode).to.equal(200); + expect(result.data).to.have.lengthOf(1); + }); }); - - it('retrieves this list of users', async function () { - // when - const response = await server.inject(requestOptions); - // then - const { result, statusCode } = response; - expect(statusCode).to.equal(200); - expect(result.data).to.have.lengthOf(3); + }); + context('When CONTAINS type is settled', function () { + context('When filters match a list of users', function () { + it('retrieves this list of users', async function () { + // given + const user = await insertUserWithRoleSuperAdmin(); + const params = + '?filter[firstName]=Ann' + '&page[number]=1&page[size]=25' + `&queryType=${QUERY_TYPES.CONTAINS}`; + + requestOptions = { + method: 'GET', + url: `/api/admin/users${params}`, + headers: { authorization: generateValidRequestAuthorizationHeader(user.id) }, + }; + // when + const response = await server.inject(requestOptions); + // then + const { result, statusCode } = response; + expect(statusCode).to.equal(200); + expect(result.data).to.have.lengthOf(3); + }); }); }); }); diff --git a/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js b/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js index c83566e882f..caaeacc3f86 100644 --- a/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js +++ b/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js @@ -1,5 +1,6 @@ import { identityAccessManagementRoutes } from '../../../../../src/identity-access-management/application/routes.js'; import { userAdminController } from '../../../../../src/identity-access-management/application/user/user.admin.controller.js'; +import { QUERY_TYPES } from '../../../../../src/identity-access-management/domain/constants/user-query.js'; import { securityPreHandlers } from '../../../../../src/shared/application/security-pre-handlers.js'; import { expect, HttpTestServer, sinon } from '../../../../test-helper.js'; @@ -22,7 +23,7 @@ describe('Integration | Identity Access Management | Application | Route | Admin // when const response = await httpTestServer.request( 'GET', - '/api/admin/users?filter[firstName]=Bruce&filter[lastName]=Wayne&filter[email]=batman@gotham.city&page[number]=3&page[size]=25', + `/api/admin/users?filter[firstName]=Bruce&filter[lastName]=Wayne&filter[email]=batman@gotham.city&page[number]=3&page[size]=25&queryType=${QUERY_TYPES.CONTAINS}`, ); // then @@ -43,7 +44,7 @@ describe('Integration | Identity Access Management | Application | Route | Admin // when const response = await httpTestServer.request( 'GET', - '/api/admin/users?filter[firstName]=Bruce&filter[lastName]=Wayne&filter[email]=batman@gotham.city&page[number]=3&page[size]=25', + `/api/admin/users?filter[firstName]=Bruce&filter[lastName]=Wayne&filter[email]=batman@gotham.city&page[number]=3&page[size]=25&queryType=${QUERY_TYPES.CONTAINS}`, ); // then diff --git a/api/tests/identity-access-management/integration/infrastructure/repositories/user.repository.test.js b/api/tests/identity-access-management/integration/infrastructure/repositories/user.repository.test.js index bd8172f0105..5a87573f23f 100644 --- a/api/tests/identity-access-management/integration/infrastructure/repositories/user.repository.test.js +++ b/api/tests/identity-access-management/integration/infrastructure/repositories/user.repository.test.js @@ -101,7 +101,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository describe('find user', function () { describe('#findByExternalIdentifier', function () { context('when identityProvider is generic', function () { - it('should return user informations for the given external identity id and identity provider', async function () { + it('returns user informations for the given external identity id and identity provider', async function () { // given const externalIdentityId = 'external-identity-id'; const userId = databaseBuilder.factory.buildUser().id; @@ -125,7 +125,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context('when identityProvider is POLE_EMPLOI', function () { - it('should return user informations for the given external identity id and identity provider', async function () { + it('returns user informations for the given external identity id and identity provider', async function () { // given const externalIdentityId = 'external-identity-id'; const userId = databaseBuilder.factory.buildUser().id; @@ -147,7 +147,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); }); - it('should return undefined when no user was found with this external identity id', async function () { + it('returns undefined when no user was found with this external identity id', async function () { // given const badId = 'not-exist-external-identity-id'; @@ -161,7 +161,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository return expect(foundUser).to.be.null; }); - it('should return null when the identity provider provided is PIX', async function () { + it('returns null when the identity provider provided is PIX', async function () { // given const externalIdentityId = 'external-identity-id'; const userId = databaseBuilder.factory.buildUser().id; @@ -183,7 +183,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository describe('#findPaginatedFiltered', function () { context('when there are users in the database', function () { - it('should return an array of users', async function () { + it('returns an array of users', async function () { // given const filter = {}; const page = { number: 1, size: 10 }; @@ -281,7 +281,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context('when there are lots of users (> 10) in the database', function () { - it('should return paginated matching users', async function () { + it('returns paginated matching users', async function () { // given const filter = {}; const page = { number: 1, size: 3 }; @@ -297,234 +297,284 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(pagination).to.deep.equal(expectedPagination); }); }); + context('when default/contains request type is settled', function () { + context('when there are multiple users matching the same "first name" search pattern', function () { + beforeEach(function () { + databaseBuilder.factory.buildUser({ firstName: 'Son Gohan' }); + databaseBuilder.factory.buildUser({ firstName: 'Son Goku' }); + databaseBuilder.factory.buildUser({ firstName: 'Son Goten' }); + databaseBuilder.factory.buildUser({ firstName: 'Vegeta' }); + databaseBuilder.factory.buildUser({ firstName: 'Piccolo' }); + return databaseBuilder.commit(); + }); - context('when there are multiple users matching the same "first name" search pattern', function () { - beforeEach(function () { - databaseBuilder.factory.buildUser({ firstName: 'Son Gohan' }); - databaseBuilder.factory.buildUser({ firstName: 'Son Goku' }); - databaseBuilder.factory.buildUser({ firstName: 'Son Goten' }); - databaseBuilder.factory.buildUser({ firstName: 'Vegeta' }); - databaseBuilder.factory.buildUser({ firstName: 'Piccolo' }); - return databaseBuilder.commit(); - }); - - it('should return only users matching "first name" if given in filter', async function () { - // given - const filter = { firstName: 'Go' }; - const page = { number: 1, size: 10 }; - const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; - - // when - const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); - - // then - expect(map(matchingUsers, 'firstName')).to.have.members(['Son Gohan', 'Son Goku', 'Son Goten']); - expect(pagination).to.deep.equal(expectedPagination); - }); - }); + it('returns only users matching "first name" if given in filter', async function () { + // given + const filter = { firstName: 'Go' }; + const page = { number: 1, size: 10 }; + const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; - context('when there are multiple users matching the same "last name" search pattern', function () { - beforeEach(async function () { - each( - [ - { firstName: 'Anakin', lastName: 'Skywalker' }, - { firstName: 'Luke', lastName: 'Skywalker' }, - { firstName: 'Leia', lastName: 'Skywalker' }, - { firstName: 'Han', lastName: 'Solo' }, - { firstName: 'Ben', lastName: 'Solo' }, - ], - (user) => { - databaseBuilder.factory.buildUser(user); - }, - ); + // when + const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); - await databaseBuilder.commit(); + // then + expect(map(matchingUsers, 'firstName')).to.have.members(['Son Gohan', 'Son Goku', 'Son Goten']); + expect(pagination).to.deep.equal(expectedPagination); + }); }); - it('should return only users matching "last name" if given in filter', async function () { - // given - const filter = { lastName: 'walk' }; - const page = { number: 1, size: 10 }; - const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; + context('when there are multiple users matching the same "last name" search pattern', function () { + beforeEach(async function () { + each( + [ + { firstName: 'Anakin', lastName: 'Skywalker' }, + { firstName: 'Luke', lastName: 'Skywalker' }, + { firstName: 'Leia', lastName: 'Skywalker' }, + { firstName: 'Han', lastName: 'Solo' }, + { firstName: 'Ben', lastName: 'Solo' }, + ], + (user) => { + databaseBuilder.factory.buildUser(user); + }, + ); - // when - const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); + await databaseBuilder.commit(); + }); - // then - expect(map(matchingUsers, 'firstName')).to.have.members(['Anakin', 'Luke', 'Leia']); - expect(pagination).to.deep.equal(expectedPagination); - }); - }); + it('returns only users matching "last name" if given in filter', async function () { + // given + const filter = { lastName: 'walk' }; + const page = { number: 1, size: 10 }; + const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; - context('when there are multiple users matching the same "email" search pattern', function () { - beforeEach(async function () { - each( - [ - { email: 'playpus@pix.fr' }, - { email: 'panda@pix.fr' }, - { email: 'otter@pix.fr' }, - { email: 'playpus@example.net' }, - { email: 'panda@example.net' }, - { email: 'PANDA@example.net' }, - { email: 'PANDA@PIX.be' }, - ], - (user) => { - databaseBuilder.factory.buildUser(user); - }, - ); + // when + const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ + filter, + page, + queryType: 'CONTAINS', + }); - await databaseBuilder.commit(); + // then + expect(map(matchingUsers, 'firstName')).to.have.members(['Anakin', 'Luke', 'Leia']); + expect(pagination).to.deep.equal(expectedPagination); + }); }); - it('should return only users matching "email" if given in filter even if it is in uppercase in database', async function () { - // given - const filter = { email: 'panda' }; - const page = { number: 1, size: 10 }; - const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 4 }; - - // when - const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); + context('when there are multiple users matching the same "email" search pattern', function () { + beforeEach(async function () { + each( + [ + { email: 'playpus@pix.fr' }, + { email: 'panda@pix.fr' }, + { email: 'otter@pix.fr' }, + { email: 'playpus@example.net' }, + { email: 'panda@example.net' }, + { email: 'PANDA@example.net' }, + { email: 'PANDA@PIX.be' }, + ], + (user) => { + databaseBuilder.factory.buildUser(user); + }, + ); - // then - expect(map(matchingUsers, 'email')).to.have.members([ - 'panda@pix.fr', - 'panda@example.net', - 'panda@example.net', - 'panda@pix.be', - ]); - expect(pagination).to.deep.equal(expectedPagination); - }); + await databaseBuilder.commit(); + }); - it('should return only users matching "email" if given in filter', async function () { - // given - const filter = { email: 'pix.fr' }; - const page = { number: 1, size: 10 }; - const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; + it('returns only users matching "email" if given in filter even if it is in uppercase in database', async function () { + // given + const filter = { email: 'panda' }; + const page = { number: 1, size: 10 }; + const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 4 }; - // when - const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); + // when + const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); - // then - expect(map(matchingUsers, 'email')).to.have.members(['playpus@pix.fr', 'panda@pix.fr', 'otter@pix.fr']); - expect(pagination).to.deep.equal(expectedPagination); - }); - }); + // then + expect(map(matchingUsers, 'email')).to.have.members([ + 'panda@pix.fr', + 'panda@example.net', + 'panda@example.net', + 'panda@pix.be', + ]); + expect(pagination).to.deep.equal(expectedPagination); + }); - context('when there are multiple users matching the same "username" search pattern', function () { - it('should return only users matching "username" if given in filter', async function () { - // given - each( - [ - { username: 'alex.ception1011' }, - { username: 'alex.terieur1011' }, - { username: 'ella.danloss0101' }, - { username: 'ella.bienhu1011' }, - { username: 'ella.bienhu2312' }, - ], - (user) => { - databaseBuilder.factory.buildUser(user); - }, - ); - await databaseBuilder.commit(); - const filter = { username: '1011' }; - const page = { number: 1, size: 10 }; + it('returns only users matching "email" if given in filter', async function () { + // given + const filter = { email: 'pix.fr' }; + const page = { number: 1, size: 10 }; + const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; - // when - const { models: matchingUsers } = await userRepository.findPaginatedFiltered({ filter, page }); + // when + const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); - // then - expect(map(matchingUsers, 'username')).to.have.members([ - 'alex.ception1011', - 'alex.terieur1011', - 'ella.bienhu1011', - ]); + // then + expect(map(matchingUsers, 'email')).to.have.members(['playpus@pix.fr', 'panda@pix.fr', 'otter@pix.fr']); + expect(pagination).to.deep.equal(expectedPagination); + }); }); - }); - context( - 'when there are multiple users matching the fields "first name", "last name" and "email" search pattern', - function () { - beforeEach(async function () { + context('when there are multiple users matching the same "username" search pattern', function () { + it('returns only users matching "username" if given in filter', async function () { + // given each( [ - // Matching users - { - firstName: 'fn_ok_1', - lastName: 'ln_ok_1', - email: 'email_ok_1@mail.com', - username: 'username_ok0210', - }, - { - firstName: 'fn_ok_2', - lastName: 'ln_ok_2', - email: 'email_ok_2@mail.com', - username: 'username_ok1214', - }, - { - firstName: 'fn_ok_3', - lastName: 'ln_ok_3', - email: 'email_ok_3@mail.com', - username: 'username_ok1010', - }, - - // Unmatching users - { - firstName: 'fn_ko_4', - lastName: 'ln_ok_4', - email: 'email_ok_4@mail.com', - username: 'username_ko1309', - }, - { - firstName: 'fn_ok_5', - lastName: 'ln_ko_5', - email: 'email_ok_5@mail.com', - username: 'username_ok1911', - }, - { - firstName: 'fn_ok_6', - lastName: 'ln_ok_6', - email: 'email_ko_6@mail.com', - username: 'username_ok2010', - }, + { username: 'alex.ception1011' }, + { username: 'alex.terieur1011' }, + { username: 'ella.danloss0101' }, + { username: 'ella.bienhu1011' }, + { username: 'ella.bienhu2312' }, ], (user) => { databaseBuilder.factory.buildUser(user); }, ); - await databaseBuilder.commit(); - }); - - it('should return only users matching "first name" AND "last name" AND "email" AND "username" if given in filter', async function () { - // given - const filter = { firstName: 'fn_ok', lastName: 'ln_ok', email: 'email_ok', username: 'username_ok' }; + const filter = { username: '1011' }; const page = { number: 1, size: 10 }; - const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; // when - const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ filter, page }); + const { models: matchingUsers } = await userRepository.findPaginatedFiltered({ filter, page }); // then - expect(map(matchingUsers, 'firstName')).to.have.members(['fn_ok_1', 'fn_ok_2', 'fn_ok_3']); - expect(map(matchingUsers, 'lastName')).to.have.members(['ln_ok_1', 'ln_ok_2', 'ln_ok_3']); - expect(map(matchingUsers, 'email')).to.have.members([ - 'email_ok_1@mail.com', - 'email_ok_2@mail.com', - 'email_ok_3@mail.com', - ]); expect(map(matchingUsers, 'username')).to.have.members([ - 'username_ok0210', - 'username_ok1214', - 'username_ok1010', + 'alex.ception1011', + 'alex.terieur1011', + 'ella.bienhu1011', ]); - expect(pagination).to.deep.equal(expectedPagination); }); - }, - ); + }); + + context( + 'when there are multiple users matching the fields "first name", "last name" and "email" search pattern', + function () { + beforeEach(async function () { + each( + [ + // Matching users + { + firstName: 'fn_ok_1', + lastName: 'ln_ok_1', + email: 'email_ok_1@mail.com', + username: 'username_ok0210', + }, + { + firstName: 'fn_ok_2', + lastName: 'ln_ok_2', + email: 'email_ok_2@mail.com', + username: 'username_ok1214', + }, + { + firstName: 'fn_ok_3', + lastName: 'ln_ok_3', + email: 'email_ok_3@mail.com', + username: 'username_ok1010', + }, + + // Unmatching users + { + firstName: 'fn_ko_4', + lastName: 'ln_ok_4', + email: 'email_ok_4@mail.com', + username: 'username_ko1309', + }, + { + firstName: 'fn_ok_5', + lastName: 'ln_ko_5', + email: 'email_ok_5@mail.com', + username: 'username_ok1911', + }, + { + firstName: 'fn_ok_6', + lastName: 'ln_ok_6', + email: 'email_ko_6@mail.com', + username: 'username_ok2010', + }, + ], + (user) => { + databaseBuilder.factory.buildUser(user); + }, + ); + + await databaseBuilder.commit(); + }); + + it('returns only users matching "first name" AND "last name" AND "email" AND "username" if given in filter', async function () { + // given + const filter = { firstName: 'fn_ok', lastName: 'ln_ok', email: 'email_ok', username: 'username_ok' }; + const page = { number: 1, size: 10 }; + const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 1, rowCount: 3 }; + + // when + const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ + filter, + page, + }); + + // then + expect(map(matchingUsers, 'firstName')).to.have.members(['fn_ok_1', 'fn_ok_2', 'fn_ok_3']); + expect(map(matchingUsers, 'lastName')).to.have.members(['ln_ok_1', 'ln_ok_2', 'ln_ok_3']); + expect(map(matchingUsers, 'email')).to.have.members([ + 'email_ok_1@mail.com', + 'email_ok_2@mail.com', + 'email_ok_3@mail.com', + ]); + expect(map(matchingUsers, 'username')).to.have.members([ + 'username_ok0210', + 'username_ok1214', + 'username_ok1010', + ]); + expect(pagination).to.deep.equal(expectedPagination); + }); + }, + ); + }); + context('when exact search is required', function () { + beforeEach(function () { + databaseBuilder.factory.buildUser({ firstName: 'Son Gohan' }); + databaseBuilder.factory.buildUser({ firstName: 'Son Goku' }); + databaseBuilder.factory.buildUser({ firstName: 'Son Goten' }); + databaseBuilder.factory.buildUser({ firstName: 'Vegeta' }); + databaseBuilder.factory.buildUser({ firstName: 'Piccolo' }); + return databaseBuilder.commit(); + }); + it('returns only exact "first name" matching if given in filter', async function () { + // given + const filter = { firstName: 'Son Gohan' }; + const page = { number: 1, size: 10 }; + + // when + const { models: matchingUsers } = await userRepository.findPaginatedFiltered({ + filter, + page, + queryType: 'EXACT_QUERY', + }); + + // then + expect(map(matchingUsers, 'firstName')).to.have.members(['Son Gohan']); + }); + it('returns no matching if matching is fuzzy', async function () { + // given + const filter = { firstName: 'Go' }; + const page = { number: 1, size: 10 }; + const expectedPagination = { page: page.number, pageSize: page.size, pageCount: 0, rowCount: 0 }; + + // when + const { models: matchingUsers, pagination } = await userRepository.findPaginatedFiltered({ + filter, + page, + queryType: 'EXACT_QUERY', + }); + + // then + expect(map(matchingUsers, 'firstName')).to.be.empty; + expect(pagination).to.deep.equal(expectedPagination); + }); + }); }); describe('#findAnotherUserByEmail', function () { - it('should return a list of a single user if email already used', async function () { + it('returns a list of a single user if email already used', async function () { // given const currentUser = databaseBuilder.factory.buildUser({ email: 'current.user@example.net', @@ -543,7 +593,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(foundUsers[0].email).to.equal(anotherUser.email); }); - it('should return a list of a single user if email case insensitive already used', async function () { + it('returns a list of a single user if email case insensitive already used', async function () { // given const currentUser = databaseBuilder.factory.buildUser({ email: 'current.user@example.net', @@ -562,7 +612,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(foundUsers[0].email).to.equal(anotherUser.email); }); - it('should return an empty list if email is not used', async function () { + it('returns an empty list if email is not used', async function () { // given const currentUser = databaseBuilder.factory.buildUser({ email: 'current.user@example.net', @@ -578,7 +628,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#findAnotherUserByUsername', function () { - it('should return a list of a single user if username already used', async function () { + it('returns a list of a single user if username already used', async function () { // given const currentUser = databaseBuilder.factory.buildUser({ username: 'current.user.name', @@ -597,7 +647,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(foundUsers[0].username).to.equal(anotherUser.username); }); - it('should return an empty list if username is not used', async function () { + it('returns an empty list if username is not used', async function () { // given const currentUser = databaseBuilder.factory.buildUser({ username: 'current.user.name', @@ -615,7 +665,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository describe('get user', function () { describe('#getByEmail', function () { - it('should handle a rejection, when user id is not found', async function () { + it('handles a rejection, when user id is not found', async function () { // given const emailThatDoesNotExist = '10093'; @@ -626,7 +676,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(result).to.be.instanceOf(UserNotFoundError); }); - it('should return the user with email case insensitive', async function () { + it('returns the user with email case insensitive', async function () { // given const mixCaseEmail = 'USER@example.net'; const userInDb = databaseBuilder.factory.buildUser({ email: mixCaseEmail }); @@ -653,7 +703,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository await databaseBuilder.commit(); }); - it('should return user informations for the given SAML ID', async function () { + it('returns user informations for the given SAML ID', async function () { // when const user = await userRepository.getBySamlId('some-saml-id'); @@ -662,7 +712,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(user.id).to.equal(userInDb.id); }); - it('should return undefined when no user was found with this SAML ID', async function () { + it('returns undefined when no user was found with this SAML ID', async function () { // given const badSamlId = 'bad-saml-id'; @@ -675,7 +725,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#get', function () { - it('should return the found user', async function () { + it('returns the found user', async function () { // given const userInDb = databaseBuilder.factory.buildUser(userToInsert); await databaseBuilder.commit(); @@ -694,7 +744,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(user.updatedAt.toISOString()).to.equal('2019-03-12T19:37:03.000Z'); }); - it('should return a UserNotFoundError if no user is found', async function () { + it('returns a UserNotFoundError if no user is found', async function () { // given const nonExistentUserId = 678; @@ -734,7 +784,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository await _insertUserWithOrganizationsAndCertificationCenterAccesses(); }); - it('should return user informations for the given email', async function () { + it('returns user informations for the given email', async function () { // given const expectedUser = new User(userInDB); @@ -752,7 +802,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(foundUser.locale).to.equal(expectedUser.locale); }); - it('should return user informations for the given email (case insensitive)', async function () { + it('returns user informations for the given email (case insensitive)', async function () { // given const expectedUser = new User(userInDB); const uppercaseEmailAlreadyInDb = userInDB.email.toUpperCase(); @@ -766,7 +816,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(foundUser.email).to.equal(expectedUser.email); }); - it('should return user informations for the given username (case insensitive)', async function () { + it('returns user informations for the given username (case insensitive)', async function () { // given const savedUser = databaseBuilder.factory.buildUser({ username: 'thomas123', @@ -789,7 +839,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(foundUser.cgu).to.equal(savedUser.cgu); }); - it('should return authenticationMethods associated to the user', async function () { + it('returns authenticationMethods associated to the user', async function () { // when const foundUser = await userRepository.getByUsernameOrEmailWithRolesAndPassword(userInDB.email); @@ -806,7 +856,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository ); }); - it('should only return actives certification center membership associated to the user', async function () { + it('only returns actives certification center membership associated to the user', async function () { // given const email = 'lilou@example.net'; const user = databaseBuilder.factory.buildUser({ email }); @@ -832,7 +882,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(certificationCenterMembership.id).to.equal(activeCertificationCenterMembership.id); }); - it('should return membership associated to the user', async function () { + it('returns membership associated to the user', async function () { // when const user = await userRepository.getByUsernameOrEmailWithRolesAndPassword(userInDB.email); @@ -867,7 +917,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); }); - it('should return certification center membership associated to the user', async function () { + it('returns certification center membership associated to the user', async function () { // given const user = databaseBuilder.factory.buildUser({ email: 'super@example.net' }); const certificationCenterMembershipId = databaseBuilder.factory.buildCertificationCenterMembership({ @@ -887,7 +937,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(firstCertificationCenterMembership.id).to.equal(certificationCenterMembershipId); }); - it('should reject with a UserNotFound error when no user was found with this email', async function () { + it('rejects with a UserNotFound error when no user was found with this email', async function () { // given const unusedEmail = 'kikou@pix.fr'; @@ -898,7 +948,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(result).to.be.instanceOf(UserNotFoundError); }); - it('should reject with a UserNotFound error when no user was found with this username', async function () { + it('rejects with a UserNotFound error when no user was found with this username', async function () { // given const unusedUsername = 'john.doe0909'; @@ -911,7 +961,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#getWithMemberships', function () { - it('should return user with his/her membership(s) for the given id', async function () { + it('returns user with his/her membership(s) for the given id', async function () { // given const user = databaseBuilder.factory.buildUser.withRawPassword({ firstName: 'Sarah', @@ -960,7 +1010,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context('when the membership associated to the user has been disabled', function () { - it('should not return the membership', async function () { + it('does not return the membership', async function () { // given const userId = databaseBuilder.factory.buildUser().id; const organizationId = databaseBuilder.factory.buildOrganization().id; @@ -980,7 +1030,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); }); - it('should reject with a UserNotFound error when no user was found with the given id', async function () { + it('rejects with a UserNotFound error when no user was found with the given id', async function () { // given const unknownUserId = 666; @@ -993,7 +1043,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#getWithCertificationCenterMemberships', function () { - it('should return user for the given id', async function () { + it('returns user for the given id', async function () { // given await _insertUserWithOrganizationsAndCertificationCenterAccesses(); const expectedUser = new User(userInDB); @@ -1011,7 +1061,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(user.cgu).to.equal(expectedUser.cgu); }); - it('should return actives certification center membership associated to the user', async function () { + it('returns actives certification center membership associated to the user', async function () { // when const userInDB = databaseBuilder.factory.buildUser(userToInsert); const certificationCenter = databaseBuilder.factory.buildCertificationCenter(); @@ -1044,7 +1094,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(associatedCertificationCenter.name).to.equal(certificationCenter.name); }); - it('should reject with a UserNotFound error when no user was found with the given id', async function () { + it('rejects with a UserNotFound error when no user was found with the given id', async function () { // given await _insertUserWithOrganizationsAndCertificationCenterAccesses(); const unknownUserId = 666; @@ -1058,7 +1108,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#getForObfuscation', function () { - it('should return a domain user with authentication methods only when found', async function () { + it('returns a domain user with authentication methods only when found', async function () { // given const userInDb = databaseBuilder.factory.buildUser(userToInsert); await databaseBuilder.commit(); @@ -1071,7 +1121,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(user.email).to.equal(userInDb.email); }); - it('should throw an error when user not found', async function () { + it('throws an error when user not found', async function () { // given const userIdThatDoesNotExist = '99999'; @@ -1084,7 +1134,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#getUserDetailsForAdmin', function () { - it('should return the found user', async function () { + it('returns the found user', async function () { // given const createdAt = new Date('2021-01-01'); const emailConfirmedAt = new Date('2022-01-01'); @@ -1133,7 +1183,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(userDetailsForAdmin.isPixAgent).to.be.false; }); - it('should return a UserNotFoundError if no user is found', async function () { + it('returns a UserNotFoundError if no user is found', async function () { // given const nonExistentUserId = 678; @@ -1145,7 +1195,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context('when user has organizationLearners', function () { - it('should return the user with his organizationLearner', async function () { + it('returns the user with his organizationLearner', async function () { // given const randomUser = databaseBuilder.factory.buildUser(); const userInDB = databaseBuilder.factory.buildUser(userToInsert); @@ -1237,7 +1287,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context("when user doesn't have organizationLearners", function () { - it('should return the user with an empty array', async function () { + it('returns the user with an empty array', async function () { // given const userInDB = databaseBuilder.factory.buildUser(userToInsert); await databaseBuilder.commit(); @@ -1280,7 +1330,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context('when user is anonymized', function () { - it('should return an empty array of authenticationMethods', async function () { + it('returns an empty array of authenticationMethods', async function () { // given const userInDB = databaseBuilder.factory.buildUser({ ...userToInsert, @@ -1300,7 +1350,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(hasBeenAnonymisedBy).to.equal(1); }); - it("should return the anonymisedBy's first and last names", async function () { + it("returns the anonymisedBy's first and last names", async function () { // given const adminWhoAnonymisedUser = databaseBuilder.factory.buildUser({ id: 1, @@ -1325,7 +1375,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); context('when user has login details', function () { - it('should return the user with his login details', async function () { + it('returns the user with his login details', async function () { // given const userInDB = databaseBuilder.factory.buildUser(userToInsert); databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ @@ -1388,7 +1438,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#updateEmail', function () { - it('should update the user email', async function () { + it('updates the user email', async function () { // given const newEmail = 'new_email@example.net'; const userInDb = databaseBuilder.factory.buildUser({ ...userToInsert, email: 'old_email@example.net' }); @@ -1436,7 +1486,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository await databaseBuilder.commit(); }); - it('should update the user email', async function () { + it('updates the user email', async function () { // given const newEmail = 'new_email@example.net'; const userAttributes = { @@ -1456,7 +1506,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(updatedUser.updatedAt).to.deep.equal(now); }); - it('should rollback the user email in case of error in transaction', async function () { + it('rolls back the user email in case of error in transaction', async function () { // given const newEmail = 'new_email@example.net'; const userAttributes = { @@ -1581,7 +1631,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#updateUsername', function () { - it('should update the username', async function () { + it('updates the username', async function () { // given const username = 'blue.carter0701'; const userId = databaseBuilder.factory.buildUser(userToInsert).id; @@ -1599,7 +1649,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(updatedUser.updatedAt).to.deep.equal(now); }); - it('should throw UserNotFoundError when user id not found', async function () { + it('throws UserNotFoundError when user id not found', async function () { // given const wrongUserId = 0; const username = 'blue.carter0701'; @@ -1616,7 +1666,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#updatePixOrgaTermsOfServiceAcceptedToTrue', function () { - it('should return the model with pixOrgaTermsOfServiceAccepted flag updated to true', async function () { + it('returns the model with pixOrgaTermsOfServiceAccepted flag updated to true', async function () { // given const userId = databaseBuilder.factory.buildUser({ pixOrgaTermsOfServiceAccepted: false }).id; await databaseBuilder.commit(); @@ -1629,7 +1679,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(result.pixOrgaTermsOfServiceAccepted).to.be.true; }); - it('should update the lastPixOrgaTermsOfServiceValidatedAt', async function () { + it('updates the lastPixOrgaTermsOfServiceValidatedAt', async function () { // given const user = databaseBuilder.factory.buildUser({ pixOrgaTermsOfServiceAccepted: true, @@ -1647,7 +1697,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#updatePixCertifTermsOfServiceAcceptedToTrue', function () { - it('should return the model with pixCertifTermsOfServiceAccepted flag updated to true', async function () { + it('returns the model with pixCertifTermsOfServiceAccepted flag updated to true', async function () { // given const userId = databaseBuilder.factory.buildUser({ pixCertifTermsOfServiceAccepted: false }).id; await databaseBuilder.commit(); @@ -1660,7 +1710,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(actualUser.pixCertifTermsOfServiceAccepted).to.be.true; }); - it('should update the pixCertifTermsOfServiceValidatedAt', async function () { + it('updates the pixCertifTermsOfServiceValidatedAt', async function () { // given const user = databaseBuilder.factory.buildUser({ pixCertifTermsOfServiceAccepted: true, @@ -1678,7 +1728,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#updateHasSeenAssessmentInstructionsToTrue', function () { - it('should return the model with hasSeenAssessmentInstructions flag updated to true', async function () { + it('returns the model with hasSeenAssessmentInstructions flag updated to true', async function () { // given const userId = databaseBuilder.factory.buildUser({ hasSeenAssessmentInstructions: false }).id; await databaseBuilder.commit(); @@ -1693,7 +1743,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#updateHasSeenNewDashboardInfoToTrue', function () { - it('should return the model with hasSeenNewDashboardInfo flag updated to true', async function () { + it('returns the model with hasSeenNewDashboardInfo flag updated to true', async function () { // given const userId = databaseBuilder.factory.buildUser({ hasSeenNewDashboardInfo: false }).id; await databaseBuilder.commit(); @@ -1718,7 +1768,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository return databaseBuilder.commit(); }); - it('should return the model with hasSeenFocusedChallengeTooltip flag updated to true', async function () { + it('returns the model with hasSeenFocusedChallengeTooltip flag updated to true', async function () { // when const challengeType = 'focused'; const actualUser = await userRepository.updateHasSeenChallengeTooltip({ userId, challengeType }); @@ -1728,7 +1778,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(actualUser.updatedAt).to.deep.equal(now); }); - it('should return the model with hasSeenOtherChallengesTooltip flag updated to true', async function () { + it('returns the model with hasSeenOtherChallengesTooltip flag updated to true', async function () { // when const challengeType = 'other'; const actualUser = await userRepository.updateHasSeenChallengeTooltip({ userId, challengeType }); @@ -1741,7 +1791,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository }); describe('#checkIfEmailIsAvailable', function () { - it('should return the email when the email is not registered', async function () { + it('returns the email when the email is not registered', async function () { // when const email = await userRepository.checkIfEmailIsAvailable('email@example.net'); @@ -1785,12 +1835,12 @@ describe('Integration | Identity Access Management | Infrastructure | Repository return databaseBuilder.commit(); }); - it('should return true when the user exists by email', async function () { + it('returns true when the user exists by email', async function () { const userExists = await userRepository.isUserExistingByEmail(email); expect(userExists).to.be.true; }); - it('should return true when the user exists by email (case insensitive)', async function () { + it('returns true when the user exists by email (case insensitive)', async function () { // given const uppercaseEmailAlreadyInDb = email.toUpperCase(); @@ -1801,14 +1851,14 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(userExists).to.be.true; }); - it('should throw an error when the user does not exist by email', async function () { + it('throws an error when the user does not exist by email', async function () { const err = await catchErr(userRepository.isUserExistingByEmail)('none'); expect(err).to.be.instanceOf(UserNotFoundError); }); }); describe('#acceptPixLastTermsOfService', function () { - it('should validate the last terms of service and save the date of acceptance ', async function () { + it('validates the last terms of service and save the date of acceptance ', async function () { // given const userId = databaseBuilder.factory.buildUser({ mustValidateTermsOfService: true, @@ -1830,7 +1880,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository describe('#isUsernameAvailable', function () { const username = 'abc.def0101'; - it("should return username when it doesn't exist", async function () { + it("returns username when it doesn't exist", async function () { // when const result = await userRepository.isUsernameAvailable(username); @@ -1838,7 +1888,7 @@ describe('Integration | Identity Access Management | Infrastructure | Repository expect(result).to.equal(username); }); - it('should throw AlreadyRegisteredUsernameError when username already exist', async function () { + it('throws AlreadyRegisteredUsernameError when username already exist', async function () { // given databaseBuilder.factory.buildUser({ username, diff --git a/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js b/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js index 282f156c350..fb71df5ebff 100644 --- a/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js +++ b/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js @@ -1,4 +1,5 @@ import { userAdminController } from '../../../../../src/identity-access-management/application/user/user.admin.controller.js'; +import { QUERY_TYPES } from '../../../../../src/identity-access-management/domain/constants/user-query.js'; import { User } from '../../../../../src/identity-access-management/domain/models/User.js'; import { usecases } from '../../../../../src/identity-access-management/domain/usecases/index.js'; import { DomainTransaction } from '../../../../../src/shared/domain/DomainTransaction.js'; @@ -49,7 +50,7 @@ describe('Unit | Identity Access Management | Application | Controller | Admin | it('allows to filter users by first name', async function () { // given - const query = { filter: { firstName: 'Alexia' }, page: {} }; + const query = { filter: { firstName: 'Alexia' }, page: {}, queryType: QUERY_TYPES.CONTAINS }; const request = { query }; usecases.findPaginatedFilteredUsers.resolves({ models: {}, pagination: {} }); @@ -62,7 +63,7 @@ describe('Unit | Identity Access Management | Application | Controller | Admin | it('allows to filter users by last name', async function () { // given - const query = { filter: { lastName: 'Granjean' }, page: {} }; + const query = { filter: { lastName: 'Granjean' }, page: {}, queryType: QUERY_TYPES.CONTAINS }; const request = { query }; usecases.findPaginatedFilteredUsers.resolves({ models: {}, pagination: {} }); @@ -75,7 +76,7 @@ describe('Unit | Identity Access Management | Application | Controller | Admin | it('allows to filter users by email', async function () { // given - const query = { filter: { email: 'alexiagranjean' }, page: {} }; + const query = { filter: { email: 'alexiagranjean' }, page: {}, queryType: QUERY_TYPES.CONTAINS }; const request = { query }; usecases.findPaginatedFilteredUsers.resolves({ models: {}, pagination: {} }); @@ -88,7 +89,11 @@ describe('Unit | Identity Access Management | Application | Controller | Admin | it('allows to paginate on a given page and page size', async function () { // given - const query = { filter: { email: 'alexiagranjean' }, page: { number: 2, size: 25 } }; + const query = { + filter: { email: 'alexiagranjean' }, + page: { number: 2, size: 25 }, + queryType: QUERY_TYPES.CONTAINS, + }; const request = { query }; usecases.findPaginatedFilteredUsers.resolves({ models: {}, pagination: {} }); diff --git a/api/tests/identity-access-management/unit/domain/usecases/find-paginated-filtered-users.usecase.test.js b/api/tests/identity-access-management/unit/domain/usecases/find-paginated-filtered-users.usecase.test.js index b8bd52cc1a7..0a803ca8dce 100644 --- a/api/tests/identity-access-management/unit/domain/usecases/find-paginated-filtered-users.usecase.test.js +++ b/api/tests/identity-access-management/unit/domain/usecases/find-paginated-filtered-users.usecase.test.js @@ -1,3 +1,4 @@ +import { QUERY_TYPES } from '../../../../../src/identity-access-management/domain/constants/user-query.js'; import { User } from '../../../../../src/identity-access-management/domain/models/User.js'; import { usecases } from '../../../../../src/identity-access-management/domain/usecases/index.js'; import { expect, sinon } from '../../../../test-helper.js'; @@ -7,18 +8,18 @@ describe('Unit | Identity Access Management | UseCase | find-paginated-filtered- // given const filter = { email: 'gigi@example.net' }; const page = { number: 1, size: 2 }; - + const queryType = QUERY_TYPES.CONTAINS; const resolvedPagination = { page: 1, pageSize: 2, itemsCount: 3, pagesCount: 2 }; const matchingUsers = [new User({ id: 1 }), new User({ id: 2 }), new User({ id: 3 })]; const userRepository = { findPaginatedFiltered: sinon.stub(), }; userRepository.findPaginatedFiltered - .withArgs({ filter, page }) + .withArgs({ filter, page, queryType }) .resolves({ models: matchingUsers, pagination: resolvedPagination }); // when - const response = await usecases.findPaginatedFilteredUsers({ filter, page, userRepository }); + const response = await usecases.findPaginatedFilteredUsers({ filter, page, queryType, userRepository }); // then expect(response.models).to.equal(matchingUsers);