From 6783a106e06e8b30477ff1ce8228f194ed168b32 Mon Sep 17 00:00:00 2001 From: Salaton Date: Wed, 22 Mar 2023 15:16:31 +0300 Subject: [PATCH] feat: get patient's temperature entries --- .github/workflows/ci.yml | 2 +- .../datastore/cloudhealthcare/fhir.go | 38 ++++ .../cloudhealthcare/fhir_unit_test.go | 56 ++++++ .../cloudhealthcare/mock/fhir_mock.go | 54 +++++- .../presentation/graph/clinical.graphql | 3 + .../presentation/graph/clinical.resolvers.go | 7 + .../presentation/graph/generated/generated.go | 175 +++++++++++++++++- pkg/clinical/repository/repository.go | 1 + pkg/clinical/usecases/clinical/observation.go | 39 ++++ .../usecases/clinical/observation_test.go | 152 +++++++++++++++ pkg/clinical/usecases/usecases.go | 3 + 11 files changed, 523 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66304f24..6a7978e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,7 @@ jobs: - name: Run tests run: | - go-acc -o coverage.txt --ignore generated,cmd ./... -- -timeout 60m + go-acc -o coverage.txt --ignore generated,cmd,graph ./... -- -timeout 60m grep -v "generated.go" coverage.txt | grep -v "_gen.go" | grep -v "_mock.go" | grep -v "*mock.go" | grep -v "mocks.go" | grep -v "*resolver*go" | grep -v "server.go" > coverage.out go tool cover -html=coverage.out -o coverage.html gocov convert coverage.out > coverage.json diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go index 2467ae35..daacb02e 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go @@ -62,6 +62,44 @@ func NewFHIRStoreImpl( } } +// SearchPatientObservations fetches all observations that belong to a specific patient +func (fh StoreImpl) SearchPatientObservations( + _ context.Context, + patientReference string, + observationCode string, + tenant dto.TenantIdentifiers, +) ([]*domain.FHIRObservation, error) { + params := map[string]interface{}{ + "patient": patientReference, + "code": observationCode, + } + + observations, err := fh.Dataset.SearchFHIRResource(observationResourceType, params, tenant) + if err != nil { + return nil, err + } + + output := []*domain.FHIRObservation{} + + for _, obs := range observations { + var observation domain.FHIRObservation + + resourceBs, err := json.Marshal(obs) + if err != nil { + return nil, fmt.Errorf("unable to marshal resource to JSON: %w", err) + } + + err = json.Unmarshal(resourceBs, &observation) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal resource: %w", err) + } + + output = append(output, &observation) + } + + return output, nil +} + // Encounters returns encounters that belong to the indicated patient. // // The patientReference should be a [string] in the format "Patient/". diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go index dd77148c..9ba21192 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go @@ -3887,3 +3887,59 @@ func TestStoreImpl_EndEpisode(t *testing.T) { }) } } + +func TestStoreImpl_SearchPatientObservations(t *testing.T) { + ctx := context.Background() + type args struct { + ctx context.Context + patientReference string + observationCode string + tenant dto.TenantIdentifiers + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Happy Case - Successfully search patient observation", + args: args{ + ctx: ctx, + patientReference: fmt.Sprintf("Patient/%s", gofakeit.UUID()), + observationCode: "5088", + }, + wantErr: false, + }, + { + name: "Sad Case - fail to search fhir resource", + args: args{ + ctx: ctx, + patientReference: fmt.Sprintf("Patient/%s", gofakeit.UUID()), + observationCode: "5088", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dataset := fakeDataset.NewFakeFHIRRepositoryMock() + fh := FHIR.NewFHIRStoreImpl(dataset) + + if tt.name == "Sad Case - fail to search fhir resource" { + dataset.MockSearchFHIRResourceFn = func(resourceType string, params map[string]interface{}, tenant dto.TenantIdentifiers) ([]map[string]interface{}, error) { + return nil, fmt.Errorf("failed to search observation resource") + } + } + + got, err := fh.SearchPatientObservations(tt.args.ctx, tt.args.patientReference, tt.args.observationCode, tt.args.tenant) + if (err != nil) != tt.wantErr { + t.Errorf("StoreImpl.SearchPatientObservations() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && got == nil { + t.Errorf("expected response not to be nil for %v", tt.name) + return + } + }) + } +} diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go index 43f78f42..5de72fb0 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go @@ -68,6 +68,7 @@ type FHIRMock struct { MockPatchFHIRPatientFn func(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error) MockUpdateFHIREpisodeOfCareFn func(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error) MockSearchFHIRPatientFn func(ctx context.Context, searchParams string, tenant dto.TenantIdentifiers) (*domain.PatientConnection, error) + MockSearchPatientObservationsFn func(ctx context.Context, patientReference, conceptID string, tenant dto.TenantIdentifiers) ([]*domain.FHIRObservation, error) } // NewFHIRMock initializes a new instance of FHIR mock @@ -466,7 +467,26 @@ func NewFHIRMock() *FHIRMock { return true, nil }, MockSearchFHIRObservationFn: func(ctx context.Context, params map[string]interface{}, tenant dto.TenantIdentifiers) (*domain.FHIRObservationRelayConnection, error) { - return &domain.FHIRObservationRelayConnection{}, nil + uuid := uuid.New().String() + finalStatus := domain.ObservationStatusEnumFinal + return &domain.FHIRObservationRelayConnection{ + Edges: []*domain.FHIRObservationRelayEdge{ + { + Cursor: new(string), + Node: &domain.FHIRObservation{ + ID: &uuid, + Status: &finalStatus, + Subject: &domain.FHIRReference{ + ID: &uuid, + }, + Encounter: &domain.FHIRReference{ + ID: &uuid, + }, + }, + }, + }, + PageInfo: &firebasetools.PageInfo{}, + }, nil }, MockCreateFHIRObservationFn: func(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) { uuid := uuid.New().String() @@ -655,6 +675,33 @@ func NewFHIRMock() *FHIRMock { PageInfo: &firebasetools.PageInfo{}, }, nil }, + MockSearchPatientObservationsFn: func(ctx context.Context, patientReference, conceptID string, tenant dto.TenantIdentifiers) ([]*domain.FHIRObservation, error) { + uuid := uuid.New().String() + finalStatus := domain.ObservationStatusEnumFinal + return []*domain.FHIRObservation{{ + ID: &uuid, + Status: &finalStatus, + Subject: &domain.FHIRReference{ + ID: &uuid, + }, + Encounter: &domain.FHIRReference{ + ID: &uuid, + }, + Code: domain.FHIRCodeableConcept{ + ID: new(string), + Coding: []*domain.FHIRCoding{ + { + ID: new(string), + Version: new(string), + Code: "", + Display: "Vital", + UserSelected: new(bool), + }, + }, + Text: "", + }, + }}, nil + }, } } @@ -902,3 +949,8 @@ func (fh *FHIRMock) UpdateFHIREpisodeOfCare(ctx context.Context, fhirResourceID func (fh *FHIRMock) SearchFHIRPatient(ctx context.Context, searchParams string, tenant dto.TenantIdentifiers) (*domain.PatientConnection, error) { return fh.MockSearchFHIRPatientFn(ctx, searchParams, tenant) } + +// SearchPatientObservations mocks the implementation of searching patient observations +func (fh *FHIRMock) SearchPatientObservations(ctx context.Context, patientReference, conceptID string, tenant dto.TenantIdentifiers) ([]*domain.FHIRObservation, error) { + return fh.MockSearchPatientObservationsFn(ctx, patientReference, conceptID, tenant) +} diff --git a/pkg/clinical/presentation/graph/clinical.graphql b/pkg/clinical/presentation/graph/clinical.graphql index e72c7b32..7212ed08 100644 --- a/pkg/clinical/presentation/graph/clinical.graphql +++ b/pkg/clinical/presentation/graph/clinical.graphql @@ -6,6 +6,9 @@ extend type Query { # Encounter listPatientEncounters(patientID: String!): [Encounter!]! + + # Observation + getPatientTemperatureEntries(patientID: String!): [Observation!] } extend type Mutation { diff --git a/pkg/clinical/presentation/graph/clinical.resolvers.go b/pkg/clinical/presentation/graph/clinical.resolvers.go index bb62f944..f39a2cb6 100644 --- a/pkg/clinical/presentation/graph/clinical.resolvers.go +++ b/pkg/clinical/presentation/graph/clinical.resolvers.go @@ -6,6 +6,7 @@ package graph import ( "context" + "github.com/savannahghi/clinical/pkg/clinical/application/dto" "github.com/savannahghi/clinical/pkg/clinical/domain" "github.com/savannahghi/clinical/pkg/clinical/presentation/graph/generated" @@ -118,6 +119,12 @@ func (r *queryResolver) ListPatientEncounters(ctx context.Context, patientID str return r.usecases.Clinical.ListPatientEncounters(ctx, patientID) } +// GetPatientTemperatureEntries is the resolver for the getPatientTemperatureEntries field. +func (r *queryResolver) GetPatientTemperatureEntries(ctx context.Context, patientID string) ([]*dto.Observation, error) { + r.CheckDependencies() + return r.usecases.Clinical.GetPatientTemperatureEntries(ctx, patientID) +} + // Mutation returns generated.MutationResolver implementation. func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} } diff --git a/pkg/clinical/presentation/graph/generated/generated.go b/pkg/clinical/presentation/graph/generated/generated.go index d1f254ae..e91dbde3 100644 --- a/pkg/clinical/presentation/graph/generated/generated.go +++ b/pkg/clinical/presentation/graph/generated/generated.go @@ -369,11 +369,12 @@ type ComplexityRoot struct { } Query struct { - GetEpisodeOfCare func(childComplexity int, id string) int - GetMedicalData func(childComplexity int, patientID string) int - ListPatientEncounters func(childComplexity int, patientID string) int - PatientHealthTimeline func(childComplexity int, input dto.HealthTimelineInput) int - __resolve__service func(childComplexity int) int + GetEpisodeOfCare func(childComplexity int, id string) int + GetMedicalData func(childComplexity int, patientID string) int + GetPatientTemperatureEntries func(childComplexity int, patientID string) int + ListPatientEncounters func(childComplexity int, patientID string) int + PatientHealthTimeline func(childComplexity int, input dto.HealthTimelineInput) int + __resolve__service func(childComplexity int) int } TimelineResource struct { @@ -410,6 +411,7 @@ type QueryResolver interface { GetMedicalData(ctx context.Context, patientID string) (*dto.MedicalData, error) GetEpisodeOfCare(ctx context.Context, id string) (*dto.EpisodeOfCare, error) ListPatientEncounters(ctx context.Context, patientID string) ([]*dto.Encounter, error) + GetPatientTemperatureEntries(ctx context.Context, patientID string) ([]*dto.Observation, error) } type executableSchema struct { @@ -1993,6 +1995,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.GetMedicalData(childComplexity, args["patientID"].(string)), true + case "Query.getPatientTemperatureEntries": + if e.complexity.Query.GetPatientTemperatureEntries == nil { + break + } + + args, err := ec.field_Query_getPatientTemperatureEntries_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.GetPatientTemperatureEntries(childComplexity, args["patientID"].(string)), true + case "Query.listPatientEncounters": if e.complexity.Query.ListPatientEncounters == nil { break @@ -2177,6 +2191,9 @@ var sources = []*ast.Source{ # Encounter listPatientEncounters(patientID: String!): [Encounter!]! + + # Observation + getPatientTemperatureEntries(patientID: String!): [Observation!] } extend type Mutation { @@ -4484,6 +4501,21 @@ func (ec *executionContext) field_Query_getMedicalData_args(ctx context.Context, return args, nil } +func (ec *executionContext) field_Query_getPatientTemperatureEntries_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["patientID"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("patientID")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["patientID"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_listPatientEncounters_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -14516,6 +14548,72 @@ func (ec *executionContext) fieldContext_Query_listPatientEncounters(ctx context return fc, nil } +func (ec *executionContext) _Query_getPatientTemperatureEntries(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getPatientTemperatureEntries(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetPatientTemperatureEntries(rctx, fc.Args["patientID"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*dto.Observation) + fc.Result = res + return ec.marshalOObservation2ᚕᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐObservationᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getPatientTemperatureEntries(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Observation_id(ctx, field) + case "status": + return ec.fieldContext_Observation_status(ctx, field) + case "patientID": + return ec.fieldContext_Observation_patientID(ctx, field) + case "encounterID": + return ec.fieldContext_Observation_encounterID(ctx, field) + case "name": + return ec.fieldContext_Observation_name(ctx, field) + case "value": + return ec.fieldContext_Observation_value(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Observation", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_getPatientTemperatureEntries_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Query__service(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query__service(ctx, field) if err != nil { @@ -20599,6 +20697,26 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) } + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) + case "getPatientTemperatureEntries": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_getPatientTemperatureEntries(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + out.Concurrently(i, func() graphql.Marshaler { return rrm(innerCtx) }) @@ -22991,6 +23109,53 @@ func (ec *executionContext) marshalOObservation2ᚕᚖgithubᚗcomᚋsavannahghi return ret } +func (ec *executionContext) marshalOObservation2ᚕᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐObservationᚄ(ctx context.Context, sel ast.SelectionSet, v []*dto.Observation) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNObservation2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐObservation(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalOObservation2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐObservation(ctx context.Context, sel ast.SelectionSet, v *dto.Observation) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/pkg/clinical/repository/repository.go b/pkg/clinical/repository/repository.go index fccaa7fa..5eca1d7e 100644 --- a/pkg/clinical/repository/repository.go +++ b/pkg/clinical/repository/repository.go @@ -51,6 +51,7 @@ type FHIRObservation interface { SearchFHIRObservation(ctx context.Context, params map[string]interface{}, tenant dto.TenantIdentifiers) (*domain.FHIRObservationRelayConnection, error) CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) DeleteFHIRObservation(ctx context.Context, id string) (bool, error) + SearchPatientObservations(ctx context.Context, patientReference, observationCode string, tenant dto.TenantIdentifiers) ([]*domain.FHIRObservation, error) } type FHIRAllergyIntolerance interface { SearchFHIRAllergyIntolerance(ctx context.Context, params map[string]interface{}, tenant dto.TenantIdentifiers) (*domain.FHIRAllergyIntoleranceRelayConnection, error) diff --git a/pkg/clinical/usecases/clinical/observation.go b/pkg/clinical/usecases/clinical/observation.go index 865bcb11..dacabe3a 100644 --- a/pkg/clinical/usecases/clinical/observation.go +++ b/pkg/clinical/usecases/clinical/observation.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/google/uuid" "github.com/savannahghi/clinical/pkg/clinical/application/common" "github.com/savannahghi/clinical/pkg/clinical/application/dto" "github.com/savannahghi/clinical/pkg/clinical/domain" @@ -21,6 +22,11 @@ func (c *UseCasesClinicalImpl) RecordTemperature(ctx context.Context, input dto. return temperatureObservation, nil } +// GetPatientTemperatureEntries returns all the temperature entries for a patient, they are automatically sorted in chronological order +func (c *UseCasesClinicalImpl) GetPatientTemperatureEntries(ctx context.Context, patientID string) ([]*dto.Observation, error) { + return c.GetPatientObservations(ctx, patientID, common.TemperatureCIELTerminologyCode) +} + // RecordHeight records a patient's height and saves it to fhir func (c *UseCasesClinicalImpl) RecordHeight(ctx context.Context, input dto.ObservationInput) (*dto.Observation, error) { heightObservation, err := c.RecordObservation(ctx, input, common.HeightCIELTerminologyCode) @@ -160,3 +166,36 @@ func (c *UseCasesClinicalImpl) RecordObservation(ctx context.Context, input dto. return mapFHIRObservationToObservationDTO(fhirObservation.Resource), nil } + +// GetPatientObservations is a helper function used to fetch patient's observations based off the passed CIEL +// terminology code. The observations will be sorted in a chronological error +func (c *UseCasesClinicalImpl) GetPatientObservations(ctx context.Context, patientID string, observationCode string) ([]*dto.Observation, error) { + _, err := uuid.Parse(patientID) + if err != nil { + return nil, fmt.Errorf("invalid patient id: %s", patientID) + } + + _, err = c.infrastructure.FHIR.GetFHIRPatient(ctx, patientID) + if err != nil { + return nil, err + } + + patientReference := fmt.Sprintf("Patient/%s", patientID) + observations := []*dto.Observation{} + + identifiers, err := c.infrastructure.BaseExtension.GetTenantIdentifiers(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get tenant identifiers from context: %w", err) + } + + patientObs, err := c.infrastructure.FHIR.SearchPatientObservations(ctx, patientReference, observationCode, *identifiers) + if err != nil { + return nil, err + } + + for _, obs := range patientObs { + observations = append(observations, mapFHIRObservationToObservationDTO(obs)) + } + + return observations, nil +} diff --git a/pkg/clinical/usecases/clinical/observation_test.go b/pkg/clinical/usecases/clinical/observation_test.go index 1887db3d..0c093553 100644 --- a/pkg/clinical/usecases/clinical/observation_test.go +++ b/pkg/clinical/usecases/clinical/observation_test.go @@ -1271,3 +1271,155 @@ func TestUseCasesClinicalImpl_RecordBMI(t *testing.T) { }) } } + +func TestUseCasesClinicalImpl_GetPatientObservations(t *testing.T) { + ctx := context.Background() + type args struct { + ctx context.Context + patientID string + observationCode string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Happy Case - successfully get patient observations", + args: args{ + ctx: ctx, + patientID: uuid.New().String(), + observationCode: "1234", + }, + wantErr: false, + }, + { + name: "Sad Case - Invalid patient ID", + args: args{ + ctx: ctx, + observationCode: "1234", + }, + wantErr: true, + }, + { + name: "Sad Case - fail to get patient", + args: args{ + ctx: ctx, + patientID: uuid.New().String(), + observationCode: "1234", + }, + wantErr: true, + }, + { + name: "Sad Case - fail to get tenant identifiers", + args: args{ + ctx: ctx, + patientID: uuid.New().String(), + observationCode: "1234", + }, + wantErr: true, + }, + { + name: "Sad Case - fail to search patient observations", + args: args{ + ctx: ctx, + patientID: uuid.New().String(), + observationCode: "1234", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeExt := fakeExtMock.NewFakeBaseExtensionMock() + fakeFHIR := fakeFHIRMock.NewFHIRMock() + fakeOCL := fakeOCLMock.NewFakeOCLMock() + fakeMCH := fakeMyCarehubMock.NewFakeMyCareHubServiceMock() + + infra := infrastructure.NewInfrastructureInteractor(fakeExt, fakeFHIR, fakeOCL, fakeMCH) + u := clinicalUsecase.NewUseCasesClinicalImpl(infra) + + if tt.name == "Sad Case - fail to get patient" { + fakeFHIR.MockGetFHIRPatientFn = func(ctx context.Context, id string) (*domain.FHIRPatientRelayPayload, error) { + return nil, fmt.Errorf("failed to get patient") + } + } + + if tt.name == "Sad Case - fail to get tenant identifiers" { + fakeExt.MockGetTenantIdentifiersFn = func(ctx context.Context) (*dto.TenantIdentifiers, error) { + return nil, fmt.Errorf("failed to get tenant identifiers") + } + } + + if tt.name == "Sad Case - fail to search patient observations" { + fakeFHIR.MockSearchPatientObservationsFn = func(ctx context.Context, patientReference, conceptID string, tenant dto.TenantIdentifiers) ([]*domain.FHIRObservation, error) { + return nil, fmt.Errorf("failed to search patient observations") + } + } + + got, err := u.GetPatientObservations(tt.args.ctx, tt.args.patientID, tt.args.observationCode) + if (err != nil) != tt.wantErr { + t.Errorf("UseCasesClinicalImpl.GetPatientObservations() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if got == nil { + t.Errorf("expected a response but got %v", got) + return + } + } + }) + } +} + +func TestUseCasesClinicalImpl_GetPatientTemperatureEntries(t *testing.T) { + ctx := context.Background() + type args struct { + ctx context.Context + patientID string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Happy Case - Successfully get patient temperature entries", + args: args{ + ctx: ctx, + patientID: uuid.New().String(), + }, + wantErr: false, + }, + { + name: "Sad Case - Invalid patient ID", + args: args{ + ctx: ctx, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeExt := fakeExtMock.NewFakeBaseExtensionMock() + fakeFHIR := fakeFHIRMock.NewFHIRMock() + fakeOCL := fakeOCLMock.NewFakeOCLMock() + fakeMCH := fakeMyCarehubMock.NewFakeMyCareHubServiceMock() + + infra := infrastructure.NewInfrastructureInteractor(fakeExt, fakeFHIR, fakeOCL, fakeMCH) + u := clinicalUsecase.NewUseCasesClinicalImpl(infra) + + got, err := u.GetPatientTemperatureEntries(tt.args.ctx, tt.args.patientID) + if (err != nil) != tt.wantErr { + t.Errorf("UseCasesClinicalImpl.GetPatientTemperatureEntries() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if got == nil { + t.Errorf("expected a response but got %v", got) + return + } + } + }) + } +} diff --git a/pkg/clinical/usecases/usecases.go b/pkg/clinical/usecases/usecases.go index 4a6e4955..e2bf10c2 100644 --- a/pkg/clinical/usecases/usecases.go +++ b/pkg/clinical/usecases/usecases.go @@ -43,6 +43,9 @@ type Clinical interface { RecordBloodPressure(ctx context.Context, input dto.ObservationInput) (*dto.Observation, error) RecordBMI(ctx context.Context, input dto.ObservationInput) (*dto.Observation, error) RecordObservation(ctx context.Context, input dto.ObservationInput, vitalSignConceptID string) (*dto.Observation, error) + + GetPatientObservations(ctx context.Context, patientID string, observationCode string) ([]*dto.Observation, error) + GetPatientTemperatureEntries(ctx context.Context, patientID string) ([]*dto.Observation, error) } // Interactor is an implementation of the usecases interface