ObsTest.java

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.text.DateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import org.junit.Assert;
import org.junit.Test;
import org.openmrs.api.APIException;
import org.openmrs.test.Verifies;

/**
 * This class tests all methods that are not getter or setters in the Obs java object TODO: finish
 * this test class for Obs
 * 
 * @see Obs
 */
public class ObsTest {
	
	/**
	 * Tests the addToGroup method in ObsGroup
	 * 
	 * @throws Exception
	 */
	@Test
	public void shouldAddandRemoveObsToGroup() throws Exception {
		
		Obs obs = new Obs(1);
		
		Obs obsGroup = new Obs(755);
		
		// These methods should not fail even with null attributes on the obs
		assertFalse(obsGroup.isObsGrouping());
		assertFalse(obsGroup.hasGroupMembers(false));
		assertFalse(obsGroup.hasGroupMembers(true)); //Check both flags for false
		
		// adding an obs when the obs group has no other obs
		// should not throw an error
		obsGroup.addGroupMember(obs);
		assertEquals(1, obsGroup.getGroupMembers().size());
		
		// check duplicate add. should only be one
		obsGroup.addGroupMember(obs);
		assertTrue(obsGroup.hasGroupMembers(false));
		assertEquals("Duplicate add should not increase the grouped obs size", 1, obsGroup.getGroupMembers().size());
		
		Obs obs2 = new Obs(2);
		
		obsGroup.removeGroupMember(obs2);
		assertTrue(obsGroup.hasGroupMembers(false));
		assertEquals("Removing a non existent obs should not decrease the number of grouped obs", 1, obsGroup
		        .getGroupMembers().size());
		
		// testing removing an obs from a group that has a null obs list
		new Obs().removeGroupMember(obs2);
		
		obsGroup.removeGroupMember(obs);
		
		assertEquals(0, obsGroup.getGroupMembers().size());
		
		// try to add an obs group to itself
		try {
			obsGroup.addGroupMember(obsGroup);
			fail("An APIException about adding an obsGroup should have been thrown");
		}
		catch (APIException e) {
			// this exception is expected
		}
	}
	
	/**
	 * tests the getRelatedObservations method:
	 */
	@Test
	public void shouldGetRelatedObservations() throws Exception {
		// create a child Obs
		Obs o = new Obs();
		o.setDateCreated(new Date());
		o.setLocation(new Location(1));
		o.setObsDatetime(new Date());
		o.setPerson(new Patient(2));
		o.setValueText("childObs");
		
		//create its sibling
		Obs oSibling = new Obs();
		oSibling.setDateCreated(new Date());
		oSibling.setLocation(new Location(1));
		oSibling.setObsDatetime(new Date());
		oSibling.setValueText("childObs2");
		oSibling.setPerson(new Patient(2));
		
		//create a parent Obs
		Obs oParent = new Obs();
		oParent.setDateCreated(new Date());
		oParent.setLocation(new Location(1));
		oParent.setObsDatetime(new Date());
		oSibling.setValueText("parentObs");
		oParent.setPerson(new Patient(2));
		
		//create a grandparent obs
		Obs oGrandparent = new Obs();
		oGrandparent.setDateCreated(new Date());
		oGrandparent.setLocation(new Location(1));
		oGrandparent.setObsDatetime(new Date());
		oGrandparent.setPerson(new Patient(2));
		oSibling.setValueText("grandParentObs");
		
		oParent.addGroupMember(o);
		oParent.addGroupMember(oSibling);
		oGrandparent.addGroupMember(oParent);
		
		//create a leaf observation at the grandparent level
		Obs o2 = new Obs();
		o2.setDateCreated(new Date());
		o2.setLocation(new Location(1));
		o2.setObsDatetime(new Date());
		o2.setPerson(new Patient(2));
		o2.setValueText("grandparentLeafObs");
		
		oGrandparent.addGroupMember(o2);
		
		/**
		 * test to make sure that if the original child obs calls getRelatedObservations, it returns
		 * itself and its siblings: original obs is one of two groupMembers, so relatedObservations
		 * should return a size of set 2 then, make sure that if oParent calls
		 * getRelatedObservations, it returns its own children as well as the leaf obs attached to
		 * the grandparentObs oParent has two members, and one leaf ancestor -- so a set of size 3
		 * should be returned.
		 */
		assertEquals(o.getRelatedObservations().size(), 2);
		assertEquals(oParent.getRelatedObservations().size(), 3);
		
		// create  a great-grandparent obs
		Obs oGGP = new Obs();
		oGGP.setDateCreated(new Date());
		oGGP.setLocation(new Location(1));
		oGGP.setObsDatetime(new Date());
		oGGP.setPerson(new Patient(2));
		oGGP.setValueText("grandParentObs");
		oGGP.addGroupMember(oGrandparent);
		
		//create a leaf great-grandparent obs
		Obs oGGPleaf = new Obs();
		oGGPleaf.setDateCreated(new Date());
		oGGPleaf.setLocation(new Location(1));
		oGGPleaf.setObsDatetime(new Date());
		oGGPleaf.setPerson(new Patient(2));
		oGGPleaf.setValueText("grandParentObs");
		oGGP.addGroupMember(oGGPleaf);
		
		/**
		 * now run the previous assertions again. this time there are two ancestor leaf obs, so the
		 * first assertion should still return a set of size 2, but the second assertion sould
		 * return a set of size 4.
		 */
		assertEquals(o.getRelatedObservations().size(), 2);
		assertEquals(oParent.getRelatedObservations().size(), 4);
		
		//remove the grandparent leaf observation:
		
		oGrandparent.removeGroupMember(o2);
		
		//now the there is only one ancestor leaf obs:
		assertEquals(o.getRelatedObservations().size(), 2);
		assertEquals(oParent.getRelatedObservations().size(), 3);
		
		/**
		 * finally, test a non-obsGroup and non-member Obs to the function Obs o2 is now not
		 * connected to our heirarchy: an empty set should be returned:
		 */
		
		assertNotNull(o2.getRelatedObservations());
		assertEquals(o2.getRelatedObservations().size(), 0);
		
	}
	
	/**
	 * @see {@link Obs#isComplex()}
	 */
	@Test
	@Verifies(value = "should return true if the concept is complex", method = "isComplex()")
	public void isComplex_shouldReturnTrueIfTheConceptIsComplex() throws Exception {
		ConceptDatatype cd = new ConceptDatatype();
		cd.setName("Complex");
		cd.setHl7Abbreviation("ED");
		
		ConceptComplex complexConcept = new ConceptComplex();
		complexConcept.setDatatype(cd);
		
		Obs obs = new Obs();
		obs.setConcept(complexConcept);
		
		Assert.assertTrue(obs.isComplex());
	}
	
	/**
	 * @see {@link Obs#setValueAsString(String)}
	 */
	@Test(expected = RuntimeException.class)
	@Verifies(value = "should fail if the value of the string is empty", method = "setValueAsString(String)")
	public void setValueAsString_shouldFailIfTheValueOfTheStringIsEmpty() throws Exception {
		Obs obs = new Obs();
		obs.setValueAsString("");
	}
	
	/**
	 * @see {@link Obs#setValueAsString(String)}
	 */
	@Test(expected = RuntimeException.class)
	@Verifies(value = "should fail if the value of the string is null", method = "setValueAsString(String)")
	public void setValueAsString_shouldFailIfTheValueOfTheStringIsNull() throws Exception {
		Obs obs = new Obs();
		obs.setValueAsString(null);
	}
	
	/**
	 * @see {@link Obs#getValueAsBoolean()}
	 */
	@Test
	@Verifies(value = "should return false for value_numeric concepts if value is 0", method = "getValueAsBoolean()")
	public void getValueAsBoolean_shouldReturnFalseForValue_numericConceptsIfValueIs0() throws Exception {
		Obs obs = new Obs();
		obs.setValueNumeric(0.0);
		Assert.assertEquals(false, obs.getValueAsBoolean());
	}
	
	/**
	 * @see {@link Obs#getValueAsBoolean()}
	 */
	@Test
	@Verifies(value = "should return null for value_numeric concepts if value is neither 1 nor 0", method = "getValueAsBoolean()")
	public void getValueAsBoolean_shouldReturnNullForValue_numericConceptsIfValueIsNeither1Nor0() throws Exception {
		Obs obs = new Obs();
		obs.setValueNumeric(24.8);
		Assert.assertNull(obs.getValueAsBoolean());
	}
	
	@Test
	@Verifies(value = "should return non precise values for NumericConcepts", method = "getValueAsString(Locale)")
	public void getValueAsString_shouldReturnNonPreciseValuesForNumericConcepts() throws Exception {
		Obs obs = new Obs();
		obs.setValueNumeric(25.125);
		ConceptNumeric cn = new ConceptNumeric();
		ConceptDatatype cdt = new ConceptDatatype();
		cdt.setHl7Abbreviation("NM");
		cn.setDatatype(cdt);
		cn.setPrecise(false);
		obs.setConcept(cn);
		String str = "25";
		Assert.assertEquals(str, obs.getValueAsString(Locale.US));
	}
	
	@Test
	@Verifies(value = "should return proper DateFormat", method = "getValueAsString()")
	public void getValueAsString_shouldReturnProperDateFormat() throws Exception {
		Obs obs = new Obs();
		obs.setValueDatetime(new Date());
		Concept cn = new Concept();
		ConceptDatatype cdt = new ConceptDatatype();
		cdt.setHl7Abbreviation("DT");
		cn.setDatatype(cdt);
		obs.setConcept(cn);
		
		Date utilDate = new Date();
		DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
		String dateString = dateFormat.format(utilDate);
		Assert.assertEquals(dateString, obs.getValueAsString(Locale.US));
	}
	
	/**
	 * @see {@link Obs#getValueAsBoolean()}
	 */
	@Test
	@Verifies(value = "should return true for value_numeric concepts if value is 1", method = "getValueAsBoolean()")
	public void getValueAsBoolean_shouldReturnTrueForValue_numericConceptsIfValueIs1() throws Exception {
		Obs obs = new Obs();
		obs.setValueNumeric(1.0);
		Assert.assertEquals(true, obs.getValueAsBoolean());
	}
	
	/**
	 * @see Obs#getGroupMembers(boolean)
	 * @verifies Get all group members if passed true, and non-voided if passed false
	 */
	@Test
	public void getGroupMembers_shouldGetAllGroupMembersIfPassedTrueAndNonvoidedIfPassedFalse() throws Exception {
		Obs parent = new Obs(1);
		Set<Obs> members = new HashSet<Obs>();
		members.add(new Obs(101));
		members.add(new Obs(103));
		Obs voided = new Obs(99);
		voided.setVoided(true);
		members.add(voided);
		parent.setGroupMembers(members);
		members = parent.getGroupMembers(true);
		assertEquals("set of all members should have length of 3", 3, members.size());
		members = parent.getGroupMembers(false);
		assertEquals("set of non-voided should have length of 2", 2, members.size());
		members = parent.getGroupMembers(); //should be same as false
		assertEquals("default should return non-voided with length of 2", 2, members.size());
	}
	
	/**
	 * @see Obs#hasGroupMembers(boolean)
	 * @verifies return true if this obs has group members based on parameter
	 */
	@Test
	public void hasGroupMembers_shouldReturnTrueIfThisObsHasGroupMembersBasedOnParameter() throws Exception {
		Obs parent = new Obs(5);
		Obs child = new Obs(33);
		child.setVoided(true);
		parent.addGroupMember(child); //Only contains 1 voided child
		assertTrue("When checking for all members, should return true", parent.hasGroupMembers(true));
		assertFalse("When checking for non-voided, should return false", parent.hasGroupMembers(false));
		assertFalse("Default should check for non-voided", parent.hasGroupMembers());
	}
	
	/**
	 * @see Obs#isObsGrouping()
	 * @verifies ignore voided Obs
	 */
	@Test
	public void isObsGrouping_shouldIncludeVoidedObs() throws Exception {
		Obs parent = new Obs(5);
		Obs child = new Obs(33);
		child.setVoided(true);
		parent.addGroupMember(child);
		assertTrue("When checking for Obs grouping, should include voided Obs", parent.isObsGrouping());
	}
}