From 77398b287457bbe1abad2069a8b88fe6c1d71716 Mon Sep 17 00:00:00 2001 From: Jason Walonoski Date: Wed, 22 May 2024 14:36:32 -0400 Subject: [PATCH] Additional unit tests. --- .../mitre/synthea/world/agents/Provider.java | 2 +- .../payeradjustment/PayerAdjustmentFixed.java | 2 +- .../PayerAdjustmentRandom.java | 2 +- .../synthea/world/agents/ProviderTest.java | 15 +++ .../agents/behaviors/PlanFinderTest.java | 30 ++++++ .../payeradjustment/PayerAdjustmentTest.java | 102 ++++++++++++++++++ .../concepts/ClinicianSpecialtyTest.java | 21 ++++ 7 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentTest.java create mode 100644 src/test/java/org/mitre/synthea/world/concepts/ClinicianSpecialtyTest.java diff --git a/src/main/java/org/mitre/synthea/world/agents/Provider.java b/src/main/java/org/mitre/synthea/world/agents/Provider.java index 4e092fa8b5..776eb8417f 100644 --- a/src/main/java/org/mitre/synthea/world/agents/Provider.java +++ b/src/main/java/org/mitre/synthea/world/agents/Provider.java @@ -743,7 +743,7 @@ public static List getProviderList() { return new ArrayList(providerByUuid.values()); } - private void merge(Provider other) { + void merge(Provider other) { if (this.uuid == null) { this.uuid = other.uuid; } diff --git a/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentFixed.java b/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentFixed.java index 463e04de11..21875be304 100644 --- a/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentFixed.java +++ b/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentFixed.java @@ -15,7 +15,7 @@ public class PayerAdjustmentFixed implements IPayerAdjustment, Serializable { private static final long serialVersionUID = -1515831606680338099L; /** Fixed adjustment rate. */ - private double rate; + double rate; /** * Create a new fixed payer adjustment. diff --git a/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentRandom.java b/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentRandom.java index d40e4c4119..30da0c18fe 100644 --- a/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentRandom.java +++ b/src/main/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentRandom.java @@ -16,7 +16,7 @@ public class PayerAdjustmentRandom implements IPayerAdjustment, Serializable { private static final long serialVersionUID = -5292509643177122361L; /** Maximum adjustment rate. */ - private double rate; + double rate; /** * Create a new random payer adjustment. diff --git a/src/test/java/org/mitre/synthea/world/agents/ProviderTest.java b/src/test/java/org/mitre/synthea/world/agents/ProviderTest.java index a0aa9a2303..29726f9c79 100644 --- a/src/test/java/org/mitre/synthea/world/agents/ProviderTest.java +++ b/src/test/java/org/mitre/synthea/world/agents/ProviderTest.java @@ -238,4 +238,19 @@ public void testAllFiles() throws Exception { public void testNPICreation() { Assert.assertEquals("1234567893", Provider.toNPI(123_456_789L)); } + + @Test + public void testProviderMerge() { + Provider.loadProviders(city, providerRandom); + Person person = new Person(0L); + city.assignPoint(person, city.randomCityName(person)); + Provider provider = Provider.findService(person, EncounterType.OUTPATIENT, 0); + Assert.assertNotNull(provider); + Provider blank = new Provider(); + blank.uuid = null; + blank.merge(provider); + Assert.assertEquals(provider.uuid, blank.uuid); + Assert.assertEquals(provider.name, blank.name); + Assert.assertEquals(provider.servicesProvided, blank.servicesProvided); + } } diff --git a/src/test/java/org/mitre/synthea/world/agents/behaviors/PlanFinderTest.java b/src/test/java/org/mitre/synthea/world/agents/behaviors/PlanFinderTest.java index 2cdc8bea0f..5df3fbd8e4 100644 --- a/src/test/java/org/mitre/synthea/world/agents/behaviors/PlanFinderTest.java +++ b/src/test/java/org/mitre/synthea/world/agents/behaviors/PlanFinderTest.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.junit.AfterClass; @@ -23,11 +24,16 @@ import org.mitre.synthea.world.agents.behaviors.planfinder.PlanFinderBestRates; import org.mitre.synthea.world.agents.behaviors.planfinder.PlanFinderPriority; import org.mitre.synthea.world.agents.behaviors.planfinder.PlanFinderRandom; +import org.mitre.synthea.world.concepts.HealthRecord; +import org.mitre.synthea.world.concepts.HealthRecord.Code; +import org.mitre.synthea.world.concepts.HealthRecord.Encounter; +import org.mitre.synthea.world.concepts.HealthRecord.EncounterType; import org.mitre.synthea.world.concepts.healthinsurance.InsurancePlan; import org.mitre.synthea.world.geography.Location; public class PlanFinderTest { + private static final Code code = new Code("system", "code", "display"); private Person person; private Location location; @@ -126,6 +132,30 @@ public void onePayerBestRate() { assertFalse(payer.isNoInsurance()); } + @Test + public void onePayerBestRateMultipleRecordsAndEncounters() { + person.hasMultipleRecords = true; + person.records = new ConcurrentHashMap(); + person.records.put("provider", person.record); + person.coverage.setPlanToNoInsurance(0L); + for (long time = 0L; time <= 3000L; time += 1000L) { + Encounter encounter = person.record.encounterStart(time, EncounterType.EMERGENCY); + encounter.codes.add(code); + person.record.encounterEnd((time + 500L), EncounterType.EMERGENCY); + } + + Config.set("generate.payers.selection_behavior", "best_rate"); + PayerManager.clear(); + PayerManager.loadPayers(location); + PlanFinderBestRates finder = new PlanFinderBestRates(); + List privatePayers = PayerManager.getAllPayers().stream().filter(payer -> payer + .getOwnership().equals(PayerManager.PRIVATE_OWNERSHIP)).collect(Collectors.toList()); + Payer payer = finder.find(PayerManager.getActivePlans(privatePayers, 0L), + person, null, 0L).getPayer(); + assertNotNull(payer); + assertFalse(payer.isNoInsurance()); + } + @Test public void planFinderPriority() { long time = Utilities.convertCalendarYearsToTime(2023); diff --git a/src/test/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentTest.java b/src/test/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentTest.java new file mode 100644 index 0000000000..3315376a90 --- /dev/null +++ b/src/test/java/org/mitre/synthea/world/agents/behaviors/payeradjustment/PayerAdjustmentTest.java @@ -0,0 +1,102 @@ +package org.mitre.synthea.world.agents.behaviors.payeradjustment; + +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; + +import org.junit.Test; +import org.mitre.synthea.world.agents.PayerManager; +import org.mitre.synthea.world.agents.Person; +import org.mitre.synthea.world.concepts.Claim; +import org.mitre.synthea.world.concepts.HealthRecord.Code; +import org.mitre.synthea.world.concepts.HealthRecord.Encounter; +import org.mitre.synthea.world.concepts.HealthRecord.EncounterType; +import org.mockito.Mockito; + +public class PayerAdjustmentTest { + + private static final Code code = new Code("system", "code", "display"); + + @Test + public void testPayerAdjustmentNone() { + PayerManager.loadNoInsurance(); + Person person = new Person(0L); + person.attributes.put(Person.BIRTHDATE, 0L); + person.coverage.setPlanToNoInsurance(0L); + Encounter encounter = person.record.encounterStart(0L, EncounterType.EMERGENCY); + encounter.codes.add(code); + Claim claim = new Claim(encounter, person); + claim.assignCosts(); + + IPayerAdjustment adjustment = new PayerAdjustmentNone(); + BigDecimal result = adjustment.adjustClaim(claim.mainEntry, person); + assertTrue("Adjustment should be zero.", result.equals(Claim.ZERO_CENTS)); + } + + @Test + public void testPayerAdjustmentFixed() { + PayerManager.loadNoInsurance(); + Person person = new Person(0L); + person.attributes.put(Person.BIRTHDATE, 0L); + person.coverage.setPlanToNoInsurance(0L); + Encounter encounter = person.record.encounterStart(0L, EncounterType.EMERGENCY); + encounter.codes.add(code); + Claim claim = new Claim(encounter, person); + claim.assignCosts(); + + IPayerAdjustment adjustment = new PayerAdjustmentFixed(0.5); + BigDecimal result = adjustment.adjustClaim(claim.mainEntry, person); + assertTrue("Adjustment should be non-zero", result.compareTo(claim.mainEntry.cost) < 0); + + adjustment = new PayerAdjustmentFixed(1.0); + result = adjustment.adjustClaim(claim.mainEntry, person); + assertTrue("Adjustment should be total", result.equals(claim.mainEntry.cost)); + + adjustment = new PayerAdjustmentFixed(0.0); + result = adjustment.adjustClaim(claim.mainEntry, person); + assertTrue("Adjustment should be zero", result.equals(Claim.ZERO_CENTS)); + } + + @Test + public void testPayerAdjustmentRandom() { + PayerManager.loadNoInsurance(); + Person person = new Person(0L); + person.attributes.put(Person.BIRTHDATE, 0L); + person.coverage.setPlanToNoInsurance(0L); + Encounter encounter = person.record.encounterStart(0L, EncounterType.EMERGENCY); + encounter.codes.add(code); + Claim claim = new Claim(encounter, person); + claim.assignCosts(); + + Person shadow = Mockito.mock(Person.class); + Mockito.when(shadow.rand(0.0, 0.5)).thenReturn(0.5); + Mockito.when(shadow.randBoolean()).thenReturn(true); + + IPayerAdjustment adjustment = new PayerAdjustmentRandom(0.5); + BigDecimal result = adjustment.adjustClaim(claim.mainEntry, shadow); + assertTrue("Adjustment should be non-zero", result.compareTo(claim.mainEntry.cost) < 0); + + Mockito.when(shadow.randBoolean()).thenReturn(false); + result = adjustment.adjustClaim(claim.mainEntry, shadow); + assertTrue("Adjustment should be zero", result.equals(Claim.ZERO_CENTS)); + } + + @Test + public void testRateBounds() { + // Below zero + PayerAdjustmentFixed adjustment = new PayerAdjustmentFixed(-1); + assertTrue("Below zero rate should be zero.", (adjustment.rate == 0)); + + // Above one + adjustment = new PayerAdjustmentFixed(2); + assertTrue("Rates above one should be one.", (adjustment.rate == 1)); + + // Below zero + PayerAdjustmentRandom randomAdjustment = new PayerAdjustmentRandom(-1); + assertTrue("Below zero rate should be zero.", (randomAdjustment.rate == 0)); + + // Above one + randomAdjustment = new PayerAdjustmentRandom(2); + assertTrue("Rates above one should be one.", (randomAdjustment.rate == 1)); + } +} diff --git a/src/test/java/org/mitre/synthea/world/concepts/ClinicianSpecialtyTest.java b/src/test/java/org/mitre/synthea/world/concepts/ClinicianSpecialtyTest.java new file mode 100644 index 0000000000..4f36cf6a3c --- /dev/null +++ b/src/test/java/org/mitre/synthea/world/concepts/ClinicianSpecialtyTest.java @@ -0,0 +1,21 @@ +package org.mitre.synthea.world.concepts; + +import org.junit.Assert; +import org.junit.Test; + +public class ClinicianSpecialtyTest { + + @Test + public void testSpecialtiesMap() { + String[] specialties = ClinicianSpecialty.getSpecialties(); + Assert.assertNotNull("Clinician Specialties should not be null.", specialties); + Assert.assertTrue("Clinician Specialities should not be empty.", specialties.length > 0); + + for (String specialty : specialties) { + String code = ClinicianSpecialty.getCMSProviderSpecialtyCode(specialty); + Assert.assertNotNull("CMS Specialty code should not be null.", code); + String msg = specialty + " => " + code; + Assert.assertTrue("CMS Speciality code should be length 2: " + msg, code.length() == 2); + } + } +}