Add a test

This commit is contained in:
James 2017-10-06 08:56:35 -04:00
parent ce720f5601
commit ff85503acb
3 changed files with 179 additions and 83 deletions

View File

@ -89,7 +89,7 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
searchParam = new SearchParam();
searchParam.setResourceName(resourceType);
searchParam.setParamName(paramName);
searchParam = mySearchParamDao.saveAndFlush(searchParam);
searchParam = mySearchParamDao.save(searchParam);
ourLog.info("Added search param {} with pid {}", paramName, searchParam.getId());
// Don't add the newly saved entity to the map in case the save fails
}

View File

@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.ResourceTag;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
@ -37,9 +38,12 @@ import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
@ -62,43 +66,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@Test
public void testTransactionWhichFailsPersistsNothing() {
// Run a transaction which points to that practitioner
// in a field that isn't allowed to refer to a practitioner
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
Patient pt = new Patient();
pt.setId("PT");
pt.setActive(true);
pt.addName().setFamily("FAMILY");
input.addEntry()
.setResource(pt)
.getRequest().setMethod(HTTPVerb.PUT).setUrl("Patient/PT");
Observation obs = new Observation();
obs.setId("OBS");
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.addPerformer().setReference("Practicioner/AAAAA");
input.addEntry()
.setResource(obs)
.getRequest().setMethod(HTTPVerb.PUT).setUrl("Observation/OBS");
try {
mySystemDao.transaction(mySrd, input);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Resource type 'Practicioner' is not valid for this path"));
}
assertThat(myResourceTableDao.findAll(), empty());
assertThat(myResourceIndexedSearchParamStringDao.findAll(), empty());
}
private Bundle createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb theVerb) {
Patient pat = new Patient();
@ -209,6 +176,11 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
return null;
}
private Bundle loadBundle(String theFileName) throws IOException {
String req = IOUtils.toString(FhirSystemDaoDstu3Test.class.getResourceAsStream(theFileName), StandardCharsets.UTF_8);
return myFhirCtx.newXmlParser().parseResource(Bundle.class, req);
}
@Test
public void testBatchCreateWithBadRead() {
Bundle request = new Bundle();
@ -1222,8 +1194,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
@Test
public void testTransactionCreateWithPutUsingUrl2() throws Exception {
String req = IOUtils.toString(FhirSystemDaoDstu3Test.class.getResourceAsStream("/bundle-dstu3.xml"), StandardCharsets.UTF_8);
Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, req);
Bundle request = loadBundle("/bundle-dstu3.xml");
mySystemDao.transaction(mySrd, request);
}
@ -1702,13 +1673,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
//@formatter:off
/*
* Transaction Order, per the spec:
*
*
* Process any DELETE interactions
* Process any POST interactions
* Process any PUT interactions
* Process any GET interactions
*
* This test creates a transaction bundle that includes
*
* This test creates a transaction bundle that includes
* these four operations in the reverse order and verifies
* that they are invoked correctly.
*/
@ -2147,6 +2118,42 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
}
}
@Test
public void testTransactionWhichFailsPersistsNothing() {
// Run a transaction which points to that practitioner
// in a field that isn't allowed to refer to a practitioner
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
Patient pt = new Patient();
pt.setId("PT");
pt.setActive(true);
pt.addName().setFamily("FAMILY");
input.addEntry()
.setResource(pt)
.getRequest().setMethod(HTTPVerb.PUT).setUrl("Patient/PT");
Observation obs = new Observation();
obs.setId("OBS");
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.addPerformer().setReference("Practicioner/AAAAA");
input.addEntry()
.setResource(obs)
.getRequest().setMethod(HTTPVerb.PUT).setUrl("Observation/OBS");
try {
mySystemDao.transaction(mySrd, input);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Resource type 'Practicioner' is not valid for this path"));
}
assertThat(myResourceTableDao.findAll(), empty());
assertThat(myResourceIndexedSearchParamStringDao.findAll(), empty());
}
/**
* Format changed, source isn't valid
*/
@ -2815,6 +2822,92 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
}
@Test
public void testMultipleConcurrentWritesToSameResource() throws InterruptedException {
ThreadPoolExecutor exec = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
final AtomicInteger errors = new AtomicInteger();
List<Future> futures = new ArrayList<>();
for (int i = 0; i < 50; i++) {
final Patient p = new Patient();
p.setId("PID");
p.setActive(true);
p.setBirthDate(new Date());
p.addIdentifier().setSystem("foo1");
p.addIdentifier().setSystem("foo2");
p.addIdentifier().setSystem("foo3");
p.addIdentifier().setSystem("foo4");
p.addName().setFamily("FOO" + i);
p.addName().addGiven("AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB1");
p.addName().addGiven("AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB2");
p.addName().addGiven("AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB3");
p.addName().addGiven("AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB4");
p.addName().addGiven("AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB5");
p.addName().addGiven("AAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBB6");
Organization o = new Organization();
o.setName("ORG" + i);
final Bundle t = new Bundle();
t.setType(BundleType.TRANSACTION);
t.addEntry()
.setResource(p)
.getRequest()
.setUrl("Patient/PID")
.setMethod(HTTPVerb.PUT);
t.addEntry()
.setResource(o)
.getRequest()
.setUrl("Organization")
.setMethod(HTTPVerb.POST);
if (i == 0) {
mySystemDao.transaction(mySrd, t);
}
futures.add(exec.submit(new Runnable() {
@Override
public void run() {
try {
mySystemDao.transaction(mySrd, t);
} catch (Exception e) {
ourLog.error("Failed to update", e);
errors.incrementAndGet();
}
}
}));
}
ourLog.info("Shutting down excutor");
StopWatch sw = new StopWatch();
for (Future next : futures) {
while (!next.isDone()) {
Thread.sleep(20);
}
}
exec.shutdown();
ourLog.info("Shut down excutor in {}ms", sw.getMillis());
ourLog.info("Had {} errors", errors.get());
Patient currentPatient = myPatientDao.read(new IdType("Patient/PID"));
Long currentVersion = currentPatient.getIdElement().getVersionIdPartAsLong();
ourLog.info("Current version: {}", currentVersion);
IBundleProvider historyBundle = myPatientDao.history(new IdType("Patient/PID"),null,null,mySrd);
Patient lastPatient = (Patient) historyBundle.getResources(0,1).get(0);
Long lastVersion = lastPatient.getIdElement().getVersionIdPartAsLong();
ourLog.info("Last version: {}", lastVersion);
assertEquals(currentVersion, lastVersion);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -1,51 +1,54 @@
package ca.uhn.fhir.jpa.stresstest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.UUID;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.util.TestUtil;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.junit.*;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.UUID;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.util.TestUtil;
import static org.junit.Assert.*;
public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StressTestDstu3Test.class);
private RequestValidatingInterceptor myRequestValidatingInterceptor;
@After
public void after() throws Exception {
super.after();
ourRestServer.unregisterInterceptor(myRequestValidatingInterceptor);
}
@Before
public void before() throws Exception {
super.before();
myRequestValidatingInterceptor = new RequestValidatingInterceptor();
FhirInstanceValidator module = new FhirInstanceValidator();
module.setValidationSupport(myValidationSupport);
myRequestValidatingInterceptor.addValidatorModule(module);
}
@After
public void after() throws Exception {
super.after();
ourRestServer.unregisterInterceptor(myRequestValidatingInterceptor);
}
@Test
public void testMultithreadedSearch() throws Exception {
Bundle input = new Bundle();
@ -56,8 +59,8 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
input.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
}
ourClient.transaction().withBundle(input).execute();
List<BaseTask> tasks = Lists.newArrayList();
try {
for (int threadIndex = 0; threadIndex < 10; threadIndex++) {
@ -74,10 +77,9 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
validateNoErrors(tasks);
}
/**
* This test prevents a deadlock that was detected with a large number of
* This test prevents a deadlock that was detected with a large number of
* threads creating resources and blocking on the searchparamcache refreshing
* (since this is a synchronized method) while the instance that was actually
* executing was waiting on a DB connection. This was solved by making
@ -87,7 +89,7 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
@Test
public void testMultithreadedSearchWithValidation() throws Exception {
ourRestServer.registerInterceptor(myRequestValidatingInterceptor);
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
for (int i = 0; i < 500; i++) {
@ -96,7 +98,7 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
input.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
}
ourClient.transaction().withBundle(input).execute();
CloseableHttpResponse getMeta = ourHttpClient.execute(new HttpGet(ourServerBase + "/metadata"));
try {
assertEquals(200, getMeta.getStatusLine().getStatusCode());
@ -133,7 +135,7 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
}
total += next.getTaskCount();
}
ourLog.info("Loaded {} searches", total);
}
@ -142,14 +144,14 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public class BaseTask extends Thread {
public class BaseTask extends Thread {
protected Throwable myError;
protected int myTaskCount = 0;
public BaseTask() {
setDaemon(true);
}
public Throwable getError() {
return myError;
}
@ -168,7 +170,7 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
for (int i = 0; i < 10; i++) {
try {
Bundle respBundle;
// Load search
HttpGet get = new HttpGet(ourServerBase + "/Patient?identifier=http%3A%2F%2Ftest%7CBAR," + UUID.randomUUID().toString());
get.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON_NEW);
@ -181,7 +183,7 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(getResp);
}
// Load page 2
get = new HttpGet(respBundle.getLink("next").getUrl());
get.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON_NEW);
@ -194,7 +196,7 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(getResp);
}
} catch (Throwable e) {
ourLog.error("Failure during search", e);
myError = e;
@ -214,9 +216,9 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
p.addIdentifier().setSystem("http://test").setValue("BAR").setType(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("bar")));
p.setGender(org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender.MALE);
ourClient.create().resource(p).execute();
ourSearchParamRegistry.forceRefresh();
} catch (Throwable e) {
ourLog.error("Failure during search", e);
myError = e;
@ -226,4 +228,5 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
}
}
}