From 2f24c9cbfe650eae6dfd260c8d482614bbd2f146 Mon Sep 17 00:00:00 2001 From: Jordan Wong <42422209+JorWo@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:27:10 -1000 Subject: [PATCH] Replace Axios with Fetch (#5) --- ui/api-handshake.js | 3 +- ui/jest.config.ts | 2 +- ui/package.json | 3 - ui/src/access/AuthorizationService.ts | 31 +- ui/src/access/Saml11Validator.ts | 10 +- ui/src/access/User.ts | 2 +- .../GroupingsApiResults.ts | 0 ui/src/services/FetchService.ts | 200 ++++ ui/src/services/GroupingsApiService.ts | 328 +++--- ui/tests/access/AuthenticationService.test.ts | 17 +- ui/tests/access/AuthorizationService.test.ts | 36 +- ui/tests/access/Saml11Validator.test.ts | 12 +- .../groupings/GroupingsApiService.test.ts | 925 ----------------- ui/tests/services/GroupingsApiService.test.ts | 930 ++++++++++++++++++ 14 files changed, 1289 insertions(+), 1210 deletions(-) rename ui/src/{services => groupings}/GroupingsApiResults.ts (100%) create mode 100644 ui/src/services/FetchService.ts delete mode 100644 ui/tests/groupings/GroupingsApiService.test.ts create mode 100644 ui/tests/services/GroupingsApiService.test.ts diff --git a/ui/api-handshake.js b/ui/api-handshake.js index 26b5e06c..a092ed10 100644 --- a/ui/api-handshake.js +++ b/ui/api-handshake.js @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -const axios = require('axios'); const env = require('@next/env'); env.loadEnvConfig(process.cwd()); @@ -23,7 +22,7 @@ or check the connection configuration and fix any possible problems.\n`; if (apiHandshakeEnabled) { - axios.get(`${apiBaseUrl}/`) + fetch(`${apiBaseUrl}/`) .catch(() => { console.log(errorMessage); process.exit(1); diff --git a/ui/jest.config.ts b/ui/jest.config.ts index b21da05b..fb6a0889 100644 --- a/ui/jest.config.ts +++ b/ui/jest.config.ts @@ -24,7 +24,7 @@ const config: Config = { moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], moduleDirectories: ['node_modules', ''], moduleNameMapper: { - "^@/(.*)$": "/src/$1" + '^@/(.*)$': '/src/$1' } }; diff --git a/ui/package.json b/ui/package.json index 5e28fb6f..11569645 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,8 +15,6 @@ "dependencies": { "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-slot": "^1.0.2", - "axios": "^1.6.7", - "axios-retry": "^4.0.0", "camaro": "^6.2.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -43,7 +41,6 @@ "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", "autoprefixer": "^10.0.1", - "axios-mock-adapter": "^1.22.0", "eslint": "^8", "eslint-config-next": "14.1.0", "eslint-plugin-testing-library": "^6.2.0", diff --git a/ui/src/access/AuthorizationService.ts b/ui/src/access/AuthorizationService.ts index 6e868f1a..c61a14ef 100644 --- a/ui/src/access/AuthorizationService.ts +++ b/ui/src/access/AuthorizationService.ts @@ -2,7 +2,6 @@ import User from './User'; import Role from './Role'; -import axios from 'axios'; const apiBaseUrl = process.env.NEXT_PUBLIC_API_2_1_BASE_URL as string; @@ -33,17 +32,10 @@ export const setRoles = async (user: User): Promise => { * * @returns True if the uhIdentifier is an owner of a grouping */ -const isOwner = async (uhIdentifier: string): Promise => { - try { - const { data } = await axios.get(`${apiBaseUrl}/owners`, { - headers: { 'current_user': uhIdentifier } - }); - return data; - } catch (error) { - console.error(error); - } - return false; -} +const isOwner = async (uhIdentifier: string): Promise => + await fetch(`${apiBaseUrl}/owners`, { headers: { 'current_user': uhIdentifier }}) + .then(res => res.json()) + .catch(() => false); /** * Calls UH Groupings API to check if the uhIdentifier is an admin. @@ -52,17 +44,10 @@ const isOwner = async (uhIdentifier: string): Promise => { * * @returns True if the uhIdentifier is an admin */ -const isAdmin = async (uhIdentifier: string): Promise => { - try { - const { data } = await axios.get(`${apiBaseUrl}/admins`, { - headers: { 'current_user': uhIdentifier } - }); - return data; - } catch (error) { - console.error(error); - } - return false; -} +const isAdmin = async (uhIdentifier: string): Promise => + await fetch(`${apiBaseUrl}/admins`, { headers: { 'current_user': uhIdentifier }}) + .then(res => res.json()) + .catch(() => false); /** * Checks if uhUuid is valid using Regex. diff --git a/ui/src/access/Saml11Validator.ts b/ui/src/access/Saml11Validator.ts index 1887ead0..1448bca0 100644 --- a/ui/src/access/Saml11Validator.ts +++ b/ui/src/access/Saml11Validator.ts @@ -2,7 +2,6 @@ import User, { AnonymousUser } from './User'; import uniqid from 'uniqid'; import { format } from 'util'; import { transform } from 'camaro'; -import axios from 'axios'; const baseUrl = process.env.NEXT_PUBLIC_BASE_URL as string; const casUrl = process.env.NEXT_PUBLIC_CAS_URL as string; @@ -30,10 +29,13 @@ export const validateTicket = async (ticket: string): Promise => { const samlRequestBody = format(samlRequestTemplate, `${uniqid()}.${currentDate}`, currentDate, ticket); try { - const response = await axios.post(samlValidateUrl, samlRequestBody, { - headers: { 'Content-Type': 'text/xml' } + const response = await fetch(samlValidateUrl, { + method: 'POST', + headers: { 'Content-Type': 'text/xml' }, + body: samlRequestBody }); - const casUser = await transform(response.data, samlResponseTemplate); + const data = await response.text(); + const casUser = await transform(data, samlResponseTemplate); return { ...casUser, diff --git a/ui/src/access/User.ts b/ui/src/access/User.ts index f0fcf25b..e73b3937 100644 --- a/ui/src/access/User.ts +++ b/ui/src/access/User.ts @@ -1,4 +1,4 @@ -import { MemberResult } from '@/services/GroupingsApiResults'; +import { MemberResult } from '@/groupings/GroupingsApiResults'; import Role from './Role'; type User = { diff --git a/ui/src/services/GroupingsApiResults.ts b/ui/src/groupings/GroupingsApiResults.ts similarity index 100% rename from ui/src/services/GroupingsApiResults.ts rename to ui/src/groupings/GroupingsApiResults.ts diff --git a/ui/src/services/FetchService.ts b/ui/src/services/FetchService.ts new file mode 100644 index 00000000..37653c77 --- /dev/null +++ b/ui/src/services/FetchService.ts @@ -0,0 +1,200 @@ +'use server'; + +import { ApiError } from '../groupings/GroupingsApiResults'; +import { getCurrentUser } from '@/access/AuthenticationService'; + +const maxRetries = 3; +const baseUrl = process.env.NEXT_PUBLIC_API_2_1_BASE_URL as string; + +/** + * Sleep/wait for the specified milliseconds. + * + * @param ms - the time in milliseconds to wait for + * + * @returns setTimeout promise + */ +const delay = async (ms = 5000) => new Promise((res) => setTimeout(res, ms)); + +/** + * Polls to getAsyncJobResult API endpoint until the async job has completed with a result. + * + * @param jobId - the jobId returned from the response of an async endpoint + * + * @returns The promise of type T or ApiError type + */ +const poll = async (jobId: number): Promise => { + const currentUser = await getCurrentUser(); + return fetch(`${baseUrl}/jobs/${jobId}`, { headers: { 'current_user': currentUser.uid } }) + .then(res => res.json()) + .then(async res => { + if (res.status === 'COMPLETED') { + return res.result; + } + await delay(); + return poll(jobId); + }) + .catch(err => err); +}; + +/** + * Perform a GET request to the specified URL. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * + * @returns The promise of type T or ApiError type + */ +export const getRequest = async ( + endpoint: string, + currentUserKey: string = '' +): Promise => + await fetch(endpoint, { headers: { 'current_user': currentUserKey } }) + .then(res => res.json()) + .catch(err => err); + +/** + * Perform a POST request to the specified URL. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const postRequest = async ( + endpoint: string, + currentUserKey: string, + body?: string | string[], +): Promise => + await fetch(endpoint, { + method: 'POST', + headers: { 'current_user': currentUserKey }, + body: JSON.stringify(body)}) + .then(res => res.json()) + .catch(err => err); + +/** + * Perform a POST request to the specified URL asynchronously using polling. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const postRequestAsync = async ( + endpoint: string, + currentUserKey: string, + body: string | string[] +): Promise => + await fetch(endpoint, { method: 'POST', headers: { 'current_user': currentUserKey }, body: JSON.stringify(body) }) + .then(res => res.json()) + .then(res => poll(res)) + .catch(err => err); + +/** + * Perform a POST request to the specified URL with retries on error with incremental backoff. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const postRequestRetry = async ( + endpoint: string, + currentUserKey: string, + body: string | string[], + retries: number = maxRetries +): Promise => + await fetch(endpoint, { method: 'POST', headers: { 'current_user': currentUserKey }, body: JSON.stringify(body) }) + .then(async res => { + if (res.status === 500 && retries > 0) { + await delay(2000 * Math.log(maxRetries / retries)); + return postRequestRetry(endpoint, currentUserKey, body, retries - 1); + } + return res.json(); + }) + .catch(err => err); + + +/** + * Perform a PUT request to the specified URL. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const putRequest = async ( + endpoint: string, + currentUserKey: string, + body?: string | string[] +): Promise => + await fetch(endpoint, { + method: 'PUT', + headers: { 'current_user': currentUserKey }, + body: JSON.stringify(body) }) + .then(res => res.json()) + .catch(err => err); + +/** + * Perform a PUT request to the specified URL asynchronously using polling. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const putRequestAsync = async ( + endpoint: string, + currentUserKey: string, + body: string | string[] +): Promise => + await fetch(endpoint, { method: 'PUT', headers: { 'current_user': currentUserKey }, body: JSON.stringify(body) }) + .then(res => res.json()) + .then(res => poll(res)) + .catch(err => err); + +/** + * Perform a DELETE request to the specified URL. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const deleteRequest = async ( + endpoint: string, + currentUserKey: string, + body?: string | string[] +): Promise => + await fetch(endpoint, { method: 'DELETE', headers: { 'current_user': currentUserKey }, body: JSON.stringify(body) }) + .then(res => res.json()) + .then(res => (res)) + .catch(err => err); + +/** + * Perform a DELETE request to the specified URL asynchronously using polling. + * + * @param endpoint - the URL to perform the request on + * @param currentUserKey - the uhIdentifier of the current user + * @param body - the request body to perform the request with + * + * @returns The promise of type T or ApiError type + */ +export const deleteRequestAsync = async ( + endpoint: string, + currentUserKey: string, + body?: string | string[] +): Promise => + await fetch(endpoint, { + method: 'DELETE', + headers: { 'current_user': currentUserKey }, + body: JSON.stringify(body) }) + .then(res => res.json()) + .then(res => poll(res)) + .catch(err => err); diff --git a/ui/src/services/GroupingsApiService.ts b/ui/src/services/GroupingsApiService.ts index bc76fb45..9f6944ac 100644 --- a/ui/src/services/GroupingsApiService.ts +++ b/ui/src/services/GroupingsApiService.ts @@ -2,7 +2,7 @@ import { getCurrentUser } from '@/access/AuthenticationService'; import { - Announcements, + Announcements, ApiError, GroupingAddResult, GroupingAddResults, @@ -19,50 +19,32 @@ import { GroupingUpdateDescriptionResult, MemberAttributeResults, MembershipResults -} from './GroupingsApiResults'; -import axios from 'axios'; -import axiosRetry from 'axios-retry'; +} from '../groupings/GroupingsApiResults'; +import { + deleteRequest, + deleteRequestAsync, + getRequest, + postRequest, + postRequestAsync, + postRequestRetry, + putRequest, + putRequestAsync +} from './FetchService'; const baseUrl = process.env.NEXT_PUBLIC_API_2_1_BASE_URL as string; -const maxRetries = 3; // TODO: // The setOptIn, setOptOut, setSyncDest service functions will be up to the person who works on // implementing Opt Attributes and Sync Desinations into our React UI. -/** - * Polls to getAsyncJobResult API endpoint until the async job has completed with a result. - * - * @param jobId - the jobId returned from the response of an async endpoint - * - * @returns The result T - */ -const poll = async (jobId: number): Promise => { - const delay = async (ms = 5000) => new Promise((res) => setTimeout(res, ms)); - - const currentUser = await getCurrentUser(); - return axios.get(`${baseUrl}/jobs/${jobId}`, { headers: { 'current_user': currentUser.uid } }) - .then(async (response) => { - if (response.data.status === 'COMPLETED') { - return response.data.result; - } else { - await delay(); - return poll(jobId); - } - }) - .catch(error => error.response.data); -} - /** * Get a list of announcements to display on the home page. * - * @returns The announcements + * @returns The promise of announcements or ApiError type */ -export const getAnnouncements = (): Promise => { +export const getAnnouncements = (): Promise => { const endpoint = `${baseUrl}/announcements`; - return axios.get(endpoint) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint); } /** @@ -74,7 +56,7 @@ export const getAnnouncements = (): Promise => { * @param sortString - String to sort by column name * @param isAscending - On true the data returns in ascending order * - * @returns The members of an owned grouping + * @returns The promise of members of an owned grouping or ApiError type */ export const ownedGrouping = async ( groupPaths: string[], @@ -82,7 +64,7 @@ export const ownedGrouping = async ( size: number, sortString: string, isAscending: boolean -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const params = new URLSearchParams({ page: page.toString(), @@ -91,11 +73,7 @@ export const ownedGrouping = async ( isAscending: isAscending.toString() }); const endpoint = `${baseUrl}/groupings/group?${params.toString()}`; - - axiosRetry(axios, { retries: maxRetries, retryDelay: axiosRetry.exponentialDelay }); - return axios.post(endpoint, groupPaths, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return postRequestRetry(endpoint, currentUser.uid, groupPaths); } /** @@ -103,16 +81,14 @@ export const ownedGrouping = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping description + * @returns The promise of the grouping description or ApiError type */ export const groupingDescription = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/description`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** @@ -120,16 +96,14 @@ export const groupingDescription = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping sync destinations + * @returns The promise of the grouping sync destinations or ApiError type */ export const groupingSyncDest = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/groupings-sync-destinations`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** @@ -137,16 +111,14 @@ export const groupingSyncDest = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping opt attributes + * @returns The promise of the grouping opt attributes or ApiError type */ export const groupingOptAttributes = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/opt-attributes`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid) } /** @@ -155,43 +127,37 @@ export const groupingOptAttributes = async ( * @param description - The new description * @param groupingPath - The path of the grouping * - * @returns The result of updating the description + * @returns The promise of the result of updating the description or ApiError type */ export const updateDescription = async ( description: string, groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/description`; - return axios.post(endpoint, description, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return postRequest(endpoint, currentUser.uid, description); } /** * Get a list of admins. * - * @returns The grouping admins + * @returns The promise of the grouping admins or ApiError type */ -export const groupingAdmins = async (): Promise => { +export const groupingAdmins = async (): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/grouping-admins`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** * Get a list of all grouping paths. * - * @returns All the grouping paths + * @returns The promise of all the grouping paths or ApiError type */ -export const getAllGroupings = async (): Promise => { +export const getAllGroupings = async (): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/all-groupings`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** @@ -200,17 +166,15 @@ export const getAllGroupings = async (): Promise => { * @param uhIdentifiers - The list of uhIdentifiers to add * @param groupingPath - The path of the grouping * - * @returns The grouping move member result + * @returns The promise of the grouping move member result or ApiError type */ export const addIncludeMembers = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/include-members`; - return axios.put(endpoint, uhIdentifiers, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return putRequest(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -219,17 +183,15 @@ export const addIncludeMembers = async ( * @param uhIdentifiers - The list of uhIdentifiers to add to include * @param groupingPath - The path of the grouping * - * @returns The grouping move member result + * @returns The promise of the grouping move member result or ApiError type */ export const addIncludeMembersAsync = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/include-members/async`; - return axios.put(endpoint, uhIdentifiers, { headers: { 'current_user': currentUser.uid } }) - .then(response => poll(response.data)) - .catch(error => error.response.data); + return putRequestAsync(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -238,17 +200,15 @@ export const addIncludeMembersAsync = async ( * @param uhIdentifiers - The list of uhIdentifiers to add to exclude * @param groupingPath - The path of the grouping * - * @returns The grouping move member result + * @returns The promise of the grouping move member result or ApiError type */ export const addExcludeMembers = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/exclude-members`; - return axios.put(endpoint, uhIdentifiers, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return putRequest(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -257,17 +217,15 @@ export const addExcludeMembers = async ( * @param uhIdentifiers - The list of uhIdentifiers to add to exclude * @param groupingPath - The path of the grouping * - * @returns The grouping move member result + * @returns The promise of the grouping move member result or ApiError type */ export const addExcludeMembersAsync = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/exclude-members/async`; - return axios.put(endpoint, uhIdentifiers, { headers: { 'current_user': currentUser.uid } }) - .then(response => poll(response.data)) - .catch(error => error.response.data); + return putRequestAsync(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -276,17 +234,15 @@ export const addExcludeMembersAsync = async ( * @param uhIdentifiers - The uhIdentifiers to add to owners * @param groupingPath - The path of the grouping * - * @returns The grouping add results + * @returns The promise of the grouping add results or ApiError type */ export const addOwners = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifiers}`; - return axios.post(endpoint, undefined, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return postRequest(endpoint, currentUser.uid); } /** @@ -294,16 +250,14 @@ export const addOwners = async ( * * @param uhIdentifier - The uhIdentifier to add to admins * - * @returns The grouping add results + * @returns The promise of the grouping add results or ApiError type */ export const addAdmin = async ( uhIdentifier: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/admins/${uhIdentifier}`; - return axios.post(endpoint, undefined, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return postRequest(endpoint, currentUser.uid); } /** @@ -312,17 +266,15 @@ export const addAdmin = async ( * @param uhIdentifier - The uhIdentifier to remove from groups * @param groupPaths - The paths to the groups to remove from * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const removeFromGroups = async ( uhIdentifier: string, groupPaths: string[], -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/admins/${groupPaths}/${uhIdentifier}`; - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid); } /** @@ -331,17 +283,15 @@ export const removeFromGroups = async ( * @param uhIdentifiers - The uhIdentifiers to remove from the include * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const removeIncludeMembers = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/include-members`; - return axios.delete(endpoint, { data: uhIdentifiers, headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -350,17 +300,15 @@ export const removeIncludeMembers = async ( * @param uhIdentifiers - The uhIdentifiers to remove from the exclude * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const removeExcludeMembers = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/exclude-members`; - return axios.delete(endpoint, { data: uhIdentifiers, headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -369,17 +317,15 @@ export const removeExcludeMembers = async ( * @param uhIdentifiers - The uhIdentifiers to remove from owners * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const removeOwners = async ( uhIdentifiers: string[], groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifiers}`; - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid); } /** @@ -387,16 +333,14 @@ export const removeOwners = async ( * * @param uhIdentifier - The uhIdentifier to remove from admins * - * @returns The grouping remove result + * @returns The promise of the grouping remove result or ApiError type */ export const removeAdmin = async ( uhIdentifier: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/admins/${uhIdentifier}`; - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid); } /** @@ -404,16 +348,14 @@ export const removeAdmin = async ( * * @param uhIdentifiers - The uhIdentifiers to get the attributes of * - * @returns The member attribute results + * @returns The promise of the member attribute results or ApiError type */ export const memberAttributeResults = async ( uhIdentifiers: string[] -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/members`; - return axios.post(endpoint, uhIdentifiers, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return postRequest(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -422,16 +364,14 @@ export const memberAttributeResults = async ( * * @param uhIdentifiers - The uhIdentifiers to get the attributes of * - * @returns The member attribute results - */ + * @returns The promise of the member attribute results or ApiError type + */ export const memberAttributeResultsAsync = async ( uhIdentifiers: string[] -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/members/async`; - return axios.post(endpoint, uhIdentifiers, { headers: { 'current_user': currentUser.uid } }) - .then(response => poll(response.data)) - .catch(error => error.response.data); + return postRequestAsync(endpoint, currentUser.uid, uhIdentifiers); } /** @@ -439,16 +379,14 @@ export const memberAttributeResultsAsync = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping move member result + * @returns The promise of the grouping move member result or ApiError type */ export const optIn = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/include-members/${currentUser.uid}/self`; - return axios.put(endpoint, undefined, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return putRequest(endpoint, currentUser.uid); } /** @@ -456,29 +394,25 @@ export const optIn = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping move member result + * @returns The promise of the grouping move member result or ApiError type */ export const optOut = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/exclude-members/${currentUser.uid}/self`; - return axios.put(endpoint, undefined, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return putRequest(endpoint, currentUser.uid); } /** * Get a list of memberships that the current user is associated with. * - * @returns The membership results + * @returns The promise of the membership results or ApiError type */ -export const membershipResults = async (): Promise => { +export const membershipResults = async (): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/members/${currentUser.uid}/memberships`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** @@ -486,42 +420,36 @@ export const membershipResults = async (): Promise * * @param uhIdentifier - The uhIdentifier to search in Manage Person * - * @returns The membership results + * @returns The promise of the membership results or ApiError type */ export const managePersonResults = async ( uhIdentifier: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/members/${uhIdentifier}/groupings`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** * Get the number of memberships the current user has * - * @returns The number of memberships + * @returns The promise of the number of memberships or ApiError type */ -export const getNumberOfMemberships = async (): Promise => { +export const getNumberOfMemberships = async (): Promise => { const currentUser = await getCurrentUser(); - const endpoint = `${baseUrl}/members/memberships/count`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + const endpoint = `${baseUrl}/members/${currentUser.uid}/memberships/count`; + return getRequest(endpoint, currentUser.uid); } /** * Get a list of grouping paths that the current user can opt into. * - * @returns The grouping paths - */ -export const optInGroupingPaths = async (): Promise => { + * @returns The promise of the grouping paths or ApiError type + */ +export const optInGroupingPaths = async (): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/members/${currentUser.uid}/opt-in-groups`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** @@ -529,16 +457,14 @@ export const optInGroupingPaths = async (): Promise => * * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const resetIncludeGroup = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/include` - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid); } /** @@ -546,16 +472,14 @@ export const resetIncludeGroup = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const resetIncludeGroupAsync = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/include/async` - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => poll(response.data)) - .catch(error => error.response.data); + return deleteRequestAsync(endpoint, currentUser.uid); } /** @@ -563,16 +487,14 @@ export const resetIncludeGroupAsync = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const resetExcludeGroup = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/exclude` - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return deleteRequest(endpoint, currentUser.uid); } /** @@ -580,16 +502,14 @@ export const resetExcludeGroup = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping remove results + * @returns The promise of the grouping remove results or ApiError type */ export const resetExcludeGroupAsync = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/exclude/async` - return axios.delete(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => poll(response.data)) - .catch(error => error.response.data); + return deleteRequestAsync(endpoint, currentUser.uid); } /** @@ -597,42 +517,36 @@ export const resetExcludeGroupAsync = async ( * * @param groupingPath - The path of the grouping * - * @returns The grouping group members + * @returns The promise of the grouping group members or ApiError type */ export const groupingOwners = async ( groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/grouping/${groupingPath}/owners`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** * Get the groupings the current user owns. * - * @returns The grouping paths + * @returns The promise of the grouping paths or ApiError type */ -export const ownerGroupings = async (): Promise => { +export const ownerGroupings = async (): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/owners/${currentUser.uid}/groupings`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** * Get the number of groupings the current user owns. * - * @returns The number of groupings + * @returns The promise of the number of groupings or ApiError type */ -export const getNumberOfGroupings = async (): Promise => { +export const getNumberOfGroupings = async (): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/owners/${currentUser.uid}/groupings/count`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } /** @@ -641,15 +555,13 @@ export const getNumberOfGroupings = async (): Promise => { * @param uhIdentifier - The uhIdentifier to check * @param groupingPath - The path of the grouping * - * @returns True if uhIdentifier is the sole owner of a grouping + * @returns The promise of true if uhIdentifier is the sole owner of a grouping or ApiError type */ export const isSoleOwner = async ( uhIdentifier: string, groupingPath: string -): Promise => { +): Promise => { const currentUser = await getCurrentUser(); const endpoint = `${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifier}`; - return axios.get(endpoint, { headers: { 'current_user': currentUser.uid } }) - .then(response => response.data) - .catch(error => error.response.data); + return getRequest(endpoint, currentUser.uid); } diff --git a/ui/tests/access/AuthenticationService.test.ts b/ui/tests/access/AuthenticationService.test.ts index 2fc9b537..fd936217 100644 --- a/ui/tests/access/AuthenticationService.test.ts +++ b/ui/tests/access/AuthenticationService.test.ts @@ -3,8 +3,6 @@ import { createMockSession } from '../setupJest'; import User, { AnonymousUser } from '@/access/User'; import { redirect } from 'next/navigation'; import IronSession from 'iron-session'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; const baseUrl = process.env.NEXT_PUBLIC_BASE_URL as string; const casUrl = process.env.NEXT_PUBLIC_CAS_URL as string; @@ -51,29 +49,22 @@ describe('AuthenticationService', () => { describe('handleLogin', () => { - let axiosMock: MockAdapter; - - beforeEach(() => { - axiosMock = new MockAdapter(axios); - }); - - it('should return when ticket to validate is invalid', async () =>{ + it('should return when ticket to validate is invalid', async () => { + fetchMock.mockAbort(); const getIronSessionSpy = jest.spyOn(IronSession, 'getIronSession'); - axiosMock.onPost().reply(500); await handleLogin('ticket'); expect(getIronSessionSpy).not.toHaveBeenCalled(); }); it('should save the user to the session', async () => { + fetchMock.mockResponse(xmlSoapResponse); + const session = createMockSession(testUser); jest.spyOn(IronSession, 'getIronSession').mockResolvedValue(session); const sessionSaveSpy = jest.spyOn(session, 'save'); - axiosMock - .onPost().reply(200, xmlSoapResponse) - .onGet().reply(200, false); await handleLogin('ticket'); expect(sessionSaveSpy).toHaveBeenCalled(); diff --git a/ui/tests/access/AuthorizationService.test.ts b/ui/tests/access/AuthorizationService.test.ts index d762e355..e208770f 100644 --- a/ui/tests/access/AuthorizationService.test.ts +++ b/ui/tests/access/AuthorizationService.test.ts @@ -1,8 +1,6 @@ import { setRoles } from '@/access/AuthorizationService'; import Role from '@/access/Role'; import User, { AnonymousUser } from '@/access/User'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; const testUser: User = JSON.parse(process.env.TEST_USER_A as string); const apiBaseUrl = process.env.NEXT_PUBLIC_API_2_1_BASE_URL as string; @@ -11,20 +9,15 @@ describe('AuthorizationService', () => { describe('setRoles', () => { - let axiosMock: MockAdapter; - - beforeEach(() => { - axiosMock = new MockAdapter(axios); - }); - afterEach(() => { AnonymousUser.roles = []; testUser.roles = []; }); it('should set the ANONYMOUS role', async () => { - axiosMock.onGet(`${apiBaseUrl}/owners`).reply(200, false); - axiosMock.onGet(`${apiBaseUrl}/admins`).reply(200, false); + fetchMock + .mockResponseOnce(JSON.stringify(false)) // isOwner + .mockResponseOnce(JSON.stringify(false)); // isAdmin await setRoles(AnonymousUser); expect(AnonymousUser.roles.includes(Role.ADMIN)).toBeFalsy(); @@ -34,8 +27,9 @@ describe('AuthorizationService', () => { }); it('should set the UH role', async () => { - axiosMock.onGet(`${apiBaseUrl}/owners`).reply(200, false); - axiosMock.onGet(`${apiBaseUrl}/admins`).reply(200, false); + fetchMock + .mockResponseOnce(JSON.stringify(false)) // isOwner + .mockResponseOnce(JSON.stringify(false)); // isAdmin await setRoles(testUser); expect(testUser.roles.includes(Role.ADMIN)).toBeFalsy(); @@ -45,8 +39,9 @@ describe('AuthorizationService', () => { }); it('should set the UH and ADMIN roles', async () => { - axiosMock.onGet(`${apiBaseUrl}/owners`).reply(200, false); - axiosMock.onGet(`${apiBaseUrl}/admins`).reply(200, true); + fetchMock + .mockResponseOnce(JSON.stringify(false)) // isOwner + .mockResponseOnce(JSON.stringify(true)); // isAdmin await setRoles(testUser); expect(testUser.roles.includes(Role.ADMIN)).toBeTruthy(); @@ -56,8 +51,9 @@ describe('AuthorizationService', () => { }); it('should set the UH and OWNER roles', async () => { - axiosMock.onGet(`${apiBaseUrl}/owners`).reply(200, true); - axiosMock.onGet(`${apiBaseUrl}/admins`).reply(200, false); + fetchMock + .mockResponseOnce(JSON.stringify(true)) // isOwner + .mockResponseOnce(JSON.stringify(false)); // isAdmin await setRoles(testUser); expect(testUser.roles.includes(Role.ADMIN)).toBeFalsy(); @@ -67,8 +63,9 @@ describe('AuthorizationService', () => { }); it('should set the UH, ADMIN, and OWNER roles', async () => { - axiosMock.onGet(`${apiBaseUrl}/owners`).reply(200, true); - axiosMock.onGet(`${apiBaseUrl}/admins`).reply(200, true); + fetchMock + .mockResponseOnce(JSON.stringify(true)) // isOwner + .mockResponseOnce(JSON.stringify(true)); // isAdmin await setRoles(testUser); expect(testUser.roles.includes(Role.ADMIN)).toBeTruthy(); @@ -78,8 +75,7 @@ describe('AuthorizationService', () => { }); it('should catch Groupings API errors', async () => { - axiosMock.onGet(`${apiBaseUrl}/owners`).reply(500); - axiosMock.onGet(`${apiBaseUrl}/admins`).reply(500); + fetchMock.mockAbort(); await setRoles(testUser); expect(testUser.roles.includes(Role.ADMIN)).toBeFalsy(); diff --git a/ui/tests/access/Saml11Validator.test.ts b/ui/tests/access/Saml11Validator.test.ts index c81d0a33..730787af 100644 --- a/ui/tests/access/Saml11Validator.test.ts +++ b/ui/tests/access/Saml11Validator.test.ts @@ -1,7 +1,5 @@ import { validateTicket } from '@/access/Saml11Validator'; import User, { AnonymousUser } from '@/access/User'; -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; const testUser: User = JSON.parse(process.env.TEST_USER_A as string); const xmlSoapResponse = process.env.XML_SOAP_RESPONSE as string; @@ -9,20 +7,14 @@ const xmlSoapResponse = process.env.XML_SOAP_RESPONSE as string; describe('Saml11Validator', () => { describe('validateTicket', () => { - - let axiosMock: MockAdapter; - - beforeEach(() => { - axiosMock = new MockAdapter(axios); - }); it('should return the user on success', async () => { - axiosMock.onPost().reply(200, xmlSoapResponse); + fetchMock.mockResponse(xmlSoapResponse); expect(await validateTicket('ticket')).toEqual(testUser); }); it('should return an AnonymousUser on error', async () => { - axiosMock.onPost().reply(500); + fetchMock.mockAbort(); expect(await validateTicket('ticket')).toEqual(AnonymousUser); }); diff --git a/ui/tests/groupings/GroupingsApiService.test.ts b/ui/tests/groupings/GroupingsApiService.test.ts deleted file mode 100644 index 7e267f8c..00000000 --- a/ui/tests/groupings/GroupingsApiService.test.ts +++ /dev/null @@ -1,925 +0,0 @@ -import { - addAdmin, - addExcludeMembers, - addExcludeMembersAsync, - addIncludeMembers, - addIncludeMembersAsync, - addOwners, - getAllGroupings, - getAnnouncements, - getNumberOfGroupings, - getNumberOfMemberships, - groupingAdmins, - groupingDescription, - groupingOptAttributes, - groupingOwners, - groupingSyncDest, - isSoleOwner, - managePersonResults, - memberAttributeResults, - memberAttributeResultsAsync, - membershipResults, - optIn, - optInGroupingPaths, - optOut, - ownedGrouping, - ownerGroupings, - removeAdmin, - removeExcludeMembers, - removeFromGroups, - removeIncludeMembers, - removeOwners, - resetExcludeGroup, - resetExcludeGroupAsync, - resetIncludeGroup, - resetIncludeGroupAsync, - updateDescription -} from '@/services/GroupingsApiService'; -import axios from 'axios'; -import * as AuthenticationService from '@/access/AuthenticationService'; -import User from '@/access/User'; -import MockAdapter from 'axios-mock-adapter'; - -const baseUrl = process.env.NEXT_PUBLIC_API_2_1_BASE_URL as string; -const testUser: User = JSON.parse(process.env.TEST_USER_A as string); - -jest.mock('@/access/AuthenticationService'); - -describe('GroupingsService', () => { - - const currentUser = testUser; - const headers = { headers: { 'current_user': currentUser.uid } }; - - const uhIdentifier = 'testiwta'; - const uhIdentifiers = ['testiwta', 'testiwtb']; - const groupingPath = 'tmp:testiwta:testiwta-aux'; - const groupPaths = [ - `${groupingPath}:include`, - `${groupingPath}:include`, - `${groupingPath}:exclude`, - `${groupingPath}:owners` - ]; - - const mockResponse = { - data: { - resultCode: 'SUCCESS' - } - } - const mockAsyncCompletedResponse = { - data: { - status: 'COMPLETED', - result: { - resultCode: 'SUCCESS' - } - } - } - const mockAsyncInProgressResponse = { - data: { - status: 'IN_PROGRESS' - } - } - const mockError = { - response: { - data: { - resultCode: 'FAILURE' - } - } - } - - beforeAll(() => { - jest.spyOn(AuthenticationService, 'getCurrentUser').mockResolvedValue(testUser); - }) - - beforeEach(() => { - new MockAdapter(axios); - }); - - describe('getAnnouncements', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await getAnnouncements(); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/announcements`); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await getAnnouncements(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await getAnnouncements(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('ownedGrouping', () => { - const page = 1; - const size = 700; - const sortString = 'name'; - const isAscending = true; - - it('should make a POST request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'post'); - await ownedGrouping(groupPaths, page, size, sortString, isAscending); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/group?` - + `page=${page}&size=${size}&sortString=${sortString}&isAscending=${isAscending}`, - groupPaths, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'post').mockResolvedValue(mockResponse); - const res = await ownedGrouping(groupPaths, page, size, sortString, isAscending); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'post').mockRejectedValue(mockError); - const res = await ownedGrouping(groupPaths, page, size, sortString, isAscending); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('groupingDescription', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await groupingDescription(groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/description`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await groupingDescription(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await groupingDescription(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('groupingSyncDest', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await groupingSyncDest(groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/groupings-sync-destinations`, - headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await groupingSyncDest(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await groupingSyncDest(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('groupingOptAttributes', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await groupingOptAttributes(groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/opt-attributes`, - headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await groupingOptAttributes(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await groupingOptAttributes(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('updateDescription', () => { - const description = 'description'; - - it('should make a POST request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'post'); - await updateDescription(description, groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/description`, description, - headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'post').mockResolvedValue(mockResponse); - const res = await updateDescription(description, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'post').mockRejectedValue(mockError); - const res = await updateDescription(description, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('groupingAdmins', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await groupingAdmins(); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/grouping-admins`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await groupingAdmins(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await groupingAdmins(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('getAllGroupings', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await getAllGroupings(); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/all-groupings`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await getAllGroupings(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await getAllGroupings(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('AddIncludeMembers', () => { - it('should make a PUT request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'put'); - await addIncludeMembers(uhIdentifiers, groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include-members`, uhIdentifiers, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'put').mockResolvedValue(mockResponse); - const res = await addIncludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'put').mockRejectedValue(mockError); - const res = await addIncludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('addIncludeMembersAsync', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should make a PUT request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'put'); - await addIncludeMembersAsync(uhIdentifiers, groupingPath); - expect(requestSpy).toHaveBeenCalledWith( - `${baseUrl}/groupings/${groupingPath}/include-members/async`, uhIdentifiers, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'put').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncInProgressResponse); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncCompletedResponse); - - const res = addIncludeMembersAsync(uhIdentifiers, groupingPath); - await jest.advanceTimersByTimeAsync(5000); - - expect(await res).toEqual(mockAsyncCompletedResponse.data.result); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'put').mockRejectedValue(mockError); - const res = await addIncludeMembersAsync(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - - jest.spyOn(axios, 'put').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res2 = addIncludeMembersAsync(uhIdentifiers, groupingPath); - await jest.advanceTimersByTimeAsync(5000); - expect(await res2).toEqual(mockError.response.data); - }); - }); - - describe('addExcludeMembers', () => { - it('should make a PUT request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'put'); - await addExcludeMembers(uhIdentifiers, groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members`, uhIdentifiers, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'put').mockResolvedValue(mockResponse); - const res = await addExcludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'put').mockRejectedValue(mockError); - const res = await addExcludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('addExcludeMembersAsync', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should make a PUT request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'put'); - await addExcludeMembersAsync(uhIdentifiers, groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members/async`, - uhIdentifiers, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'put').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncInProgressResponse); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncCompletedResponse); - - const res = addExcludeMembersAsync(uhIdentifiers, groupingPath); - await jest.advanceTimersByTimeAsync(5000); - - expect(await res).toEqual(mockAsyncCompletedResponse.data.result); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'put').mockRejectedValue(mockError); - const res = await addExcludeMembersAsync(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - - jest.spyOn(axios, 'put').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res2 = addExcludeMembersAsync(uhIdentifiers, groupingPath); - await jest.advanceTimersByTimeAsync(5000); - expect(await res2).toEqual(mockError.response.data); - }); - }); - - describe('addOwners', () => { - it('should make a POST request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'post'); - await addOwners(uhIdentifiers, groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifiers}`, - undefined, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'post').mockResolvedValue(mockResponse); - const res = await addOwners(uhIdentifiers, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'post').mockRejectedValue(mockError); - const res = await addOwners(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('addAdmin', () => { - it('should make a POST request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'post'); - await addAdmin(uhIdentifier); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/admins/${uhIdentifier}`, undefined, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'post').mockResolvedValue(mockResponse); - const res = await addAdmin(uhIdentifier); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'post').mockRejectedValue(mockError); - const res = await addAdmin(uhIdentifier); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('removeFromGroups', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await removeFromGroups(uhIdentifier, groupPaths); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/admins/${groupPaths}/${uhIdentifier}`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await removeFromGroups(uhIdentifier, groupPaths); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await removeFromGroups(uhIdentifier, groupPaths); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('removeIncludeMembers', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await removeIncludeMembers(uhIdentifiers, groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include-members`, - { data: uhIdentifiers, ...headers }); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await removeIncludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await removeIncludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('removeExcludeMembers', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await removeExcludeMembers(uhIdentifiers, groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members`, - { data: uhIdentifiers, ...headers }); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await removeExcludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await removeExcludeMembers(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('removeOwners', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await removeOwners(uhIdentifiers, groupingPath); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifiers}`, - headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await removeOwners(uhIdentifiers, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await removeOwners(uhIdentifiers, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('removeAdmin', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await removeAdmin(uhIdentifier); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/admins/${uhIdentifier}`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await removeAdmin(uhIdentifier); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await removeAdmin(uhIdentifier); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('memberAttributeResults', () => { - it('should make a POST request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'post'); - await memberAttributeResults(uhIdentifiers); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/members`, uhIdentifiers, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'post').mockResolvedValue(mockResponse); - const res = await memberAttributeResults(uhIdentifiers); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'post').mockRejectedValue(mockError); - const res = await memberAttributeResults(uhIdentifiers); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('memberAttributeResultsAsync', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should make a POST request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'post'); - await memberAttributeResultsAsync(uhIdentifiers); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/members/async`, uhIdentifiers, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'post').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncInProgressResponse); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncCompletedResponse); - - const res = memberAttributeResultsAsync(uhIdentifiers); - await jest.advanceTimersByTimeAsync(5000); - - expect(await res).toEqual(mockAsyncCompletedResponse.data.result); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'post').mockRejectedValue(mockError); - const res = await memberAttributeResultsAsync(uhIdentifiers); - expect(res).toEqual(mockError.response.data); - - jest.spyOn(axios, 'post').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res2 = memberAttributeResultsAsync(uhIdentifiers); - await jest.advanceTimersByTimeAsync(5000); - expect(await res2).toEqual(mockError.response.data); - }); - }); - - describe('optIn', () => { - it('should make a PUT request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'put'); - await optIn(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include-members/${currentUser.uid}/self`, - undefined, headers) - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'put').mockResolvedValue(mockResponse); - const res = await optIn(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'put').mockRejectedValue(mockError); - const res = await optIn(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('optOut', () => { - it('should make a PUT request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'put'); - await optOut(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members/${currentUser.uid}/self`, - undefined, headers) - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'put').mockResolvedValue(mockResponse); - const res = await optOut(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'put').mockRejectedValue(mockError); - const res = await optOut(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('membershipResults', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await membershipResults(); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/members/${currentUser.uid}/memberships`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await membershipResults(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await membershipResults(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('managePersonResults', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await managePersonResults(uhIdentifier); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/members/${uhIdentifier}/groupings`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await managePersonResults(uhIdentifier); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await managePersonResults(uhIdentifier); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('getNumberOfMemberships', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await getNumberOfMemberships(); - expect(requestSpy).toHaveBeenCalledWith(`${baseUrl}/members/memberships/count`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await getNumberOfMemberships(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await getNumberOfMemberships(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('optInGroupingPaths', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await optInGroupingPaths(); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/members/${currentUser.uid}/opt-in-groups`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await optInGroupingPaths(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await optInGroupingPaths(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('resetIncludeGroup', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await resetIncludeGroup(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await resetIncludeGroup(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await resetIncludeGroup(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('resetIncludeGroupAsync', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await resetIncludeGroupAsync(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include/async`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncInProgressResponse); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncCompletedResponse); - - const res = resetIncludeGroupAsync(groupingPath); - await jest.advanceTimersByTimeAsync(5000); - - expect(await res).toEqual(mockAsyncCompletedResponse.data.result); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await resetIncludeGroupAsync(groupingPath); - expect(res).toEqual(mockError.response.data); - - jest.spyOn(axios, 'delete').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res2 = resetIncludeGroupAsync(groupingPath); - await jest.advanceTimersByTimeAsync(5000); - expect(await res2).toEqual(mockError.response.data); - }); - }); - - describe('resetExcludeGroup', () => { - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await resetExcludeGroup(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(mockResponse); - const res = await resetExcludeGroup(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await resetExcludeGroup(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('resetExcludeGroupAsync', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should make a DELETE request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'delete'); - await resetExcludeGroupAsync(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude/async`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'delete').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncInProgressResponse); - jest.spyOn(axios, 'get').mockResolvedValueOnce(mockAsyncCompletedResponse); - - const res = resetExcludeGroupAsync(groupingPath); - await jest.advanceTimersByTimeAsync(5000); - - expect(await res).toEqual(mockAsyncCompletedResponse.data.result); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'delete').mockRejectedValue(mockError); - const res = await resetExcludeGroupAsync(groupingPath); - expect(res).toEqual(mockError.response.data); - - jest.spyOn(axios, 'delete').mockResolvedValue(0); - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res2 = resetExcludeGroupAsync(groupingPath); - await jest.advanceTimersByTimeAsync(5000); - expect(await res2).toEqual(mockError.response.data); - }); - }); - - describe('groupingOwners', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await groupingOwners(groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/grouping/${groupingPath}/owners`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await groupingOwners(groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await groupingOwners(groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('ownersGroupings', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await ownerGroupings(); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/owners/${currentUser.uid}/groupings`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await ownerGroupings(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await ownerGroupings(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('getNumberOfGroupings', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await getNumberOfGroupings(); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/owners/${currentUser.uid}/groupings/count`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await getNumberOfGroupings(); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await getNumberOfGroupings(); - expect(res).toEqual(mockError.response.data); - }); - }); - - describe('isSoleOwner', () => { - it('should make a GET request at the correct endpoint', async () => { - const requestSpy = jest.spyOn(axios, 'get'); - await isSoleOwner(uhIdentifier, groupingPath); - expect(requestSpy) - .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifier}`, headers); - }); - - it('should handle the successful response', async () => { - jest.spyOn(axios, 'get').mockResolvedValue(mockResponse); - const res = await isSoleOwner(uhIdentifier, groupingPath); - expect(res).toEqual(mockResponse.data); - }); - - it('should handle the error response', async () => { - jest.spyOn(axios, 'get').mockRejectedValue(mockError); - const res = await isSoleOwner(uhIdentifier, groupingPath); - expect(res).toEqual(mockError.response.data); - }); - }); - -}); diff --git a/ui/tests/services/GroupingsApiService.test.ts b/ui/tests/services/GroupingsApiService.test.ts new file mode 100644 index 00000000..58004c8f --- /dev/null +++ b/ui/tests/services/GroupingsApiService.test.ts @@ -0,0 +1,930 @@ +import { + addAdmin, + addExcludeMembers, + addExcludeMembersAsync, + addIncludeMembers, + addIncludeMembersAsync, + addOwners, + getAllGroupings, + getAnnouncements, + getNumberOfGroupings, + getNumberOfMemberships, + groupingAdmins, + groupingDescription, + groupingOptAttributes, + groupingOwners, + groupingSyncDest, + isSoleOwner, + managePersonResults, + memberAttributeResults, + memberAttributeResultsAsync, + membershipResults, + optIn, + optInGroupingPaths, + optOut, + ownedGrouping, + ownerGroupings, + removeAdmin, + removeExcludeMembers, + removeFromGroups, + removeIncludeMembers, + removeOwners, + resetExcludeGroup, + resetExcludeGroupAsync, + resetIncludeGroup, + resetIncludeGroupAsync, + updateDescription +} from '@/services/GroupingsApiService'; +import * as AuthenticationService from '@/access/AuthenticationService'; +import User from '@/access/User'; + +const baseUrl = process.env.NEXT_PUBLIC_API_2_1_BASE_URL as string; +const testUser: User = JSON.parse(process.env.TEST_USER_A as string); + +jest.mock('@/access/AuthenticationService'); + +describe('GroupingsService', () => { + + const currentUser = testUser; + const headers = { 'current_user': currentUser.uid }; + + const uhIdentifier = 'testiwta'; + const uhIdentifiers = ['testiwta', 'testiwtb']; + const groupingPath = 'tmp:testiwta:testiwta-aux'; + const groupPaths = [ + `${groupingPath}:include`, + `${groupingPath}:include`, + `${groupingPath}:exclude`, + `${groupingPath}:owners` + ]; + + const mockResponse = { + resultCode: 'SUCCESS' + } + const mockAsyncCompletedResponse = { + status: 'COMPLETED', + result: { + resultCode: 'SUCCESS' + } + } + const mockAsyncInProgressResponse = { + status: 'IN_PROGRESS' + } + const mockError = { + resultCode: 'FAILURE' + } + + beforeAll(() => { + jest.spyOn(AuthenticationService, 'getCurrentUser').mockResolvedValue(testUser); + }) + + describe('getAnnouncements', () => { + it('should make a GET request at the correct endpoint', async () => { + await getAnnouncements(); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/announcements`, { + headers: { 'current_user': ''} + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await getAnnouncements()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await getAnnouncements()).toEqual(mockError); + }); + }); + + describe('ownedGrouping', () => { + const page = 1; + const size = 700; + const sortString = 'name'; + const isAscending = true; + + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should make a POST request at the correct endpoint', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + await ownedGrouping(groupPaths, page, size, sortString, isAscending); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/group?` + + `page=${page}&size=${size}&sortString=${sortString}&isAscending=${isAscending}`, { + body: JSON.stringify(groupPaths), + headers, + method: 'POST' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await ownedGrouping(groupPaths, page, size, sortString, isAscending)).toEqual(mockResponse); + + fetchMock + .mockResponseOnce(JSON.stringify(mockResponse), { status: 500 }) + .mockResponseOnce(JSON.stringify(mockResponse)); + let res = ownedGrouping(groupPaths, page, size, sortString, isAscending); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockResponse); + + fetchMock + .mockResponseOnce(JSON.stringify(mockResponse), { status: 500 }) + .mockResponseOnce(JSON.stringify(mockResponse), { status: 500 }) + .mockResponseOnce(JSON.stringify(mockResponse)); + res = ownedGrouping(groupPaths, page, size, sortString, isAscending); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockResponse); + + fetchMock + .mockResponseOnce(JSON.stringify(mockResponse), { status: 500 }) + .mockResponseOnce(JSON.stringify(mockResponse), { status: 500 }) + .mockResponseOnce(JSON.stringify(mockResponse), { status: 500 }) + .mockResponseOnce(JSON.stringify(mockResponse)); + res = ownedGrouping(groupPaths, page, size, sortString, isAscending); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockResponse(JSON.stringify(mockError), { status: 500 }); + let res = ownedGrouping(groupPaths, page, size, sortString, isAscending); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + + fetchMock.mockReject(() => Promise.reject(mockError)); + res = ownedGrouping(groupPaths, page, size, sortString, isAscending); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + }); + }); + + describe('groupingDescription', () => { + it('should make a GET request at the correct endpoint', async () => { + await groupingDescription(groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/description`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await groupingDescription(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await groupingDescription(groupingPath)).toEqual(mockError); + }); + }); + + describe('groupingSyncDest', () => { + it('should make a GET request at the correct endpoint', async () => { + await groupingSyncDest(groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/groupings-sync-destinations`, + { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await groupingSyncDest(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await groupingSyncDest(groupingPath)).toEqual(mockError); + }); + }); + + describe('groupingOptAttributes', () => { + it('should make a GET request at the correct endpoint', async () => { + await groupingOptAttributes(groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/opt-attributes`, + { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await groupingOptAttributes(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await groupingOptAttributes(groupingPath)).toEqual(mockError); + }); + }); + + describe('updateDescription', () => { + const description = 'description'; + + it('should make a POST request at the correct endpoint', async () => { + await updateDescription(description, groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/description`, { + body: JSON.stringify(description), + headers, + method: 'POST' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await updateDescription(description, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await updateDescription(description, groupingPath)).toEqual(mockError); + }); + }); + + describe('groupingAdmins', () => { + it('should make a GET request at the correct endpoint', async () => { + await groupingAdmins(); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/grouping-admins`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await groupingAdmins()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await groupingAdmins()).toEqual(mockError); + }); + }); + + describe('getAllGroupings', () => { + it('should make a GET request at the correct endpoint', async () => { + await getAllGroupings(); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/all-groupings`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await getAllGroupings()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await getAllGroupings()).toEqual(mockError); + }); + }); + + describe('AddIncludeMembers', () => { + it('should make a PUT request at the correct endpoint', async () => { + await addIncludeMembers(uhIdentifiers, groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include-members`, { + body: JSON.stringify(uhIdentifiers), + headers, + method: 'PUT' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await addIncludeMembers(uhIdentifiers, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await addIncludeMembers(uhIdentifiers, groupingPath)).toEqual(mockError); + }); + }); + + describe('addIncludeMembersAsync', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should make a PUT request at the correct endpoint', async () => { + await addIncludeMembersAsync(uhIdentifiers, groupingPath); + expect(fetch).toHaveBeenCalledWith( + `${baseUrl}/groupings/${groupingPath}/include-members/async`,{ + body: JSON.stringify(uhIdentifiers), + headers, + method: 'PUT' + }); + }); + + it('should handle the successful response', async () => { + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockResponseOnce(JSON.stringify(mockAsyncInProgressResponse)) + .mockResponseOnce(JSON.stringify(mockAsyncCompletedResponse)); + + const res = addIncludeMembersAsync(uhIdentifiers, groupingPath); + await jest.advanceTimersByTimeAsync(5000); + + expect(await res).toEqual(mockAsyncCompletedResponse.result); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + let res = addIncludeMembersAsync(uhIdentifiers, groupingPath); + expect(await res).toEqual(mockError); + + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockRejectOnce(() => Promise.reject(mockError)); + res = addIncludeMembersAsync(uhIdentifiers, groupingPath); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + }); + }); + + describe('addExcludeMembers', () => { + it('should make a PUT request at the correct endpoint', async () => { + await addExcludeMembers(uhIdentifiers, groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members`, { + body: JSON.stringify(uhIdentifiers), + headers, + method: 'PUT' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await addExcludeMembers(uhIdentifiers, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await addExcludeMembers(uhIdentifiers, groupingPath)).toEqual(mockError); + }); + }); + + describe('addExcludeMembersAsync', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should make a PUT request at the correct endpoint', async () => { + await addExcludeMembersAsync(uhIdentifiers, groupingPath); + expect(fetch).toHaveBeenCalledWith( + `${baseUrl}/groupings/${groupingPath}/exclude-members/async`,{ + body: JSON.stringify(uhIdentifiers), + headers, + method: 'PUT' + }); + }); + + it('should handle the successful response', async () => { + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockResponseOnce(JSON.stringify(mockAsyncInProgressResponse)) + .mockResponseOnce(JSON.stringify(mockAsyncCompletedResponse)); + + const res = addExcludeMembersAsync(uhIdentifiers, groupingPath); + await jest.advanceTimersByTimeAsync(5000); + + expect(await res).toEqual(mockAsyncCompletedResponse.result); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + let res = addExcludeMembersAsync(uhIdentifiers, groupingPath); + expect(await res).toEqual(mockError); + + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockRejectOnce(() => Promise.reject(mockError)); + res = addExcludeMembersAsync(uhIdentifiers, groupingPath); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + }); + }); + + describe('addOwners', () => { + it('should make a POST request at the correct endpoint', async () => { + await addOwners(uhIdentifiers, groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifiers}`, { + headers, + method: 'POST' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await addOwners(uhIdentifiers, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await addOwners(uhIdentifiers, groupingPath)).toEqual(mockError); + }); + }); + + describe('addAdmin', () => { + it('should make a POST request at the correct endpoint', async () => { + await addAdmin(uhIdentifier); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/admins/${uhIdentifier}`, { + headers, + method: 'POST' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await addAdmin(uhIdentifier)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await addAdmin(uhIdentifier)).toEqual(mockError); + }); + }); + + describe('removeFromGroups', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await removeFromGroups(uhIdentifier, groupPaths); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/admins/${groupPaths}/${uhIdentifier}`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await removeFromGroups(uhIdentifier, groupPaths)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await removeFromGroups(uhIdentifier, groupPaths)).toEqual(mockError); + }); + }); + + describe('removeIncludeMembers', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await removeIncludeMembers(uhIdentifiers, groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include-members`, { + body: JSON.stringify(uhIdentifiers), + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await removeIncludeMembers(uhIdentifiers, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await removeIncludeMembers(uhIdentifiers, groupingPath)).toEqual(mockError); + }); + }); + + describe('removeExcludeMembers', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await removeExcludeMembers(uhIdentifiers, groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members`, { + body: JSON.stringify(uhIdentifiers), + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await removeExcludeMembers(uhIdentifiers, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await removeExcludeMembers(uhIdentifiers, groupingPath)).toEqual(mockError); + }); + }); + + describe('removeOwners', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await removeOwners(uhIdentifiers, groupingPath); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifiers}`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await removeOwners(uhIdentifiers, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await removeOwners(uhIdentifiers, groupingPath)).toEqual(mockError); + }); + }); + + describe('removeAdmin', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await removeAdmin(uhIdentifier); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/admins/${uhIdentifier}`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await removeAdmin(uhIdentifier)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await removeAdmin(uhIdentifier)).toEqual(mockError); + }); + }); + + describe('memberAttributeResults', () => { + it('should make a POST request at the correct endpoint', async () => { + await memberAttributeResults(uhIdentifiers); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/members`, { + body: JSON.stringify(uhIdentifiers), + headers, + method: 'POST' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await memberAttributeResults(uhIdentifiers)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await memberAttributeResults(uhIdentifiers)).toEqual(mockError); + }); + }); + + describe('memberAttributeResultsAsync', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should make a POST request at the correct endpoint', async () => { + await memberAttributeResultsAsync(uhIdentifiers); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/members/async`, { + body: JSON.stringify(uhIdentifiers), + headers, + method: 'POST' + }); + }); + + it('should handle the successful response', async () => { + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockResponseOnce(JSON.stringify(mockAsyncInProgressResponse)) + .mockResponseOnce(JSON.stringify(mockAsyncCompletedResponse)); + + const res = memberAttributeResultsAsync(uhIdentifiers); + await jest.advanceTimersByTimeAsync(5000); + + expect(await res).toEqual(mockAsyncCompletedResponse.result); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + let res = memberAttributeResultsAsync(uhIdentifiers); + expect(await res).toEqual(mockError); + + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockRejectOnce(() => Promise.reject(mockError)); + res = addExcludeMembersAsync(uhIdentifiers, groupingPath); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + }); + }); + + describe('optIn', () => { + it('should make a PUT request at the correct endpoint', async () => { + await optIn(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include-members/${currentUser.uid}/self`, { + headers, + method: 'PUT' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await optIn(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await optIn(groupingPath)).toEqual(mockError); + }); + }); + + describe('optOut', () => { + it('should make a PUT request at the correct endpoint', async () => { + await optOut(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude-members/${currentUser.uid}/self`, { + headers, + method: 'PUT' + }) + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await optOut(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await optOut(groupingPath)).toEqual(mockError); + }); + }); + + describe('membershipResults', () => { + it('should make a GET request at the correct endpoint', async () => { + await membershipResults(); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/members/${currentUser.uid}/memberships`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await membershipResults()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await membershipResults()).toEqual(mockError); + }); + }); + + describe('managePersonResults', () => { + it('should make a GET request at the correct endpoint', async () => { + await managePersonResults(uhIdentifier); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/members/${uhIdentifier}/groupings`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await managePersonResults(uhIdentifier)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await managePersonResults(uhIdentifier)).toEqual(mockError); + }); + }); + + describe('getNumberOfMemberships', () => { + it('should make a GET request at the correct endpoint', async () => { + await getNumberOfMemberships(); + expect(fetch).toHaveBeenCalledWith(`${baseUrl}/members/${currentUser.uid}/memberships/count`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await getNumberOfMemberships()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await getNumberOfMemberships()).toEqual(mockError); + }); + }); + + describe('optInGroupingPaths', () => { + it('should make a GET request at the correct endpoint', async () => { + await optInGroupingPaths(); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/members/${currentUser.uid}/opt-in-groups`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await optInGroupingPaths()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await optInGroupingPaths()).toEqual(mockError); + }); + }); + + describe('resetIncludeGroup', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await resetIncludeGroup(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await resetIncludeGroup(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await resetIncludeGroup(groupingPath)).toEqual(mockError); + }); + }); + + describe('resetIncludeGroupAsync', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should make a DELETE request at the correct endpoint', async () => { + await resetIncludeGroupAsync(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/include/async`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockResponseOnce(JSON.stringify(mockAsyncInProgressResponse)) + .mockResponseOnce(JSON.stringify(mockAsyncCompletedResponse)); + + const res = resetIncludeGroupAsync(groupingPath); + await jest.advanceTimersByTimeAsync(5000); + + expect(await res).toEqual(mockAsyncCompletedResponse.result); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + let res = resetIncludeGroupAsync(groupingPath); + expect(await res).toEqual(mockError); + + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockRejectOnce(() => Promise.reject(mockError)); + res = resetIncludeGroupAsync(groupingPath); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + }); + }); + + describe('resetExcludeGroup', () => { + it('should make a DELETE request at the correct endpoint', async () => { + await resetExcludeGroup(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await resetExcludeGroup(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await resetExcludeGroup(groupingPath)).toEqual(mockError); + }); + }); + + describe('resetExcludeGroupAsync', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should make a DELETE request at the correct endpoint', async () => { + await resetExcludeGroupAsync(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/exclude/async`, { + headers, + method: 'DELETE' + }); + }); + + it('should handle the successful response', async () => { + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockResponseOnce(JSON.stringify(mockAsyncInProgressResponse)) + .mockResponseOnce(JSON.stringify(mockAsyncCompletedResponse)); + + const res = resetExcludeGroupAsync(groupingPath); + await jest.advanceTimersByTimeAsync(5000); + + expect(await res).toEqual(mockAsyncCompletedResponse.result); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + let res = resetExcludeGroupAsync(groupingPath); + expect(await res).toEqual(mockError); + + fetchMock + .mockResponseOnce(JSON.stringify(0)) + .mockRejectOnce(() => Promise.reject(mockError)); + res = resetExcludeGroupAsync(groupingPath); + await jest.advanceTimersByTimeAsync(5000); + expect(await res).toEqual(mockError); + }); + }); + + describe('groupingOwners', () => { + it('should make a GET request at the correct endpoint', async () => { + await groupingOwners(groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/grouping/${groupingPath}/owners`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await groupingOwners(groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await groupingOwners(groupingPath)).toEqual(mockError); + }); + }); + + describe('ownersGroupings', () => { + it('should make a GET request at the correct endpoint', async () => { + await ownerGroupings(); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/owners/${currentUser.uid}/groupings`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await ownerGroupings()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await ownerGroupings()).toEqual(mockError); + }); + }); + + describe('getNumberOfGroupings', () => { + it('should make a GET request at the correct endpoint', async () => { + await getNumberOfGroupings(); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/owners/${currentUser.uid}/groupings/count`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await getNumberOfGroupings()).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await getNumberOfGroupings()).toEqual(mockError); + }); + }); + + describe('isSoleOwner', () => { + it('should make a GET request at the correct endpoint', async () => { + await isSoleOwner(uhIdentifier, groupingPath); + expect(fetch) + .toHaveBeenCalledWith(`${baseUrl}/groupings/${groupingPath}/owners/${uhIdentifier}`, { headers }); + }); + + it('should handle the successful response', async () => { + fetchMock.mockResponse(JSON.stringify(mockResponse)); + expect(await isSoleOwner(uhIdentifier, groupingPath)).toEqual(mockResponse); + }); + + it('should handle the error response', async () => { + fetchMock.mockReject(() => Promise.reject(mockError)); + expect(await isSoleOwner(uhIdentifier, groupingPath)).toEqual(mockError); + }); + }); +});