From 0e4f651adbcf4b16d8f603b11dfd34cac3d08d8d Mon Sep 17 00:00:00 2001 From: craigrbarnes Date: Mon, 3 Feb 2025 16:47:21 -0600 Subject: [PATCH] add tests for cohortApi --- .../GWAS/Utils/cohortApi.test.tsx | 160 ++++++++++++++++++ src/lib/AnalysisApps/GWAS/Utils/cohortApi.tsx | 61 +++++++ .../AnalysisApps/GWAS/Utils/cohortHooks.ts | 29 ++++ .../Utils/teamProjectHooks.test.tsx | 17 +- 4 files changed, 252 insertions(+), 15 deletions(-) create mode 100644 src/lib/AnalysisApps/GWAS/Utils/cohortApi.test.tsx create mode 100644 src/lib/AnalysisApps/GWAS/Utils/cohortApi.tsx create mode 100644 src/lib/AnalysisApps/GWAS/Utils/cohortHooks.ts diff --git a/src/lib/AnalysisApps/GWAS/Utils/cohortApi.test.tsx b/src/lib/AnalysisApps/GWAS/Utils/cohortApi.test.tsx new file mode 100644 index 0000000..cc0a7ae --- /dev/null +++ b/src/lib/AnalysisApps/GWAS/Utils/cohortApi.test.tsx @@ -0,0 +1,160 @@ +import { waitFor } from '@testing-library/react'; +import { http, HttpResponse } from 'msw'; +import { setupServer } from 'msw/node'; +import { + describe, + expect, + it, + beforeAll, + afterAll, + afterEach, + beforeEach, +} from '@jest/globals'; +import { renderHook } from '../../../test/test-utils'; + +import { + GEN3_COHORT_MIDDLEWARE_API, + useGetCohortDefinitionsQuery, + useGetSourcesQuery +} from './cohortApi'; + +const server = setupServer(); + +const cohortDefinitionAndStatsData = { + cohort_definitions_and_stats: [ + { + cohort_definition_id: 573, + cohort_name: 'team2 - test new cohort - catch all', + size: 70240, + }, + { + cohort_definition_id: 559, + cohort_name: 'test new cohort - catch all', + size: 70240, + }, + { + cohort_definition_id: 574, + cohort_name: 'team2 - test new cohort - medium + large', + size: 23800, + }, + { + cohort_definition_id: 575, + cohort_name: 'team2 - test new cohort - small', + size: 80, + }, + ]}; + +describe('cohortApi', () => { + beforeAll(() => { + // Start the interception. + server.listen(); + }); + beforeEach(() => {}); + + afterEach(() => { + // Remove any handlers you may have added + // in individual tests (runtime handlers). + server.resetHandlers(); + }); + + afterAll(() => { + // Disable request interception and clean up. + server.close(); + }); + + it('fetches and returns cohort definitions successfully', async () => { + const sourceId = '1234567890'; + const selectedTeamProject = 'project2'; + + server.use( + http.get( + `${GEN3_COHORT_MIDDLEWARE_API}/cohortdefinition-stats/by-source-id/1234567890/by-team-project`, + ({ request }) => { + const url = new URL(request.url); + const project = url.searchParams.get('team-project'); + if (project !== 'project2') { + return new HttpResponse(null, { + status: 403, + }); + } + return HttpResponse.json(cohortDefinitionAndStatsData); + }, + ), + ); + + const { result } = renderHook(() => + useGetCohortDefinitionsQuery({ sourceId, selectedTeamProject }), + ); + + expect(result.current.isFetching).toBe(true); + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); + + expect(result.current).toMatchObject({ + isError: false, + isFetching: false, + isSuccess: true, + isLoading: false, + data: cohortDefinitionAndStatsData, + }); + }); + + it('returns error if project id not accessible ', async () => { + + const sourceId = '1234567890'; + const selectedTeamProject = 'project2345'; + + server.use( + http.get( + `${GEN3_COHORT_MIDDLEWARE_API}/cohortdefinition-stats/by-source-id/1234567890/by-team-project`, + () => { + return new HttpResponse(null, { + status: 403, + }); + }, + ), + ); + + const { result } = renderHook(() => + useGetCohortDefinitionsQuery({ sourceId, selectedTeamProject }), + ); + + expect(result.current.isFetching).toBe(true); + + await waitFor(() => expect(result.current.isError).toBeTruthy()); + expect(result.current).toMatchObject({ + isError: true, + isFetching: false, + isSuccess: false, + isLoading: false, + error: { status: 403} + }); + }); + + it('test for successful useGetSources ', async () => { + const data = {"sources":[{"source_id":123,"source_name":"MVP-batch19000101"}]}; + server.use( + http.get( + `${GEN3_COHORT_MIDDLEWARE_API}/sources`, + () => { + return HttpResponse.json(data); + }, + ), + ); + + const { result } = renderHook(() => + useGetSourcesQuery(), + ); + + expect(result.current.isFetching).toBe(true); + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); + expect(result.current).toMatchObject({ + isError: false, + isFetching: false, + isSuccess: true, + isLoading: false, + data: data + }); + }); +}); diff --git a/src/lib/AnalysisApps/GWAS/Utils/cohortApi.tsx b/src/lib/AnalysisApps/GWAS/Utils/cohortApi.tsx new file mode 100644 index 0000000..55b3708 --- /dev/null +++ b/src/lib/AnalysisApps/GWAS/Utils/cohortApi.tsx @@ -0,0 +1,61 @@ +import { gen3Api, GEN3_API} from '@gen3/core'; + +const TAGS = 'GWASApp'; +export const GEN3_COHORT_MIDDLEWARE_API = `${GEN3_API}/cohort-middleware`; + +const hareConceptId = 2000007027; + +export const gwasCohortApiTags = gen3Api.enhanceEndpoints({ + addTagTypes: [TAGS], +}); + +// Types for API calls + +interface CohortDefinitionQueryParams { + sourceId: string; + selectedTeamProject: string; +} + +export interface GWASCohortDefinition { + cohort_definition_id: number; + cohort_name: string; + size: number; +} + +export interface GWASCohortDefinitionResponse { + cohort_definitions_and_stats: Array; +} + +interface SourcesResponse { + sources: Array<{source_id: string, source_name: string}> +} + +export const gwasCohortApi = gwasCohortApiTags.injectEndpoints({ + endpoints: (builder) => ({ + getCohortDefinitions: builder.query({ + query: ({ + sourceId, + selectedTeamProject + }) => `${GEN3_COHORT_MIDDLEWARE_API}/cohortdefinition-stats/by-source-id/${sourceId}/by-team-project?team-project=${selectedTeamProject}`, + transformResponse: (response: Record) => { + // confirm data is valid + if (!response || typeof response !== 'object') { + throw new Error('Invalid response format'); + } + if (!('cohort_definitions_and_stats' in response)) { + throw new Error('Missing field cohort_definitions_and_stats'); + } + return { cohort_definitions_and_stats: response.cohort_definitions_and_stats }; + }, + }), + getSources: builder.query< SourcesResponse, void> ({ + query: () => `${GEN3_COHORT_MIDDLEWARE_API}/sources`, + }), + }), +}); + + +export const { + useGetCohortDefinitionsQuery, + useGetSourcesQuery +} = gwasCohortApi; diff --git a/src/lib/AnalysisApps/GWAS/Utils/cohortHooks.ts b/src/lib/AnalysisApps/GWAS/Utils/cohortHooks.ts new file mode 100644 index 0000000..a084d48 --- /dev/null +++ b/src/lib/AnalysisApps/GWAS/Utils/cohortHooks.ts @@ -0,0 +1,29 @@ +import { useCallback, useState, useEffect } from 'react'; +import { useGetSourcesQuery } from '@/lib/AnalysisApps/GWAS/Utils/cohortApi'; + +export const useSourceFetch = () => { + const [sourceId, setSourceId] = useState(undefined); + const { data, error, isError, isSuccess, isFetching } = useGetSourcesQuery(); + const getSources = () => { + // fetch sources on initialization + + if (isSuccess) { + if (Array.isArray(data?.sources) && data.sources.length === 1) { + setSourceId(data.sources[0].source_id); + } else { + const message = `Data source received in an invalid format: ${JSON.stringify( + data?.sources, + )}`; + throw new Error(message); + } + } + if (isError) { + const message = `Data source received an error: ${error}`; + throw new Error(message); + } + }; + useEffect(() => { + getSources(); + }, [getSources]); + return { isFetching, sourceId }; +}; diff --git a/src/lib/AnalysisApps/SharedUtils/TeamProject/Utils/teamProjectHooks.test.tsx b/src/lib/AnalysisApps/SharedUtils/TeamProject/Utils/teamProjectHooks.test.tsx index 9863f9b..4d264c8 100644 --- a/src/lib/AnalysisApps/SharedUtils/TeamProject/Utils/teamProjectHooks.test.tsx +++ b/src/lib/AnalysisApps/SharedUtils/TeamProject/Utils/teamProjectHooks.test.tsx @@ -15,21 +15,7 @@ import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; import { GEN3_AUTHZ_API } from '@gen3/core'; -const server = setupServer( - http.get(`${GEN3_AUTHZ_API}/mapping`, () => { - return HttpResponse.json({ - '/gwas_projects/project1': [{ abc: 'def' }], - '/gwas_projects/project2': [ - { abc: 'def' }, - { - service: 'atlas-argo-wrapper-and-cohort-middleware', - method: 'access', - }, - ], - '/other/project3': [{ abc: 'def' }], - }); - }), -); +const server = setupServer(); describe('useTeamProjects', () => { beforeAll(() => { @@ -82,6 +68,7 @@ describe('useTeamProjects', () => { ], }); }); + it('fetches and returns isError', async () => { server.use( http.get(`${GEN3_AUTHZ_API}/mapping`, () => {