@@ -974,6 +974,12 @@ describe('Amount/Unit CRUD', () => {
974974 expect ( errorMsg . text ) . toContain ( NO_AMOUNT_ERROR ) ;
975975 errorMsg = await ExperimentCRUDUtils . importSample ( server , "Name\tStoredAmount\tUnits\nData1\t1.1\tL" , dataType , "INSERT" , topFolderOptions , editorUserOptions ) ;
976976 expect ( errorMsg . text ) . toContain ( INCOMPATIBLE_ERROR ) ;
977+ errorMsg = await ExperimentCRUDUtils . importSample ( server , "Name\tStoredAmount\tUnits\nData1\t1.1\tunit" , dataType , "INSERT" , topFolderOptions , editorUserOptions ) ;
978+ expect ( errorMsg . text ) . toContain ( 'Units value (unit) is not compatible with the ' + dataType + ' display units (g).' ) ;
979+ errorMsg = await ExperimentCRUDUtils . importSample ( server , "Name\tStoredAmount\tUnits\nData1\t1.1\tcells" , dataType , "INSERT" , topFolderOptions , editorUserOptions ) ;
980+ expect ( errorMsg . text ) . toContain ( 'Units value (cells) is not compatible with the ' + dataType + ' display units (g).' ) ;
981+ errorMsg = await ExperimentCRUDUtils . importSample ( server , "Name\tStoredAmount\tUnits\nData1\t1.1\tbogus" , dataType , "INSERT" , topFolderOptions , editorUserOptions ) ;
982+ expect ( errorMsg . text ) . toContain ( 'Unsupported Units value (bogus). Supported values are: kg, g, mg, ug, ng.' ) ;
977983 errorMsg = await ExperimentCRUDUtils . importSample ( server , "Name\tStoredAmount\tUnits\nData1\t-1.1\tkg" , dataType , "INSERT" , topFolderOptions , editorUserOptions ) ;
978984 expect ( errorMsg . text ) . toContain ( NEGATIVE_ERROR ) ;
979985 errorMsg = await ExperimentCRUDUtils . importCrossTypeData ( server , "Name\tStoredAmount\tUnits\tSampleType\nData1\t-1.1\tkg\t" + dataType , 'IMPORT' , topFolderOptions , adminOptions , true ) ;
@@ -1085,5 +1091,235 @@ describe('Amount/Unit CRUD', () => {
10851091
10861092 } ) ;
10871093
1094+ it ( "Test units conversion on insert/update" , async ( ) => {
1095+ const sampleTypeMass = 'SampleTypeWithMassUnits' ;
1096+ const sampleTypeVolume = 'SampleTypeWithVolumeUnits' ;
1097+ const sampleTypeCount = 'SampleTypeWithCountUnits' ;
1098+
1099+ const sampleTypeUnits = {
1100+ [ sampleTypeMass ] : 'ug' ,
1101+ [ sampleTypeVolume ] : 'L' ,
1102+ [ sampleTypeCount ] : 'unit'
1103+ } ;
1104+
1105+ for ( const [ dataType , unit ] of Object . entries ( sampleTypeUnits ) ) {
1106+ const createPayload = {
1107+ kind : 'SampleSet' ,
1108+ domainDesign : { name : dataType , fields : [ { name : 'Name' } ] } ,
1109+ options : {
1110+ name : dataType ,
1111+ metricUnit : unit
1112+ }
1113+ } ;
1114+ await server . post ( 'property' , 'createDomain' , createPayload , { ...topFolderOptions , ...designerReaderOptions } ) . expect ( successfulResponse ) ;
1115+ }
1116+
1117+ let sampleRowsWithUnits = await ExperimentCRUDUtils . insertRows ( server , [
1118+ { name : 'S-ng' , amount : 4.56 , units : 'ng' } ,
1119+ { name : 'S-ug' , amount : 4.56 , units : 'ug' } ,
1120+ { name : 'S-mg' , amount : 4.56 , units : 'mg' } ,
1121+ { name : 'S-g' , amount : 4.56 , units : 'g' } ,
1122+ { name : 'S-kg' , amount : 4.56 , units : 'kg' } ,
1123+ ] , 'samples' , sampleTypeMass , topFolderOptions , editorUserOptions ) ;
1124+
1125+ // check for raw amount in g and display amount in ug
1126+ let expectedRawAmounts : { } = {
1127+ 'S-ng' : 4.56e-9 ,
1128+ 'S-ug' : 4.56e-6 ,
1129+ 'S-mg' : 0.00456 ,
1130+ 'S-g' : 4.56 ,
1131+ 'S-kg' : 4560 ,
1132+ } ;
1133+ let expectedStoredAmounts : { } = {
1134+ 'S-ng' : 4.56e-3 ,
1135+ 'S-ug' : 4.56 ,
1136+ 'S-mg' : 4560 ,
1137+ 'S-g' : 4.56e6 ,
1138+ 'S-kg' : 4.56e9 ,
1139+ } ;
1140+
1141+ for ( const sampleRow of sampleRowsWithUnits ) {
1142+ const sampleName = caseInsensitive ( sampleRow , 'name' ) ;
1143+ let sampleData = await ExperimentCRUDUtils . getSampleDataByName ( server , sampleName , sampleTypeMass , 'StoredAmount,Units,RawAmount,RawUnits' , topFolderOptions , readerUserOptions ) ;
1144+ expect ( caseInsensitive ( sampleData , 'RawAmount' ) ) . toBeCloseTo ( expectedRawAmounts [ sampleName ] ) ;
1145+ expect ( caseInsensitive ( sampleData , 'StoredAmount' ) ) . toBeCloseTo ( expectedStoredAmounts [ sampleName ] ) ;
1146+ expect ( caseInsensitive ( sampleData , 'RawUnits' ) ) . toEqual ( 'g' ) ;
1147+ expect ( caseInsensitive ( sampleData , 'Units' ) ) . toEqual ( 'ug' ) ;
1148+ await server . post ( 'query' , 'updateRows' , {
1149+ schemaName : 'samples' ,
1150+ queryName : sampleTypeMass ,
1151+ rows : [ {
1152+ amount : 6.54 ,
1153+ units : sampleName . substring ( 2 ) ,
1154+ rowId : caseInsensitive ( sampleRow , 'rowId' )
1155+ } ]
1156+ } , { ...topFolderOptions , ...editorUserOptions } ) . expect ( successfulResponse ) ;
1157+ }
1158+
1159+ expectedRawAmounts = {
1160+ 'S-ng' : 6.54e-9 ,
1161+ 'S-ug' : 6.54e-6 ,
1162+ 'S-mg' : 0.00654 ,
1163+ 'S-g' : 6.54 ,
1164+ 'S-kg' : 6540 ,
1165+ } ;
1166+ expectedStoredAmounts = {
1167+ 'S-ng' : 6.54e-3 ,
1168+ 'S-ug' : 6.54 ,
1169+ 'S-mg' : 6540 ,
1170+ 'S-g' : 6.54e6 ,
1171+ 'S-kg' : 6.54e9 ,
1172+ } ;
1173+ for ( const sampleRow of sampleRowsWithUnits ) {
1174+ const sampleName = caseInsensitive ( sampleRow , 'name' ) ;
1175+ let sampleData = await ExperimentCRUDUtils . getSampleDataByName ( server , sampleName , sampleTypeMass , 'StoredAmount,Units,RawAmount,RawUnits' , topFolderOptions , readerUserOptions ) ;
1176+ expect ( caseInsensitive ( sampleData , 'RawAmount' ) ) . toBeCloseTo ( expectedRawAmounts [ sampleName ] ) ;
1177+ expect ( caseInsensitive ( sampleData , 'StoredAmount' ) ) . toBeCloseTo ( expectedStoredAmounts [ sampleName ] ) ;
1178+ expect ( caseInsensitive ( sampleData , 'RawUnits' ) ) . toEqual ( 'g' ) ;
1179+ expect ( caseInsensitive ( sampleData , 'Units' ) ) . toEqual ( 'ug' ) ;
1180+ }
1181+
1182+ sampleRowsWithUnits = await ExperimentCRUDUtils . insertRows ( server , [
1183+ { name : 'S-L' , amount : 4.56 , units : 'L' } ,
1184+ { name : 'S-mL' , amount : 4.56 , units : 'mL' } ,
1185+ { name : 'S-uL' , amount : 4.56 , units : 'uL' } ,
1186+ ] , 'samples' , sampleTypeVolume , topFolderOptions , editorUserOptions ) ;
1187+
1188+ // check for storedamount in mL
1189+ expectedRawAmounts = {
1190+ 'S-L' : 4560 ,
1191+ 'S-mL' : 4.56 ,
1192+ 'S-uL' : 0.00456 ,
1193+ } ;
1194+ // stored amount is in L
1195+ expectedStoredAmounts = {
1196+ 'S-L' : 4.56 ,
1197+ 'S-mL' : 0.00456 ,
1198+ 'S-uL' : 4.56e-6 ,
1199+ }
1200+ for ( const sampleRow of sampleRowsWithUnits ) {
1201+ const sampleName = caseInsensitive ( sampleRow , 'name' ) ;
1202+ const sampleData = await ExperimentCRUDUtils . getSampleDataByName ( server , sampleName , sampleTypeVolume , 'StoredAmount,Units,RawAmount,RawUnits' , topFolderOptions , readerUserOptions ) ;
1203+ expect ( caseInsensitive ( sampleData , 'RawAmount' ) ) . toBeCloseTo ( expectedRawAmounts [ sampleName ] ) ;
1204+ expect ( caseInsensitive ( sampleData , 'StoredAmount' ) ) . toBeCloseTo ( expectedStoredAmounts [ sampleName ] ) ;
1205+ expect ( caseInsensitive ( sampleData , 'RawUnits' ) ) . toEqual ( 'mL' ) ;
1206+ expect ( caseInsensitive ( sampleData , 'Units' ) ) . toEqual ( 'L' ) ;
1207+ }
1208+
1209+ const countRows = [
1210+ { name : 'S-unit' , amount : 4.56 , units : 'unit' } ,
1211+ { name : 'S-pieces' , amount : 4.56 , units : 'pieces' } ,
1212+ { name : 'S-kits' , amount : 4.56 , units : 'kits' } ,
1213+ { name : 'S-cells' , amount : 4.56 , units : 'cells' }
1214+ ]
1215+ sampleRowsWithUnits = await ExperimentCRUDUtils . insertRows ( server , countRows , 'samples' , sampleTypeCount , topFolderOptions , editorUserOptions ) ;
1216+
1217+ for ( const sampleRow of sampleRowsWithUnits ) {
1218+ const sampleName = caseInsensitive ( sampleRow , 'name' ) ;
1219+ const usedUnit = sampleName . substring ( 2 ) ;
1220+ const sampleData = await ExperimentCRUDUtils . getSampleDataByName ( server , sampleName , sampleTypeCount , 'StoredAmount,Units,RawAmount,RawUnits' , topFolderOptions , readerUserOptions ) ;
1221+ expect ( caseInsensitive ( sampleData , 'RawAmount' ) ) . toBeCloseTo ( 4.56 ) ;
1222+ expect ( caseInsensitive ( sampleData , 'StoredAmount' ) ) . toBeCloseTo ( 4.56 ) ;
1223+ expect ( caseInsensitive ( sampleData , 'RawUnits' ) ) . toEqual ( usedUnit ) ;
1224+ expect ( caseInsensitive ( sampleData , 'Units' ) ) . toEqual ( usedUnit ) ;
1225+
1226+ await server . post ( 'query' , 'updateRows' , {
1227+ schemaName : 'samples' ,
1228+ queryName : sampleTypeCount ,
1229+ rows : [ {
1230+ amount : 6.54 ,
1231+ units : usedUnit ,
1232+ rowId : caseInsensitive ( sampleRow , 'rowId' )
1233+ } ]
1234+ } , { ...topFolderOptions , ...editorUserOptions } ) . expect ( successfulResponse ) ;
1235+ }
1236+
1237+ for ( const sampleRow of sampleRowsWithUnits ) {
1238+ const sampleName = caseInsensitive ( sampleRow , 'name' ) ;
1239+ const usedUnit = sampleName . substring ( 2 ) ;
1240+ const sampleData = await ExperimentCRUDUtils . getSampleDataByName ( server , sampleName , sampleTypeCount , 'StoredAmount,Units,RawAmount,RawUnits' , topFolderOptions , readerUserOptions ) ;
1241+ expect ( caseInsensitive ( sampleData , 'RawAmount' ) ) . toBeCloseTo ( 6.54 ) ;
1242+ expect ( caseInsensitive ( sampleData , 'StoredAmount' ) ) . toBeCloseTo ( 6.54 ) ;
1243+ expect ( caseInsensitive ( sampleData , 'RawUnits' ) ) . toEqual ( usedUnit ) ;
1244+ expect ( caseInsensitive ( sampleData , 'Units' ) ) . toEqual ( usedUnit ) ;
1245+ }
1246+
1247+ } )
1248+
1249+ async function verifyCountTypeAliquotRollup ( sampleTypeName : string , hasSampleTypeDisplayUnit : boolean ) {
1250+ const dataRows = [
1251+ { name : 'S-no-amount' } ,
1252+ { AliquotedFrom : 'S-no-amount' , name : 'S-no-pcs1' , amount : 2 , units : 'pieces' } ,
1253+ { AliquotedFrom : 'S-no-amount' , name : 'S-no-pcs2' , amount : 2 , units : 'pieces' } ,
1254+ { name : 'S-unit' , amount : 1 , units : 'unit' } ,
1255+ { AliquotedFrom : 'S-unit' , name : 'S-unit-unit1' , amount : 2 , units : 'unit' } ,
1256+ { AliquotedFrom : 'S-unit' , name : 'S-unit-unit2' , amount : 2 , units : 'unit' } ,
1257+ { name : 'S-pieces' , amount : 1 , units : 'pieces' } ,
1258+ { AliquotedFrom : 'S-pieces' , name : 'S-pcs-pcs1' , amount : 2 , units : 'pieces' } ,
1259+ { AliquotedFrom : 'S-pieces' , name : 'S-pcs-pcs2' , amount : 2 , units : 'pieces' } ,
1260+ { name : 'S-kits' , amount : 1 , units : 'kits' } ,
1261+ { AliquotedFrom : 'S-kits' , name : 'S-kit-pcs1' , amount : 2 , units : 'pieces' } ,
1262+ { AliquotedFrom : 'S-kits' , name : 'S-kit-pcs2' , amount : 2 , units : 'pieces' } ,
1263+ { name : 'S-cells' , amount : 1 , units : 'cells' } ,
1264+ { AliquotedFrom : 'S-cells' , name : 'S-cells-pcs1' , amount : 2 , units : 'pieces' } ,
1265+ { AliquotedFrom : 'S-cells' , name : 'S-cells-cells2' , amount : 2 , units : 'cells' } ,
1266+ ]
1267+
1268+ const insertedResults = await ExperimentCRUDUtils . insertRows ( server , dataRows , 'samples' , sampleTypeName , topFolderOptions , editorUserOptions ) ;
1269+ const insertedMap = { } ;
1270+ for ( const row of insertedResults ) {
1271+ insertedMap [ caseInsensitive ( row , 'name' ) ] = row ;
1272+ }
1273+
1274+ let expectedAliquotUnit = {
1275+ 'S-no-amount' : 'pieces' ,
1276+ 'S-unit' : 'unit' ,
1277+ 'S-pieces' : 'pieces' ,
1278+ 'S-kits' : 'pieces' ,
1279+ 'S-cells' : hasSampleTypeDisplayUnit ? 'unit' : 'cells' ,
1280+ } ;
1281+
1282+ // for each expectedRollupAmounts
1283+ for ( const [ sampleName , expectedAliquotUnitValue ] of Object . entries ( expectedAliquotUnit ) ) {
1284+ let parentUnit = sampleName . substring ( 2 ) ;
1285+ if ( parentUnit === 'no-amount' ) {
1286+ parentUnit = null ;
1287+ }
1288+ const sampleData = await ExperimentCRUDUtils . getSampleDataByName ( server , sampleName , sampleTypeName , 'Units,RawUnits,AliquotVolume,AliquotCount,AliquotUnit' , topFolderOptions , readerUserOptions ) ;
1289+ expect ( caseInsensitive ( sampleData , 'RawUnits' ) ) . toEqual ( parentUnit ) ;
1290+ expect ( caseInsensitive ( sampleData , 'Units' ) ) . toEqual ( parentUnit ) ;
1291+ expect ( caseInsensitive ( sampleData , 'AliquotVolume' ) ) . toEqual ( 4 ) ;
1292+ expect ( caseInsensitive ( sampleData , 'AliquotCount' ) ) . toEqual ( 2 ) ;
1293+ expect ( caseInsensitive ( sampleData , 'AliquotUnit' ) ) . toEqual ( expectedAliquotUnitValue ) ;
1294+
1295+ }
1296+ }
1297+
1298+ it ( "Test aliquot rollup for count display unit" , async ( ) => {
1299+ let dataType = 'SampleTypeAliquotWithCountUnit' ;
1300+ let createPayload : { } = {
1301+ kind : 'SampleSet' ,
1302+ domainDesign : { name : dataType , fields : [ { name : 'Name' } ] } ,
1303+ options : {
1304+ name : dataType ,
1305+ metricUnit : 'unit'
1306+ }
1307+ } ;
1308+ await server . post ( 'property' , 'createDomain' , createPayload , { ...topFolderOptions , ...designerReaderOptions } ) . expect ( successfulResponse ) ;
1309+ await verifyCountTypeAliquotRollup ( dataType , true ) ;
1310+
1311+ dataType = 'SampleTypeAliquoNoDisplayUnit' ;
1312+ createPayload = {
1313+ kind : 'SampleSet' ,
1314+ domainDesign : { name : dataType , fields : [ { name : 'Name' } ] } ,
1315+ options : {
1316+ name : dataType ,
1317+ }
1318+ } ;
1319+ await server . post ( 'property' , 'createDomain' , createPayload , { ...topFolderOptions , ...designerReaderOptions } ) . expect ( successfulResponse ) ;
1320+ await verifyCountTypeAliquotRollup ( dataType , false ) ;
1321+
1322+ } )
1323+
10881324} ) ;
10891325
0 commit comments