Files
FHIR-Sandbox/app/db/clinical_test.go

346 lines
9.1 KiB
Go
Raw Normal View History

package db_test
// clinical_test.go — tests for clinical resource persistence (Observation,
// Condition, DocumentReference, PatientSync).
import (
"testing"
"github.com/AmanTahiliani/FHIR-Sandbox/app/models"
)
const (
testEHRURL = "https://ehr.example.com/fhir"
testPatientID = "patient-clinical-001"
)
// ---------------------------------------------------------------------------
// Observation tests
// ---------------------------------------------------------------------------
func TestUpsertObservation_NewAndUpdate(t *testing.T) {
store := newTestStore(t)
qty := 98.6
obs := &models.Observation{
FHIRID: "obs-001",
EHRURL: testEHRURL,
PatientFHIRID: testPatientID,
Status: "final",
Category: "vital-signs",
CodeText: "Body Temperature",
CodeSystem: "http://loinc.org",
CodeCode: "8310-5",
EffectiveDate: "2024-01-15",
ValueQuantity: &qty,
ValueUnit: "°F",
}
id1, err := store.UpsertObservation(obs)
if err != nil {
t.Fatalf("initial UpsertObservation: %v", err)
}
if id1 == "" {
t.Fatal("expected non-empty ID")
}
// Update: change value and status.
newQty := 99.1
obs.ValueQuantity = &newQty
obs.Status = "amended"
id2, err := store.UpsertObservation(obs)
if err != nil {
t.Fatalf("update UpsertObservation: %v", err)
}
// ID must be stable across upserts.
if id1 != id2 {
t.Errorf("ID changed on upsert: was %q, got %q", id1, id2)
}
// Read back and confirm updated fields.
rows, err := store.ListObservations(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("ListObservations: %v", err)
}
if len(rows) != 1 {
t.Fatalf("expected 1 observation, got %d", len(rows))
}
got := rows[0]
if got.Status != "amended" {
t.Errorf("Status: got %q, want %q", got.Status, "amended")
}
if got.ValueQuantity == nil || *got.ValueQuantity != 99.1 {
t.Errorf("ValueQuantity: got %v, want 99.1", got.ValueQuantity)
}
}
func TestUpsertObservation_TenantIsolation(t *testing.T) {
store := newTestStore(t)
makeObs := func(ehrURL string) *models.Observation {
return &models.Observation{
FHIRID: "obs-shared",
EHRURL: ehrURL,
PatientFHIRID: testPatientID,
Status: "final",
}
}
id1, err := store.UpsertObservation(makeObs("https://ehr-a.example.com/fhir"))
if err != nil {
t.Fatalf("upsert A: %v", err)
}
id2, err := store.UpsertObservation(makeObs("https://ehr-b.example.com/fhir"))
if err != nil {
t.Fatalf("upsert B: %v", err)
}
if id1 == id2 {
t.Error("expected different IDs for same fhir_id at different ehr_urls")
}
}
func TestListObservations_Empty(t *testing.T) {
store := newTestStore(t)
rows, err := store.ListObservations("no-such-patient", testEHRURL)
if err != nil {
t.Fatalf("ListObservations: %v", err)
}
if len(rows) != 0 {
t.Errorf("expected 0 rows, got %d", len(rows))
}
}
func TestListObservations_OrderedNewestFirst(t *testing.T) {
store := newTestStore(t)
for _, item := range []struct {
id string
date string
}{
{"obs-a", "2024-01-01"},
{"obs-b", "2024-06-15"},
{"obs-c", "2023-12-31"},
} {
_, err := store.UpsertObservation(&models.Observation{
FHIRID: item.id,
EHRURL: testEHRURL,
PatientFHIRID: testPatientID,
Status: "final",
EffectiveDate: item.date,
})
if err != nil {
t.Fatalf("UpsertObservation %s: %v", item.id, err)
}
}
rows, err := store.ListObservations(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("ListObservations: %v", err)
}
if len(rows) != 3 {
t.Fatalf("expected 3 rows, got %d", len(rows))
}
// Newest first: obs-b (Jun) > obs-a (Jan) > obs-c (Dec 2023)
if rows[0].FHIRID != "obs-b" {
t.Errorf("first row: got %q, want obs-b", rows[0].FHIRID)
}
if rows[2].FHIRID != "obs-c" {
t.Errorf("last row: got %q, want obs-c", rows[2].FHIRID)
}
}
// ---------------------------------------------------------------------------
// Condition tests
// ---------------------------------------------------------------------------
func TestUpsertCondition_NewAndUpdate(t *testing.T) {
store := newTestStore(t)
cond := &models.Condition{
FHIRID: "cond-001",
EHRURL: testEHRURL,
PatientFHIRID: testPatientID,
ClinicalStatus: "active",
VerificationStatus: "confirmed",
Category: "problem-list-item",
CodeText: "Hypertension",
CodeSystem: "http://snomed.info/sct",
CodeCode: "38341003",
OnsetDate: "2020-03-01",
RecordedDate: "2020-03-05",
}
id1, err := store.UpsertCondition(cond)
if err != nil {
t.Fatalf("initial UpsertCondition: %v", err)
}
// Update clinical status to resolved.
cond.ClinicalStatus = "resolved"
id2, err := store.UpsertCondition(cond)
if err != nil {
t.Fatalf("update UpsertCondition: %v", err)
}
if id1 != id2 {
t.Errorf("ID changed on upsert: was %q, got %q", id1, id2)
}
rows, err := store.ListConditions(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("ListConditions: %v", err)
}
if len(rows) != 1 {
t.Fatalf("expected 1 condition, got %d", len(rows))
}
if rows[0].ClinicalStatus != "resolved" {
t.Errorf("ClinicalStatus: got %q, want resolved", rows[0].ClinicalStatus)
}
}
func TestListConditions_Empty(t *testing.T) {
store := newTestStore(t)
rows, err := store.ListConditions("no-such-patient", testEHRURL)
if err != nil {
t.Fatalf("ListConditions: %v", err)
}
if len(rows) != 0 {
t.Errorf("expected 0, got %d", len(rows))
}
}
// ---------------------------------------------------------------------------
// DocumentReference tests
// ---------------------------------------------------------------------------
func TestUpsertDocumentReference_NewAndUpdate(t *testing.T) {
store := newTestStore(t)
doc := &models.DocumentReference{
FHIRID: "doc-001",
EHRURL: testEHRURL,
PatientFHIRID: testPatientID,
Status: "current",
DocStatus: "final",
TypeText: "Discharge Summary",
TypeSystem: "http://loinc.org",
TypeCode: "18842-5",
Date: "2024-02-10",
Description: "Hospital discharge summary",
ContentType: "text/plain",
ContentURL: "https://ehr.example.com/fhir/Binary/bin-001",
}
id1, err := store.UpsertDocumentReference(doc)
if err != nil {
t.Fatalf("UpsertDocumentReference: %v", err)
}
// Supersede the document.
doc.Status = "superseded"
id2, err := store.UpsertDocumentReference(doc)
if err != nil {
t.Fatalf("update UpsertDocumentReference: %v", err)
}
if id1 != id2 {
t.Errorf("ID changed on upsert: was %q, got %q", id1, id2)
}
rows, err := store.ListDocumentReferences(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("ListDocumentReferences: %v", err)
}
if len(rows) != 1 {
t.Fatalf("expected 1 doc, got %d", len(rows))
}
if rows[0].Status != "superseded" {
t.Errorf("Status: got %q, want superseded", rows[0].Status)
}
}
func TestListDocumentReferences_Empty(t *testing.T) {
store := newTestStore(t)
rows, err := store.ListDocumentReferences("no-such-patient", testEHRURL)
if err != nil {
t.Fatalf("ListDocumentReferences: %v", err)
}
if len(rows) != 0 {
t.Errorf("expected 0, got %d", len(rows))
}
}
// ---------------------------------------------------------------------------
// PatientSync tests
// ---------------------------------------------------------------------------
func TestRecordAndLatestSync(t *testing.T) {
store := newTestStore(t)
// No sync yet.
ps, err := store.LatestSync(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("LatestSync (empty): %v", err)
}
if ps != nil {
t.Errorf("expected nil before any sync, got %+v", ps)
}
// Record first sync.
recorded, err := store.RecordSync(testPatientID, testEHRURL, 10, 3, 1)
if err != nil {
t.Fatalf("RecordSync: %v", err)
}
if recorded.ID == "" {
t.Fatal("expected non-empty ID")
}
if recorded.ObsCount != 10 || recorded.CondCount != 3 || recorded.DocCount != 1 {
t.Errorf("counts wrong: %+v", recorded)
}
// LatestSync should now return that record.
latest, err := store.LatestSync(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("LatestSync: %v", err)
}
if latest == nil {
t.Fatal("expected non-nil latest sync")
}
if latest.ID != recorded.ID {
t.Errorf("ID mismatch: got %q, want %q", latest.ID, recorded.ID)
}
// Record a second sync with higher counts; LatestSync must return the newer one.
_, err = store.RecordSync(testPatientID, testEHRURL, 20, 5, 2)
if err != nil {
t.Fatalf("RecordSync second: %v", err)
}
latest2, err := store.LatestSync(testPatientID, testEHRURL)
if err != nil {
t.Fatalf("LatestSync second: %v", err)
}
if latest2.ObsCount != 20 {
t.Errorf("expected ObsCount 20 from latest, got %d", latest2.ObsCount)
}
}
func TestLatestSync_TenantIsolation(t *testing.T) {
store := newTestStore(t)
_, err := store.RecordSync(testPatientID, "https://ehr-a.example.com/fhir", 5, 1, 0)
if err != nil {
t.Fatalf("RecordSync EHR-A: %v", err)
}
// Querying for a different EHR URL should return nil.
ps, err := store.LatestSync(testPatientID, "https://ehr-b.example.com/fhir")
if err != nil {
t.Fatalf("LatestSync EHR-B: %v", err)
}
if ps != nil {
t.Errorf("expected nil for different EHR URL, got %+v", ps)
}
}