diff --git a/packages/openchs-android/integrationTest/BaseIntegrationTest.js b/packages/openchs-android/integrationTest/BaseIntegrationTest.js index 2fae87a2c..fad37486d 100644 --- a/packages/openchs-android/integrationTest/BaseIntegrationTest.js +++ b/packages/openchs-android/integrationTest/BaseIntegrationTest.js @@ -78,6 +78,10 @@ class TestDb { console["debug"]("Creating object of type", clazz.schema.name, " with overwrite:", overwrite); return this.db.create(clazz.schema.name, entity, overwrite); } + + objectForPrimaryKey(clazz, key) { + return this.db.objectForPrimaryKey(clazz.schema.name, key); + } } export default BaseIntegrationTest; diff --git a/packages/openchs-android/integrationTest/IntegrationTestApp.js b/packages/openchs-android/integrationTest/IntegrationTestApp.js index 29b38396e..5b8d5ef2c 100644 --- a/packages/openchs-android/integrationTest/IntegrationTestApp.js +++ b/packages/openchs-android/integrationTest/IntegrationTestApp.js @@ -13,6 +13,7 @@ import UtilTest from "./UtilTest"; import UserInfoServiceTest from "./UserInfoServiceTest"; import RealmProxyTest from "./RealmProxyTest"; import ReportCardServiceIntegrationTest from "./ReportCardServiceIntegrationTest"; +import RealmDBOperationsCascadeTest from "./RealmDBOperationsCascadeTest"; import EntityApprovalServiceTest from "./EntityApprovalServiceTest"; import IndividualIntegrationTest from "./model/IndividualIntegrationTest"; import General from "../src/utility/General"; @@ -85,7 +86,7 @@ class IntegrationTestApp extends Component { LogBox.ignoreAllLogs(); FileSystem.init(); this.getBean = this.getBean.bind(this); - this.integrationTestRunner = new IntegrationTestRunner(DatabaseTest, IndividualIntegrationTest, EntityApprovalServiceTest, ReportCardServiceIntegrationTest, UserInfoServiceTest, PersonRegisterActionsIntegrationTest, UtilTest, RealmProxyTest, PruneMediaIntegrationTest); + this.integrationTestRunner = new IntegrationTestRunner(DatabaseTest, IndividualIntegrationTest, EntityApprovalServiceTest, ReportCardServiceIntegrationTest, UserInfoServiceTest, PersonRegisterActionsIntegrationTest, UtilTest, RealmProxyTest, PruneMediaIntegrationTest, RealmDBOperationsCascadeTest); this.state = {isInitialisationDone: false, testSuite: this.integrationTestRunner.testSuite, expandedTestClasses: []}; } diff --git a/packages/openchs-android/integrationTest/RealmDBOperationsCascadeTest.js b/packages/openchs-android/integrationTest/RealmDBOperationsCascadeTest.js new file mode 100644 index 000000000..5438d0d06 --- /dev/null +++ b/packages/openchs-android/integrationTest/RealmDBOperationsCascadeTest.js @@ -0,0 +1,104 @@ +import {AddressLevel, Concept, Encounter, EncounterType, Individual, PrimitiveValue, SubjectType} from 'openchs-models'; +import TestSubjectTypeFactory from '../test/model/TestSubjectTypeFactory'; +import TestEncounterTypeFactory from '../test/model/TestEncounterTypeFactory'; +import TestConceptFactory from '../test/model/TestConceptFactory'; +import TestAddressLevelFactory from '../test/model/TestAddressLevelFactory'; +import TestSubjectFactory from '../test/model/txn/TestSubjectFactory'; +import TestObsFactory from '../test/model/TestObsFactory'; +import TestEncounterFactory from '../test/model/txn/TestEncounterFactory'; +import moment from 'moment/moment'; +import {assert} from 'chai'; +import {JSONStringify} from '../src/utility/JsonStringify'; +import BaseIntegrationTest from './BaseIntegrationTest'; + +class RealmDBOperationsCascadeTest extends BaseIntegrationTest { + + setup() { + super.setup(); + + this.executeInWrite((db) => { + const LAST_NAME_VALUE = 'LAST'; + const updateMode = true; + try { + + //Init subjectType and concept + this.subjectType = db.create(SubjectType, TestSubjectTypeFactory.createWithDefaults({ + type: SubjectType.types.Person, + name: 'Beneficiary' + })); + this.encounterType = db.create(EncounterType, TestEncounterTypeFactory.create({name: "Bar"})); + this.originalConcept = db.create(Concept, TestConceptFactory.createWithDefaults({ + name: 'concept-1', + dataType: Concept.dataType.Text + }), updateMode); + this.savedAddressLevel = db.create(AddressLevel, TestAddressLevelFactory.createWithDefaults({level: 1})); + + //Create Individual + this.individual = db.create(Individual, TestSubjectFactory.createWithDefaults({ + subjectType : this.subjectType, + address: this.savedAddressLevel, + firstName: "XYZ", + lastName: "bar", + observations: [TestObsFactory.create({ + concept: this.originalConcept, + valueJSON: JSON.stringify(this.originalConcept.getValueWrapperFor("ABC")) + })], + approvalStatuses: [] + }), updateMode); + + //Create encounter + this.encounter = db.create(Encounter, TestEncounterFactory.create({ + earliestVisitDateTime: moment().add(-2, "day").toDate(), + maxVisitDateTime: moment().add(2, "day").toDate(), + encounterType: this.encounterType, + approvalStatuses: [], + observations: [TestObsFactory.create({ + concept: this.originalConcept, + valueJSON: JSON.stringify(this.originalConcept.getValueWrapperFor("ABC")) + })], + latestEntityApprovalStatus: null, + subject: this.individual + })); + + // Link encounter to individual + this.individual.addEncounter(this.encounter); + + //Modify individual name and observation value + this.encounter.individual.lastName = LAST_NAME_VALUE; + this.encounter.individual.observations[0].valueJSON = JSON.stringify(this.originalConcept.getValueWrapperFor("DEF")); + + } catch (error) { + assert.fail(error.message); + } + }); + } + + + saveIndividualShouldNotCascadeUpdateToEncounter() { + const LAST_NAME_VALUE = 'LAST'; + const updateMode = true; + + let updatedIndividual; + + this.executeInWrite((db) => { + updatedIndividual = db.create(Individual, this.individual, updateMode); + }); + + //Concept name should remain the same + assert.equal(updatedIndividual.lastName, LAST_NAME_VALUE); + assert.equal(updatedIndividual.observations[0].valueJSON, JSONStringify(new PrimitiveValue("DEF", Concept.dataType.Text))); + + //Modify encounter observation value + // this.encounter.observations[0].valueJSON = JSON.stringify(this.originalConcept.getValueWrapperFor("DEF")); + let refetchedEncounter; + this.executeInWrite((db) => { + refetchedEncounter = db.objectForPrimaryKey(Encounter, this.encounter.uuid); + }); + + assert.equal(refetchedEncounter.observations.length, 1); + assert.equal(refetchedEncounter.observations[0].valueJSON, JSONStringify(new PrimitiveValue("ABC", Concept.dataType.Text))); + + } +} + +export default RealmDBOperationsCascadeTest; \ No newline at end of file diff --git a/packages/openchs-android/integrationTest/RealmProxyTest.js b/packages/openchs-android/integrationTest/RealmProxyTest.js index 157cf782b..9d9ab2026 100644 --- a/packages/openchs-android/integrationTest/RealmProxyTest.js +++ b/packages/openchs-android/integrationTest/RealmProxyTest.js @@ -1,16 +1,17 @@ import BaseIntegrationTest from "./BaseIntegrationTest"; -import {AddressLevel, Observation, Individual, SubjectType} from "openchs-models"; +import {AddressLevel, Concept, Individual, SubjectType} from "openchs-models"; import {assert} from "chai"; import TestObsFactory from "../test/model/TestObsFactory"; import TestSubjectFactory from "../test/model/txn/TestSubjectFactory"; import TestAddressLevelFactory from "../test/model/TestAddressLevelFactory"; import moment from "moment/moment"; import TestSubjectTypeFactory from "../test/model/TestSubjectTypeFactory"; +import TestConceptFactory from '../test/model/TestConceptFactory'; function shouldFail(baseIntegrationTest, obs, updateMode) { baseIntegrationTest.executeInWrite((db) => { try { - db.create(Observation, obs, updateMode); + db.create(Concept, obs, updateMode); assert.fail("Comment without subject and CommentThread should have failed to save."); } catch (error) { } @@ -42,17 +43,53 @@ function shouldFailSubjectCreationWithoutAddress(baseIntegrationTest, sub, updat }); } +function conceptNameShouldRemainSameOnEntityObservationSave(baseIntegrationTest, updateMode) { + baseIntegrationTest.executeInWrite((db) => { + const originalConceptName = 'concept-1'; + try { + + //Init subjectType and concept + const subjectType = db.create(SubjectType, TestSubjectTypeFactory.createWithDefaults({type: SubjectType.types.Person, name: 'Beneficiary'})); + const originalConcept = db.create(Concept, TestConceptFactory.createWithDefaults({name: originalConceptName, dataType: Concept.dataType.Text}), updateMode); + const savedAddressLevel = db.create(AddressLevel, TestAddressLevelFactory.createWithDefaults({level: 1})); + + //Clone and modify the concept name + const originalConceptClone = originalConcept.cloneForReference(); + const modifiedConceptName = originalConcept.name + "modified"; + originalConceptClone.name = modifiedConceptName; + + //Create Individual + const individual = db.create(Individual, TestSubjectFactory.createWithDefaults({ + subjectType, + address: savedAddressLevel, + firstName: "XYZ", + lastName: "bar", + observations: [TestObsFactory.create({concept: originalConceptClone, valueJSON: JSON.stringify(originalConceptClone.getValueWrapperFor("ABC"))})], + approvalStatuses: [] + }), updateMode); + + //Fetch the concept from db again + const modifiedConcept = db.objectForPrimaryKey(Concept, originalConcept.uuid); + + //Concept name would have changed + assert.equal(modifiedConceptName, modifiedConcept.name); + } catch (error) { + assert.fail(error.message); + } + }); +} + + function shouldPass(baseIntegrationTest, obs, updateMode) { baseIntegrationTest.executeInWrite((db) => { - db.create(Observation, obs, updateMode); + db.create(Concept, obs, updateMode); }); } class RealmProxyTest extends BaseIntegrationTest { doNotAllowCreateWithMandatoryObjectTypePropertyAsNull() { - shouldFail(this, TestObsFactory.create({valueJSON: "{}"}), false); - shouldFail(this, TestObsFactory.create({concept: null, valueJSON: "{}"}), true); - shouldPass(this, TestObsFactory.create({valueJSON: "{}"}), true); + shouldFail(this, TestConceptFactory.createWithDefaults({dataType: null}), false); + shouldPass(this, TestConceptFactory.createWithDefaults({dataType: Concept.dataType.Text}), false); } doNotAllowCreationOfIndividualWithoutSubjectType() { @@ -66,6 +103,11 @@ class RealmProxyTest extends BaseIntegrationTest { TestSubjectFactory.createWithDefaults({firstName:'foo', lastName:'bar', address:null, registrationDate: moment().toDate(), observations:[]}), false) } + + saveOfEntityObservationShouldNotCascadeToConcept() { + conceptNameShouldRemainSameOnEntityObservationSave(this, true); + } + } export default RealmProxyTest;