chore: initial git load of code space
This commit is contained in:
@@ -0,0 +1,936 @@
|
||||
[
|
||||
{
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000000
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
"BILLCPTBilledCPTCode": "700000",
|
||||
"BILLCPTBilledCPTID": 700000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 4000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000002
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 2000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000001
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "800000",
|
||||
"BILLCPTBilledCPTID": 800000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 8000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000003
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "800000",
|
||||
"BILLCPTBilledCPTID": 800000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "300000Dept_Code",
|
||||
"DEPTDepartmentID": 300000,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 8000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000004
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "800000",
|
||||
"BILLCPTBilledCPTID": 800000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "350000Dept_Code",
|
||||
"DEPTDepartmentID": 350000,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 9999.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000005
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 6,
|
||||
"DimAgeAgeID": 6,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "3333333",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 5000000,
|
||||
"PATIENTTYPEPatientTypeRollupName": "OutpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 3333333,
|
||||
"AccountBalance": 50.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "5000000ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 15000.0000,
|
||||
"RowID": 125892
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "900000",
|
||||
"BILLCPTBilledCPTID": 900000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 6000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 2000.0000,
|
||||
"UnitsOfService": 2.0,
|
||||
"RowID": 2000000006
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 6000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 4000.0000,
|
||||
"UnitsOfService": 2.0,
|
||||
"RowID": 2000000007
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "700000",
|
||||
"BILLCPTBilledCPTID": 700000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 6000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 8000.0000,
|
||||
"UnitsOfService": 2.0,
|
||||
"RowID": 2000000008
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "700000",
|
||||
"BILLCPTBilledCPTID": 700000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "300000Dept_Code",
|
||||
"DEPTDepartmentID": 300000,
|
||||
"DimPatientEnEncounterID": 6000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 10000.0000,
|
||||
"UnitsOfService": 2.0,
|
||||
"RowID": 2000000009
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 6,
|
||||
"DimAgeAgeID": 6,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "4444444",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 6000000,
|
||||
"PATIENTTYPEPatientTypeRollupName": "OutpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 4444444,
|
||||
"AccountBalance": 100.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "6000000ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 30000.0000,
|
||||
"RowID": 125893
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 7000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 200000.0000,
|
||||
"UnitsOfService": 3.0,
|
||||
"RowID": 2000000010
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 10,
|
||||
"DimAgeAgeID": 10,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "3333333",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 7000000,
|
||||
"PATIENTTYPEPatientTypeRollupName": "OutpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 3333333,
|
||||
"AccountBalance": 505.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "7000000ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 60000.0000,
|
||||
"RowID": 125894
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "0",
|
||||
"BILLCPTBilledCPTID": 0,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 8000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 500000.0000,
|
||||
"UnitsOfService": 3.0,
|
||||
"RowID": 2000000011
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "0",
|
||||
"BILLCPTBilledCPTID": 0,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 8000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 2000.0000,
|
||||
"UnitsOfService": 3.0,
|
||||
"RowID": 2000000012
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 18,
|
||||
"DimAgeAgeID": 18,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 8000000,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 1005.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "8000000ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 14999.0000,
|
||||
"RowID": 125895
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "0",
|
||||
"BILLCPTBilledCPTID": 0,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "500000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 500000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 9000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 2000.0000,
|
||||
"UnitsOfService": 4.0,
|
||||
"RowID": 2000000013
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 55,
|
||||
"DimAgeAgeID": 55,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-04T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12788,
|
||||
"DimPatientEnEncounterID": 9000000,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 0.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-04T00:00:00",
|
||||
"EncounterRecordNumber": "9000000ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 240000.0000,
|
||||
"RowID": 125896
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 9000005,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 3000.0000,
|
||||
"UnitsOfService": 4.0,
|
||||
"RowID": 2000000014
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 60,
|
||||
"DimAgeAgeID": 60,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-04T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12788,
|
||||
"DimPatientEnEncounterID": 9000005,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 1.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-04T00:00:00",
|
||||
"EncounterRecordNumber": "9000005ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 480000.0000,
|
||||
"RowID": 125897
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "300000Dept_Code",
|
||||
"DEPTDepartmentID": 300000,
|
||||
"DimPatientEnEncounterID": 9000010,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 4000.0000,
|
||||
"UnitsOfService": 4.0,
|
||||
"RowID": 2000000015
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 65,
|
||||
"DimAgeAgeID": 65,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-11T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12795,
|
||||
"DimPatientEnEncounterID": 9000010,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 0.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-11T00:00:00",
|
||||
"EncounterRecordNumber": "9000010ERN",
|
||||
"TotalActualPayments": 1.0000,
|
||||
"TotalCharges": 960000.0000,
|
||||
"RowID": 125898
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 9000020,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 4000.0000,
|
||||
"UnitsOfService": 5.0,
|
||||
"RowID": 2000000016
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "250000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 250000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 9000020,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 10000.0000,
|
||||
"UnitsOfService": 5.0,
|
||||
"RowID": 2000000017
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "500000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 500000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "300000Dept_Code",
|
||||
"DEPTDepartmentID": 300000,
|
||||
"DimPatientEnEncounterID": 9000020,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000018
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 5,
|
||||
"DimAgeAgeID": 5,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 9000020,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 50.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "9000020ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 15000.0000,
|
||||
"RowID": 125899
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "500000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 500000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "300000Dept_Code",
|
||||
"DEPTDepartmentID": 300000,
|
||||
"DimPatientEnEncounterID": 9000030,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 11000.0000,
|
||||
"UnitsOfService": 3.0,
|
||||
"RowID": 2000000019
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 10,
|
||||
"DimAgeAgeID": 10,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "3333333",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 9000030,
|
||||
"PATIENTTYPEPatientTypeRollupName": "OutpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 3333333,
|
||||
"AccountBalance": 50.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "9000030ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 15000.0000,
|
||||
"RowID": 125900
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-11T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 9000060,
|
||||
"ServDateServiceDateID": 12795,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000022
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 1,
|
||||
"DimAgeAgeID": 1,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 9000060,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 10.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "9000060ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 7500.0000,
|
||||
"RowID": 125905
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-02-11T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 9000070,
|
||||
"ServDateServiceDateID": 12826,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000023
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12788,
|
||||
"DimAgeAge": 1,
|
||||
"DimAgeAgeID": 1,
|
||||
"AdmitDateCalendarDateTime": "2015-01-04T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12785,
|
||||
"DimPatientEnEncounterID": 9000070,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 10.0000,
|
||||
"AdmitDateTime": "2015-01-04T00:00:00",
|
||||
"DischargeDateTime": "2015-01-01T00:00:00",
|
||||
"EncounterRecordNumber": "9000070ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 7500.0000,
|
||||
"RowID": 125906
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-11T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 9000080,
|
||||
"ServDateServiceDateID": 12795,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000024
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12788,
|
||||
"DimAgeAge": 1,
|
||||
"DimAgeAgeID": 1,
|
||||
"AdmitDateCalendarDateTime": "2015-01-04T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12785,
|
||||
"DimPatientEnEncounterID": 9000080,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 10.0000,
|
||||
"AdmitDateTime": "2015-01-04T00:00:00",
|
||||
"DischargeDateTime": "2015-01-01T00:00:00",
|
||||
"EncounterRecordNumber": "9000080ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 7500.0000,
|
||||
"RowID": 125907
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-11T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "28000Mod_Code",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 28000,
|
||||
"DEPTDepartmentCode": "275000Dept_Code",
|
||||
"DEPTDepartmentID": 275000,
|
||||
"DimPatientEnEncounterID": 9000090,
|
||||
"ServDateServiceDateID": 12795,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 2000000025
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 1,
|
||||
"DimAgeAgeID": 1,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-03T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12787,
|
||||
"DimPatientEnEncounterID": 9000090,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 10.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-03T00:00:00",
|
||||
"EncounterRecordNumber": "9000090",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 7500.0000,
|
||||
"RowID": 125908
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "ea0cdbd2-fcf0-4a75-b08c-83b0709aea66",
|
||||
"Data": {
|
||||
|
||||
"BILLCPTBilledCPTCode": "500000",
|
||||
"BILLCPTBilledCPTID": 500000,
|
||||
"ServDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"CCChargeCode": "200000CC_Code",
|
||||
"CHGMOD1ChargeModifier1Code": "0",
|
||||
"CCChargeCodeID": 200000,
|
||||
"CHGMOD1ChargeModifier1ID": 0,
|
||||
"DEPTDepartmentCode": "250000Dept_Code",
|
||||
"DEPTDepartmentID": 250000,
|
||||
"DimPatientEnEncounterID": 20000000,
|
||||
"ServDateServiceDateID": 12785,
|
||||
"Charge": 1000.0000,
|
||||
"UnitsOfService": 1.0,
|
||||
"RowID": 1990000000
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 1,
|
||||
"DimAgeAgeID": 1,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-02T00:00:00",
|
||||
"PATIENTTYPECode": "1111111",
|
||||
"DischDateDischargeDateID": 12786,
|
||||
"DimPatientEnEncounterID": 20000000,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 1111111,
|
||||
"AccountBalance": 5.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-02T00:00:00",
|
||||
"EncounterRecordNumber": "20000000ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 5000.0000,
|
||||
"RowID": 125903
|
||||
},
|
||||
"AllChildData": []
|
||||
},
|
||||
{
|
||||
|
||||
"DataSourceGuid": "41639c8f-fecf-4449-b6e6-53f796c0c3e4",
|
||||
"Data": {
|
||||
|
||||
"AdmitDateAdmitDateID": 12785,
|
||||
"DimAgeAge": 1,
|
||||
"DimAgeAgeID": 1,
|
||||
"AdmitDateCalendarDateTime": "2015-01-01T00:00:00",
|
||||
"DischDateCalendarDateTime": "2015-01-02T00:00:00",
|
||||
"PATIENTTYPECode": "2222222",
|
||||
"DischDateDischargeDateID": 12786,
|
||||
"DimPatientEnEncounterID": 20000001,
|
||||
"PATIENTTYPEPatientTypeRollupName": "InpatientTest",
|
||||
"DimPatientPatientID": 0,
|
||||
"PATIENTTYPEPatientTypeID": 2222222,
|
||||
"AccountBalance": 10.0000,
|
||||
"AdmitDateTime": "2015-01-01T00:00:00",
|
||||
"DischargeDateTime": "2015-01-02T00:00:00",
|
||||
"EncounterRecordNumber": "20000001ERN",
|
||||
"TotalActualPayments": 0.0000,
|
||||
"TotalCharges": 7500.0000,
|
||||
"RowID": 125904
|
||||
},
|
||||
"AllChildData": []
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,405 @@
|
||||
using Strata.SqlTools.Rules.Rule.Expression;
|
||||
|
||||
namespace Strata.SqlTools.Rules.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class MarkdownParserTests
|
||||
{
|
||||
#region Basic Literals
|
||||
|
||||
[Test]
|
||||
public void Parse_IntegerLiteral_ReturnsNumberLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("42");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<NumberLiteral>());
|
||||
Assert.That(((NumberLiteral)result).Value, Is.EqualTo(42m));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_DecimalLiteral_ReturnsNumberLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("3.14");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<NumberLiteral>());
|
||||
Assert.That(((NumberLiteral)result).Value, Is.EqualTo(3.14m));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_StringLiteral_DoubleQuotes_ReturnsStringLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("\"hello world\"");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<StringLiteral>());
|
||||
Assert.That(((StringLiteral)result).Value, Is.EqualTo("hello world"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_StringLiteral_SingleQuotes_ReturnsStringLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("'test string'");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<StringLiteral>());
|
||||
Assert.That(((StringLiteral)result).Value, Is.EqualTo("test string"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_BooleanLiteral_True_ReturnsLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("true");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Literal>());
|
||||
Assert.That(((Literal)result).Value, Is.EqualTo(true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_BooleanLiteral_False_ReturnsLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("false");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Literal>());
|
||||
Assert.That(((Literal)result).Value, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Property Access
|
||||
|
||||
[Test]
|
||||
public void Parse_SimpleProperty_ReturnsProperty()
|
||||
{
|
||||
var result = Markdown.Parse("PropertyName");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Property>());
|
||||
var prop = (Property)result;
|
||||
Assert.That(prop.PropertyName, Is.EqualTo("PropertyName"));
|
||||
Assert.That(prop.Expression, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_PropertyWithParameter_ReturnsProperty()
|
||||
{
|
||||
var result = Markdown.Parse("x.Age");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Property>());
|
||||
var prop = (Property)result;
|
||||
Assert.That(prop.PropertyName, Is.EqualTo("Age"));
|
||||
Assert.That(prop.Expression, Is.InstanceOf<Parameter>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexTextProperty_ReturnsProperty()
|
||||
{
|
||||
var result = Markdown.Parse("\\text{input.Department}");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Property>());
|
||||
var prop = (Property)result;
|
||||
Assert.That(prop.PropertyName, Is.EqualTo("Department"));
|
||||
Assert.That(prop.Expression, Is.InstanceOf<Parameter>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexTextString_ReturnsStringLiteral()
|
||||
{
|
||||
var result = Markdown.Parse("\\text{some text}");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<StringLiteral>());
|
||||
Assert.That(((StringLiteral)result).Value, Is.EqualTo("some text"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparisons
|
||||
|
||||
[Test]
|
||||
public void Parse_Equality_ReturnsEqual()
|
||||
{
|
||||
var result = Markdown.Parse("x.Age = 25");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Equal>());
|
||||
var equal = (Equal)result;
|
||||
Assert.That(equal.Left, Is.InstanceOf<Property>());
|
||||
Assert.That(equal.Right, Is.InstanceOf<NumberLiteral>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_NotEqual_ReturnsNotEqual()
|
||||
{
|
||||
var result = Markdown.Parse("x.Status != 'active'");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<NotEqual>());
|
||||
var notEqual = (NotEqual)result;
|
||||
Assert.That(notEqual.Left, Is.InstanceOf<Property>());
|
||||
Assert.That(notEqual.Right, Is.InstanceOf<StringLiteral>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexNotEqual_ReturnsNotEqual()
|
||||
{
|
||||
var result = Markdown.Parse("x.Value \\neq 100");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<NotEqual>());
|
||||
var notEqual = (NotEqual)result;
|
||||
Assert.That(notEqual.Left, Is.InstanceOf<Property>());
|
||||
Assert.That(notEqual.Right, Is.InstanceOf<NumberLiteral>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_GreaterThan_ReturnsGreaterThan()
|
||||
{
|
||||
var result = Markdown.Parse("x.Score > 100");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<GreaterThan>());
|
||||
var greaterThan = (GreaterThan)result;
|
||||
Assert.That(greaterThan.Left, Is.InstanceOf<Property>());
|
||||
Assert.That(greaterThan.Right, Is.InstanceOf<NumberLiteral>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexGreaterThan_ReturnsGreaterThan()
|
||||
{
|
||||
var result = Markdown.Parse("x.Total \\gt 500");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<GreaterThan>());
|
||||
var greaterThan = (GreaterThan)result;
|
||||
Assert.That(greaterThan.Left, Is.InstanceOf<Property>());
|
||||
Assert.That(greaterThan.Right, Is.InstanceOf<NumberLiteral>());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logical Operations
|
||||
|
||||
[Test]
|
||||
public void Parse_And_ReturnsAnd()
|
||||
{
|
||||
var result = Markdown.Parse("x.Age > 18 AND y.Active = true");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<And>());
|
||||
var and = (And)result;
|
||||
Assert.That(and.Left, Is.InstanceOf<GreaterThan>());
|
||||
Assert.That(and.Right, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_Or_ReturnsOr()
|
||||
{
|
||||
var result = Markdown.Parse("x.Status = 'pending' OR x.Status = 'active'");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Or>());
|
||||
var or = (Or)result;
|
||||
Assert.That(or.Left, Is.InstanceOf<Equal>());
|
||||
Assert.That(or.Right, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexLand_ReturnsAnd()
|
||||
{
|
||||
var result = Markdown.Parse("x.Valid = true \\land y.Count > 0");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<And>());
|
||||
var and = (And)result;
|
||||
Assert.That(and.Left, Is.InstanceOf<Equal>());
|
||||
Assert.That(and.Right, Is.InstanceOf<GreaterThan>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexLor_ReturnsOr()
|
||||
{
|
||||
var result = Markdown.Parse("a.Type = 1 \\lor b.Type = 2");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Or>());
|
||||
var or = (Or)result;
|
||||
Assert.That(or.Left, Is.InstanceOf<Equal>());
|
||||
Assert.That(or.Right, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexWedge_ReturnsAnd()
|
||||
{
|
||||
var result = Markdown.Parse("x.Active = true \\wedge x.Verified = true");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<And>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexVee_ReturnsOr()
|
||||
{
|
||||
var result = Markdown.Parse("x.Status = 'A' \\vee x.Status = 'B'");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Or>());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Complex Expressions
|
||||
|
||||
[Test]
|
||||
public void Parse_NestedParentheses_ReturnsCorrectStructure()
|
||||
{
|
||||
var result = Markdown.Parse("(x.Age > 18 AND x.Active = true) OR y.Override = true");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Or>());
|
||||
var or = (Or)result;
|
||||
Assert.That(or.Left, Is.InstanceOf<And>());
|
||||
Assert.That(or.Right, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LatexParentheses_ReturnsCorrectStructure()
|
||||
{
|
||||
var result = Markdown.Parse("\\left(x.Value = 10\\right) \\land y.Value = 20");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<And>());
|
||||
var and = (And)result;
|
||||
Assert.That(and.Left, Is.InstanceOf<Equal>());
|
||||
Assert.That(and.Right, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_ComplexExpression_ReturnsCorrectStructure()
|
||||
{
|
||||
var result = Markdown.Parse("x.Age > 18 AND (y.Status = 'active' OR y.Status = 'pending')");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<And>());
|
||||
var and = (And)result;
|
||||
Assert.That(and.Left, Is.InstanceOf<GreaterThan>());
|
||||
Assert.That(and.Right, Is.InstanceOf<Or>());
|
||||
|
||||
var or = (Or)and.Right;
|
||||
Assert.That(or.Left, Is.InstanceOf<Equal>());
|
||||
Assert.That(or.Right, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Markdown Delimiters
|
||||
|
||||
[Test]
|
||||
public void Parse_InlineMath_RemovesDelimiters()
|
||||
{
|
||||
var result = Markdown.Parse("$x.Age = 25$");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_BlockMath_RemovesDelimiters()
|
||||
{
|
||||
var result = Markdown.Parse("$$\nx.Age > 18\n$$");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<GreaterThan>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_MathCodeFence_RemovesDelimiters()
|
||||
{
|
||||
var result = Markdown.Parse("```math\nx.Active = true\n```");
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Error Handling
|
||||
|
||||
[Test]
|
||||
public void Parse_NullString_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Markdown.Parse(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_EmptyString_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Markdown.Parse(""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_WhitespaceOnly_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Markdown.Parse(" "));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_InvalidExpression_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Markdown.Parse("@#$%"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryParse_ValidExpression_ReturnsTrue()
|
||||
{
|
||||
var success = Markdown.TryParse("x.Age = 25", out var expression);
|
||||
|
||||
Assert.That(success, Is.True);
|
||||
Assert.That(expression, Is.Not.Null);
|
||||
Assert.That(expression, Is.InstanceOf<Equal>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryParse_InvalidExpression_ReturnsFalse()
|
||||
{
|
||||
var success = Markdown.TryParse("@#$%", out var expression);
|
||||
|
||||
Assert.That(success, Is.False);
|
||||
Assert.That(expression, Is.Null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Integration Tests
|
||||
|
||||
[Test]
|
||||
public void Parse_RealWorldExample_UserAgeAndStatus()
|
||||
{
|
||||
var markdown = "$$\\text{user.Age} > 18 \\land \\text{user.Status} = \\text{active}$$";
|
||||
var result = Markdown.Parse(markdown);
|
||||
|
||||
Assert.That(result, Is.InstanceOf<And>());
|
||||
var and = (And)result;
|
||||
|
||||
// Left side: user.Age > 18
|
||||
Assert.That(and.Left, Is.InstanceOf<GreaterThan>());
|
||||
var greaterThan = (GreaterThan)and.Left;
|
||||
Assert.That(greaterThan.Left, Is.InstanceOf<Property>());
|
||||
var userAge = (Property)greaterThan.Left;
|
||||
Assert.That(userAge.PropertyName, Is.EqualTo("Age"));
|
||||
|
||||
// Right side: user.Status = "active"
|
||||
Assert.That(and.Right, Is.InstanceOf<Equal>());
|
||||
var equal = (Equal)and.Right;
|
||||
Assert.That(equal.Left, Is.InstanceOf<Property>());
|
||||
var userStatus = (Property)equal.Left;
|
||||
Assert.That(userStatus.PropertyName, Is.EqualTo("Status"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_RealWorldExample_ComplexBusinessRule()
|
||||
{
|
||||
var markdown = @"$
|
||||
(invoice.TotalCharges > 1000 AND invoice.Status = 'pending')
|
||||
OR
|
||||
(invoice.Priority = 'urgent' AND invoice.ApprovedBy != '')
|
||||
$";
|
||||
|
||||
var result = Markdown.Parse(markdown);
|
||||
|
||||
Assert.That(result, Is.InstanceOf<Or>());
|
||||
var or = (Or)result;
|
||||
Assert.That(or.Left, Is.InstanceOf<And>());
|
||||
Assert.That(or.Right, Is.InstanceOf<And>());
|
||||
|
||||
// Left AND: TotalCharges > 1000 AND Status = 'pending'
|
||||
var leftAnd = (And)or.Left;
|
||||
Assert.That(leftAnd.Left, Is.InstanceOf<GreaterThan>());
|
||||
Assert.That(leftAnd.Right, Is.InstanceOf<Equal>());
|
||||
|
||||
// Right AND: Priority = 'urgent' AND ApprovedBy != ''
|
||||
var rightAnd = (And)or.Right;
|
||||
Assert.That(rightAnd.Left, Is.InstanceOf<Equal>());
|
||||
Assert.That(rightAnd.Right, Is.InstanceOf<NotEqual>());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,680 @@
|
||||
using Strata.SqlTools.Rules.Rule;
|
||||
using Strata.SqlTools.Rules.Rule.Expression;
|
||||
using GroupAnd = Strata.SqlTools.Rules.Rule.Groups.And;
|
||||
using GroupOr = Strata.SqlTools.Rules.Rule.Groups.Or;
|
||||
|
||||
namespace Strata.SqlTools.Rules.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Comprehensive unit tests for Strata.Rules based on Microsoft RulesEngine examples.
|
||||
/// These tests demonstrate basic rule creation, evaluation, and various expression types.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class RuleEngineTests
|
||||
{
|
||||
#region BasicComparison Tests
|
||||
|
||||
[Test]
|
||||
public async Task BasicEqualityRule_WithMatchingValue_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Create a simple rule: input.country == "india"
|
||||
var inputParam = new Parameter("input");
|
||||
var countryProperty = inputParam.Property("country");
|
||||
var expression = countryProperty == "india";
|
||||
|
||||
var rule = new SingleRule("CheckCountryIsIndia", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "india" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BasicEqualityRule_WithNonMatchingValue_ReturnsFalse()
|
||||
{
|
||||
// Arrange - Create a simple rule: input.country == "india"
|
||||
var inputParam = new Parameter("input");
|
||||
var countryProperty = inputParam.Property("country");
|
||||
var expression = countryProperty == "india";
|
||||
|
||||
var rule = new SingleRule("CheckCountryIsIndia", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "usa" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BasicNotEqualRule_WithDifferentValue_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Create a simple rule: input.country != "india"
|
||||
var inputParam = new Parameter("input");
|
||||
var countryProperty = inputParam.Property("country");
|
||||
var expression = countryProperty != "india";
|
||||
|
||||
var rule = new SingleRule("CheckCountryIsNotIndia", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "usa" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BasicGreaterThanRule_WithHigherValue_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Create a simple rule: input.totalPurchases > 5000
|
||||
var inputParam = new Parameter("input");
|
||||
var totalProperty = inputParam.Property("totalPurchases");
|
||||
var expression = new GreaterThan(totalProperty, 5000m);
|
||||
|
||||
var rule = new SingleRule("CheckMinimumPurchase", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { totalPurchases = 10000m };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BasicGreaterThanRule_WithLowerValue_ReturnsFalse()
|
||||
{
|
||||
// Arrange - Create a simple rule: input.totalPurchases > 5000
|
||||
var inputParam = new Parameter("input");
|
||||
var totalProperty = inputParam.Property("totalPurchases");
|
||||
var expression = new GreaterThan(totalProperty, 5000m);
|
||||
|
||||
var rule = new SingleRule("CheckMinimumPurchase", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { totalPurchases = 3000m };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logical Operator Tests
|
||||
|
||||
[Test]
|
||||
public async Task And_WithBothConditionsTrue_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Rule: input.country == "india" AND input.loyaltyFactor > 2
|
||||
var inputParam = new Parameter("input");
|
||||
var countryExpr = inputParam.Property("country") == "india";
|
||||
var loyaltyExpr = new GreaterThan(inputParam.Property("loyaltyFactor"), 2m);
|
||||
var expression = countryExpr & loyaltyExpr;
|
||||
|
||||
var rule = new SingleRule("CheckIndiaWithHighLoyalty", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "india", loyaltyFactor = 3m };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task And_WithOneConditionFalse_ReturnsFalse()
|
||||
{
|
||||
// Arrange - Rule: input.country == "india" AND input.loyaltyFactor > 2
|
||||
var inputParam = new Parameter("input");
|
||||
var countryExpr = inputParam.Property("country") == "india";
|
||||
var loyaltyExpr = new GreaterThan(inputParam.Property("loyaltyFactor"), 2m);
|
||||
var expression = countryExpr & loyaltyExpr;
|
||||
|
||||
var rule = new SingleRule("CheckIndiaWithHighLoyalty", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "india", loyaltyFactor = 1m };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Or_WithOneConditionTrue_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Rule: input.country == "india" OR input.country == "usa"
|
||||
var inputParam = new Parameter("input");
|
||||
var indiaExpr = inputParam.Property("country") == "india";
|
||||
var usaExpr = inputParam.Property("country") == "usa";
|
||||
var expression = indiaExpr | usaExpr;
|
||||
|
||||
var rule = new SingleRule("CheckIndiaOrUsa", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "usa" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Or_WithBothConditionsFalse_ReturnsFalse()
|
||||
{
|
||||
// Arrange - Rule: input.country == "india" OR input.country == "usa"
|
||||
var inputParam = new Parameter("input");
|
||||
var indiaExpr = inputParam.Property("country") == "india";
|
||||
var usaExpr = inputParam.Property("country") == "usa";
|
||||
var expression = indiaExpr | usaExpr;
|
||||
|
||||
var rule = new SingleRule("CheckIndiaOrUsa", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "canada" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ComplexLogicalExpression_WithMixedOperators_EvaluatesCorrectly()
|
||||
{
|
||||
// Arrange - Rule: (input.country == "india" AND input.loyaltyFactor >= 2) OR input.vipCustomer == true
|
||||
var inputParam = new Parameter("input");
|
||||
var countryExpr = inputParam.Property("country") == "india";
|
||||
var loyaltyExpr = new GreaterThan(inputParam.Property("loyaltyFactor"), 1m);
|
||||
var vipExpr = inputParam.Property("vipCustomer") == "true";
|
||||
|
||||
var And = countryExpr & loyaltyExpr;
|
||||
var expression = And | vipExpr;
|
||||
|
||||
var rule = new SingleRule("CheckEligibleCustomer", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "usa", loyaltyFactor = 1m, vipCustomer = "true" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True, "Should return true because vipCustomer is true");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multiple Property Access Tests
|
||||
|
||||
[Test]
|
||||
public async Task MultipleProperties_AllConditionsMet_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Similar to RulesEngine discount example
|
||||
// Rule: country == "india" AND totalPurchases >= 5000 AND totalOrders > 2
|
||||
var inputParam = new Parameter("input");
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
new GreaterThan(inputParam.Property("totalPurchases"), 4999m) &
|
||||
new GreaterThan(inputParam.Property("totalOrders"), 2m);
|
||||
|
||||
var rule = new SingleRule("GiveDiscount10", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
totalPurchases = 5000m,
|
||||
totalOrders = 3m
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task MultipleProperties_OneConditionNotMet_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var inputParam = new Parameter("input");
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
new GreaterThan(inputParam.Property("totalPurchases"), 4999m) &
|
||||
new GreaterThan(inputParam.Property("totalOrders"), 2m);
|
||||
|
||||
var rule = new SingleRule("GiveDiscount10", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
totalPurchases = 5000m,
|
||||
totalOrders = 1m // This condition fails
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rule Group Tests
|
||||
|
||||
[Test]
|
||||
public async Task AndRuleGroup_AllRulesPass_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Create multiple rules that must all pass
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var rule1 = new SingleRule("CheckCountry", inputParam.Property("country") == "india");
|
||||
var rule2 = new SingleRule("CheckLoyalty", new GreaterThan(inputParam.Property("loyaltyFactor"), 2m));
|
||||
var rule3 = new SingleRule("CheckPurchases", new GreaterThan(inputParam.Property("totalPurchases"), 5000m));
|
||||
|
||||
var andGroup = new GroupAnd(new IRule[] { rule1, rule2, rule3 });
|
||||
var ruleSet = new RuleSet(andGroup, Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
loyaltyFactor = 3m,
|
||||
totalPurchases = 10000m
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AndRuleGroup_OneRuleFails_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var rule1 = new SingleRule("CheckCountry", inputParam.Property("country") == "india");
|
||||
var rule2 = new SingleRule("CheckLoyalty", new GreaterThan(inputParam.Property("loyaltyFactor"), 2m));
|
||||
var rule3 = new SingleRule("CheckPurchases", new GreaterThan(inputParam.Property("totalPurchases"), 5000m));
|
||||
|
||||
var andGroup = new GroupAnd(new IRule[] { rule1, rule2, rule3 });
|
||||
var ruleSet = new RuleSet(andGroup, Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
loyaltyFactor = 1m, // This rule fails
|
||||
totalPurchases = 10000m
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OrRuleGroup_OneRulePasses_ReturnsTrue()
|
||||
{
|
||||
// Arrange - At least one rule must pass
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var rule1 = new SingleRule("CheckIndia", inputParam.Property("country") == "india");
|
||||
var rule2 = new SingleRule("CheckUSA", inputParam.Property("country") == "usa");
|
||||
var rule3 = new SingleRule("CheckCanada", inputParam.Property("country") == "canada");
|
||||
|
||||
var orGroup = new GroupOr(new IRule[] { rule1, rule2, rule3 });
|
||||
var ruleSet = new RuleSet(orGroup, Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "usa" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task OrRuleGroup_AllRulesFail_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var rule1 = new SingleRule("CheckIndia", inputParam.Property("country") == "india");
|
||||
var rule2 = new SingleRule("CheckUSA", inputParam.Property("country") == "usa");
|
||||
var rule3 = new SingleRule("CheckCanada", inputParam.Property("country") == "canada");
|
||||
|
||||
var orGroup = new GroupOr(new IRule[] { rule1, rule2, rule3 });
|
||||
var ruleSet = new RuleSet(orGroup, Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { country = "france" };
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Discount Calculation Example (Based on RulesEngine Documentation)
|
||||
|
||||
[Test]
|
||||
public async Task DiscountRule_10Percent_AllConditionsMet_ReturnsTrue()
|
||||
{
|
||||
// Arrange - Simulates the RulesEngine discount example
|
||||
// Rule: country == "india" AND loyaltyFactor <= 2 AND totalPurchases >= 5000 AND totalOrders > 2
|
||||
var inputParam = new Parameter("input");
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
(inputParam.Property("loyaltyFactor") == 2m) & // <= 2
|
||||
new GreaterThan(inputParam.Property("totalPurchasesToDate"), 4999m) & // >= 5000
|
||||
new GreaterThan(inputParam.Property("totalOrders"), 2m);
|
||||
|
||||
var rule = new SingleRule("GiveDiscount10", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
loyaltyFactor = 2m,
|
||||
totalPurchasesToDate = 5000m,
|
||||
totalOrders = 3m
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DiscountRule_20Percent_HigherLoyalty_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
// Rule: country == "india" AND loyaltyFactor == 3 AND totalPurchases >= 10000
|
||||
var inputParam = new Parameter("input");
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
(inputParam.Property("loyaltyFactor") == 3m) &
|
||||
new GreaterThan(inputParam.Property("totalPurchasesToDate"), 9999m);
|
||||
|
||||
var rule = new SingleRule("GiveDiscount20", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
loyaltyFactor = 3m,
|
||||
totalPurchasesToDate = 10000m
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task MultipleDiscountRules_OnlyOneMatches_UsesOrGroup()
|
||||
{
|
||||
// Arrange - Either 10% or 20% discount can apply
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var discount10 = new SingleRule("GiveDiscount10",
|
||||
(inputParam.Property("country") == "india") &
|
||||
(inputParam.Property("loyaltyFactor") == 2m) &
|
||||
new GreaterThan(inputParam.Property("totalPurchases"), 4999m));
|
||||
|
||||
var discount20 = new SingleRule("GiveDiscount20",
|
||||
(inputParam.Property("country") == "india") &
|
||||
(inputParam.Property("loyaltyFactor") == 3m) &
|
||||
new GreaterThan(inputParam.Property("totalPurchases"), 9999m));
|
||||
|
||||
var orGroup = new GroupOr(new IRule[] { discount10, discount20 });
|
||||
var ruleSet = new RuleSet(orGroup, Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
country = "india",
|
||||
loyaltyFactor = 3m,
|
||||
totalPurchases = 15000m
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True, "Should match the 20% discount rule");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String Operations
|
||||
|
||||
[Test]
|
||||
public async Task StringComparison_CaseSensitive_WorksCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var inputParam = new Parameter("input");
|
||||
var expression = inputParam.Property("status") == "Active";
|
||||
|
||||
var rule = new SingleRule("CheckActiveStatus", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
|
||||
// Act & Assert - Matching case
|
||||
var result1 = await engine.RunRules(ruleSet, new { status = "Active" });
|
||||
Assert.That(result1, Is.True);
|
||||
|
||||
// Act & Assert - Different case (should fail)
|
||||
var result2 = await engine.RunRules(ruleSet, new { status = "active" });
|
||||
Assert.That(result2, Is.False);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested Properties
|
||||
|
||||
[Test]
|
||||
public async Task NestedProperty_Access_WorksCorrectly()
|
||||
{
|
||||
// Arrange - Access nested object properties
|
||||
var inputParam = new Parameter("input");
|
||||
var customerProperty = inputParam.Property("customer");
|
||||
var expression = new Property(customerProperty, "country") == "india";
|
||||
|
||||
var rule = new SingleRule("CheckCustomerCountry", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
customer = new { country = "india", name = "John" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeepNestedProperty_MultipleLevel_WorksCorrectly()
|
||||
{
|
||||
// Arrange - Access deeply nested properties
|
||||
var inputParam = new Parameter("input");
|
||||
var orderProperty = inputParam.Property("order");
|
||||
var customerProperty = new Property(orderProperty, "customer");
|
||||
var addressProperty = new Property(customerProperty, "address");
|
||||
var expression = new Property(addressProperty, "country") == "india";
|
||||
|
||||
var rule = new SingleRule("CheckOrderCustomerCountry", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new
|
||||
{
|
||||
order = new
|
||||
{
|
||||
customer = new
|
||||
{
|
||||
address = new { country = "india", city = "Mumbai" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RuleSet Isolation Tests
|
||||
|
||||
[Test]
|
||||
public async Task MultipleRuleSets_EvaluatedIndependently()
|
||||
{
|
||||
// Arrange - Two separate rule sets
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var rule1 = new SingleRule("Rule1", new GreaterThan(inputParam.Property("value"), 100m));
|
||||
var ruleSet1 = new RuleSet(new GroupAnd(new[] { rule1 }), Guid.NewGuid());
|
||||
|
||||
var rule2 = new SingleRule("Rule2", inputParam.Property("value") == 50m);
|
||||
var ruleSet2 = new RuleSet(new GroupAnd(new[] { rule2 }), Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
var input = new { value = 50m };
|
||||
|
||||
// Act
|
||||
var result1 = await engine.RunRules(ruleSet1, input);
|
||||
var result2 = await engine.RunRules(ruleSet2, input);
|
||||
|
||||
// Assert
|
||||
Assert.That(result1, Is.False, "First rule set should fail");
|
||||
Assert.That(result2, Is.True, "Second rule set should pass");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Complex Real-World Scenario
|
||||
|
||||
[Test]
|
||||
public async Task ComplexBusinessRule_EligibilityCheck_EvaluatesCorrectly()
|
||||
{
|
||||
// Arrange - Complex eligibility rule inspired by RulesEngine examples
|
||||
// Eligible if: (Premium member) OR (Regular member with good history)
|
||||
var inputParam = new Parameter("input");
|
||||
|
||||
var premiumRule = new SingleRule("PremiumMember",
|
||||
inputParam.Property("membershipType") == "Premium");
|
||||
|
||||
var regularWithHistoryExpr =
|
||||
(inputParam.Property("membershipType") == "Regular") &
|
||||
new GreaterThan(inputParam.Property("accountAge"), 365m) &
|
||||
new GreaterThan(inputParam.Property("totalSpent"), 1000m) &
|
||||
(inputParam.Property("negativeReviews") == 0m);
|
||||
|
||||
var regularRule = new SingleRule("RegularMemberGoodHistory", regularWithHistoryExpr);
|
||||
|
||||
var eligibilityGroup = new GroupOr(new IRule[] { premiumRule, regularRule });
|
||||
var ruleSet = new RuleSet(eligibilityGroup, Guid.NewGuid());
|
||||
|
||||
var engine = new RuleSetEngine();
|
||||
|
||||
// Act & Assert - Premium member should be eligible
|
||||
var premiumInput = new
|
||||
{
|
||||
membershipType = "Premium",
|
||||
accountAge = 30m,
|
||||
totalSpent = 100m,
|
||||
negativeReviews = 5m
|
||||
};
|
||||
var premiumResult = await engine.RunRules(ruleSet, premiumInput);
|
||||
Assert.That(premiumResult, Is.True, "Premium member should be eligible");
|
||||
|
||||
// Act & Assert - Regular member with good history should be eligible
|
||||
var regularGoodInput = new
|
||||
{
|
||||
membershipType = "Regular",
|
||||
accountAge = 400m,
|
||||
totalSpent = 2000m,
|
||||
negativeReviews = 0m
|
||||
};
|
||||
var regularGoodResult = await engine.RunRules(ruleSet, regularGoodInput);
|
||||
Assert.That(regularGoodResult, Is.True, "Regular member with good history should be eligible");
|
||||
|
||||
// Act & Assert - Regular member with bad history should not be eligible
|
||||
var regularBadInput = new
|
||||
{
|
||||
membershipType = "Regular",
|
||||
accountAge = 400m,
|
||||
totalSpent = 2000m,
|
||||
negativeReviews = 3m
|
||||
};
|
||||
var regularBadResult = await engine.RunRules(ruleSet, regularBadInput);
|
||||
Assert.That(regularBadResult, Is.False, "Regular member with negative reviews should not be eligible");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
# RuleEngineTests Documentation
|
||||
|
||||
Comprehensive unit tests for Strata.Rules based on Microsoft RulesEngine patterns. This test suite demonstrates rule creation, evaluation, and various expression types.
|
||||
|
||||
## Test Structure Overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Tests[RuleEngineTests]
|
||||
Tests --> Basic[Basic Comparison Tests]
|
||||
Tests --> Logical[Logical Operator Tests]
|
||||
Tests --> Multi[Multiple Property Access Tests]
|
||||
Tests --> Groups[Rule Group Tests]
|
||||
Tests --> Discount[Discount Calculation Examples]
|
||||
Tests --> String[String Operations]
|
||||
Tests --> Nested[Nested Properties]
|
||||
Tests --> Isolation[RuleSet Isolation Tests]
|
||||
Tests --> Complex[Complex Real-World Scenarios]
|
||||
|
||||
Basic --> Equality[Equality ==]
|
||||
Basic --> NotEqual[Not Equal !=]
|
||||
Basic --> GreaterThan[Greater Than >]
|
||||
Basic --> LessThan[Less Than <]
|
||||
|
||||
Logical --> And[AND &]
|
||||
Logical --> Or[OR |]
|
||||
Logical --> Mixed[Mixed Operators]
|
||||
|
||||
Groups --> AndGroup[AND Group]
|
||||
Groups --> OrGroup[OR Group]
|
||||
|
||||
style Tests fill:#e1f5ff
|
||||
style Basic fill:#fff4e6
|
||||
style Logical fill:#e8f5e9
|
||||
style Groups fill:#f3e5f5
|
||||
style Discount fill:#fff9c4
|
||||
style Complex fill:#ffebee
|
||||
```
|
||||
|
||||
## Test Categories
|
||||
|
||||
### 1. Basic Comparison Tests
|
||||
|
||||
Tests fundamental comparison operations with single conditions.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Test
|
||||
participant Parameter
|
||||
participant Expression
|
||||
participant RuleSet
|
||||
participant Engine
|
||||
|
||||
Test->>Parameter: Create Parameter("input")
|
||||
Test->>Parameter: Get Property("country")
|
||||
Test->>Expression: Create Equality (property == "india")
|
||||
Test->>RuleSet: Create SingleRule + GroupAnd
|
||||
Test->>Engine: RunRules(ruleSet, input)
|
||||
Engine-->>Test: Return true/false
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```csharp
|
||||
// Test: input.country == "india"
|
||||
var inputParam = new Parameter("input");
|
||||
var expression = inputParam.Property("country") == "india";
|
||||
var rule = new SingleRule("CheckCountryIsIndia", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
|
||||
var result = await engine.RunRules(ruleSet, new { country = "india" });
|
||||
// Result: true
|
||||
```
|
||||
|
||||
### 2. Logical Operator Tests
|
||||
|
||||
Demonstrates combining expressions using AND (`&`) and OR (`|`) operators.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Country == #quot;india#quot;] -->|&| C[AND Expression]
|
||||
B[LoyaltyFactor > 2] -->|&| C
|
||||
C -->|Result| D[Combined Rule]
|
||||
|
||||
E[Country == #quot;india#quot;] -->|#124;| G[OR Expression]
|
||||
F[Country == #quot;usa#quot;] -->|#124;| G
|
||||
G -->|Result| H[Alternative Rule]
|
||||
|
||||
style C fill:#e8f5e9
|
||||
style G fill:#fff4e6
|
||||
```
|
||||
|
||||
**AND Example:**
|
||||
```csharp
|
||||
// Rule: input.country == "india" AND input.loyaltyFactor > 2
|
||||
var countryExpr = inputParam.Property("country") == "india";
|
||||
var loyaltyExpr = new GreaterThan(inputParam.Property("loyaltyFactor"), 2m);
|
||||
var expression = countryExpr & loyaltyExpr;
|
||||
```
|
||||
|
||||
**OR Example:**
|
||||
```csharp
|
||||
// Rule: input.country == "india" OR input.country == "usa"
|
||||
var indiaExpr = inputParam.Property("country") == "india";
|
||||
var usaExpr = inputParam.Property("country") == "usa";
|
||||
var expression = indiaExpr | usaExpr;
|
||||
```
|
||||
|
||||
**Mixed Operators Example:**
|
||||
```csharp
|
||||
// Rule: (country == "india" AND loyaltyFactor >= 2) OR vipCustomer == true
|
||||
var And = countryExpr & loyaltyExpr;
|
||||
var expression = And | vipExpr;
|
||||
```
|
||||
|
||||
### 3. Multiple Property Access Tests
|
||||
|
||||
Tests rules that evaluate multiple properties from the same input object.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Input[Input Object] --> P1[country]
|
||||
Input --> P2[totalPurchases]
|
||||
Input --> P3[totalOrders]
|
||||
|
||||
P1 -->|==| E1[#quot;india#quot;]
|
||||
P2 -->|>=| E2[5000]
|
||||
P3 -->|>| E3[2]
|
||||
|
||||
E1 -->|&| Combine[Combined Expression]
|
||||
E2 -->|&| Combine
|
||||
E3 -->|&| Combine
|
||||
|
||||
Combine --> Result[Evaluation Result]
|
||||
|
||||
style Input fill:#e1f5ff
|
||||
style Combine fill:#e8f5e9
|
||||
style Result fill:#fff4e6
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```csharp
|
||||
// Rule: country == "india" AND totalPurchases >= 5000 AND totalOrders > 2
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
new GreaterThan(inputParam.Property("totalPurchases"), 4999m) &
|
||||
new GreaterThan(inputParam.Property("totalOrders"), 2m);
|
||||
```
|
||||
|
||||
### 4. Rule Group Tests
|
||||
|
||||
Demonstrates organizing multiple rules into AND or OR groups.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph AND Group [All Rules Must Pass]
|
||||
R1[Rule 1: Age > 18]
|
||||
R2[Rule 2: Status == Active]
|
||||
R3[Rule 3: Balance > 0]
|
||||
R1 --> AndEval[AND Evaluation]
|
||||
R2 --> AndEval
|
||||
R3 --> AndEval
|
||||
end
|
||||
|
||||
subgraph OR Group [Any Rule Can Pass]
|
||||
R4[Rule 4: Premium Member]
|
||||
R5[Rule 5: Loyalty > 5 years]
|
||||
R6[Rule 6: Total Spent > $10k]
|
||||
R4 --> OrEval[OR Evaluation]
|
||||
R5 --> OrEval
|
||||
R6 --> OrEval
|
||||
end
|
||||
|
||||
AndEval --> Final[Final Result]
|
||||
OrEval --> Final
|
||||
|
||||
style AndEval fill:#e8f5e9
|
||||
style OrEval fill:#fff4e6
|
||||
```
|
||||
|
||||
**AND Group Example:**
|
||||
```csharp
|
||||
// All rules must pass
|
||||
var rule1 = new SingleRule("R1", inputParam.Property("age") > 18m);
|
||||
var rule2 = new SingleRule("R2", inputParam.Property("status") == "Active");
|
||||
var andGroup = new GroupAnd(new[] { rule1, rule2 });
|
||||
var ruleSet = new RuleSet(andGroup, Guid.NewGuid());
|
||||
```
|
||||
|
||||
**OR Group Example:**
|
||||
```csharp
|
||||
// Any rule can pass
|
||||
var rule1 = new SingleRule("R1", inputParam.Property("premium") == "true");
|
||||
var rule2 = new SingleRule("R2", new GreaterThan(inputParam.Property("loyalty"), 5m));
|
||||
var orGroup = new GroupOr(new[] { rule1, rule2 });
|
||||
var ruleSet = new RuleSet(orGroup, Guid.NewGuid());
|
||||
```
|
||||
|
||||
### 5. Discount Calculation Examples
|
||||
|
||||
Real-world examples based on Microsoft RulesEngine discount calculation scenarios.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Customer Input] --> Check1{Country == #quot;india#quot;?}
|
||||
Check1 -->|No| NoDiscount[No Discount]
|
||||
Check1 -->|Yes| Check2{Loyalty Factor?}
|
||||
|
||||
Check2 -->|== 2| Check3{Purchases >= $5000<br/>AND Orders > 2?}
|
||||
Check2 -->|== 3| Check4{Purchases >= $10000?}
|
||||
|
||||
Check3 -->|Yes| Discount10[10% Discount]
|
||||
Check3 -->|No| NoDiscount
|
||||
|
||||
Check4 -->|Yes| Discount20[20% Discount]
|
||||
Check4 -->|No| Check3
|
||||
|
||||
style Discount10 fill:#c8e6c9
|
||||
style Discount20 fill:#81c784
|
||||
style NoDiscount fill:#ffcdd2
|
||||
```
|
||||
|
||||
**10% Discount Rule:**
|
||||
```csharp
|
||||
// Rule: country == "india" AND loyaltyFactor <= 2
|
||||
// AND totalPurchases >= 5000 AND totalOrders > 2
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
(inputParam.Property("loyaltyFactor") == 2m) &
|
||||
new GreaterThan(inputParam.Property("totalPurchasesToDate"), 4999m) &
|
||||
new GreaterThan(inputParam.Property("totalOrders"), 2m);
|
||||
|
||||
var rule = new SingleRule("GiveDiscount10", expression);
|
||||
```
|
||||
|
||||
**20% Discount Rule:**
|
||||
```csharp
|
||||
// Rule: country == "india" AND loyaltyFactor == 3
|
||||
// AND totalPurchases >= 10000
|
||||
var expression =
|
||||
(inputParam.Property("country") == "india") &
|
||||
(inputParam.Property("loyaltyFactor") == 3m) &
|
||||
new GreaterThan(inputParam.Property("totalPurchasesToDate"), 9999m);
|
||||
|
||||
var rule = new SingleRule("GiveDiscount20", expression);
|
||||
```
|
||||
|
||||
**Multiple Discount Rules (OR Group):**
|
||||
```csharp
|
||||
// Either 10% or 20% discount can apply
|
||||
var discount10 = new SingleRule("GiveDiscount10", /* 10% expression */);
|
||||
var discount20 = new SingleRule("GiveDiscount20", /* 20% expression */);
|
||||
var orGroup = new GroupOr(new IRule[] { discount10, discount20 });
|
||||
```
|
||||
|
||||
### 6. String Operations
|
||||
|
||||
Tests string comparison operations including case-sensitivity.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[String Property] --> B{Comparison Type}
|
||||
B -->|Case Sensitive| C[Exact Match]
|
||||
B -->|StartsWith| D[Prefix Check]
|
||||
B -->|Contains| E[Substring Search]
|
||||
B -->|EndsWith| F[Suffix Check]
|
||||
|
||||
C --> Result[Boolean Result]
|
||||
D --> Result
|
||||
E --> Result
|
||||
F --> Result
|
||||
|
||||
style A fill:#e1f5ff
|
||||
style Result fill:#e8f5e9
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```csharp
|
||||
// Case-sensitive string comparison
|
||||
var expression = inputParam.Property("name") == "John Doe";
|
||||
|
||||
// Tests verify exact string matching
|
||||
var input = new { name = "John Doe" }; // Returns true
|
||||
var input2 = new { name = "john doe" }; // Returns false
|
||||
```
|
||||
|
||||
### 7. Nested Properties
|
||||
|
||||
Demonstrates accessing nested object properties.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Root[Root Object] --> Child1[customer]
|
||||
Root --> Child2[address]
|
||||
Root --> Child3[account]
|
||||
|
||||
Child1 --> CP1[name]
|
||||
Child1 --> CP2[age]
|
||||
|
||||
Child2 --> AP1[street]
|
||||
Child2 --> AP2[city]
|
||||
Child2 --> AP3[country]
|
||||
|
||||
Child3 --> AC1[balance]
|
||||
Child3 --> AC2[type]
|
||||
|
||||
AP3 -.->|Property Access| Expr[Expression: address.country == #quot;USA#quot;]
|
||||
|
||||
style Root fill:#e1f5ff
|
||||
style Expr fill:#e8f5e9
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```csharp
|
||||
// Accessing nested properties: input.address.country
|
||||
var addressProp = inputParam.Property("address");
|
||||
var countryProp = addressProp.Property("country");
|
||||
var expression = countryProp == "USA";
|
||||
|
||||
// Test with nested object
|
||||
var input = new
|
||||
{
|
||||
address = new { country = "USA", city = "New York" }
|
||||
};
|
||||
```
|
||||
|
||||
### 8. RuleSet Isolation Tests
|
||||
|
||||
Verifies that multiple RuleSets evaluate independently without interference.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Test
|
||||
participant Engine
|
||||
participant RuleSet1
|
||||
participant RuleSet2
|
||||
|
||||
Test->>Engine: Create Engine
|
||||
Test->>RuleSet1: Create RuleSet (Condition A)
|
||||
Test->>RuleSet2: Create RuleSet (Condition B)
|
||||
|
||||
Test->>Engine: RunRules(RuleSet1, input)
|
||||
Engine->>RuleSet1: Evaluate
|
||||
RuleSet1-->>Engine: Result 1
|
||||
Engine-->>Test: Result 1
|
||||
|
||||
Test->>Engine: RunRules(RuleSet2, input)
|
||||
Engine->>RuleSet2: Evaluate
|
||||
RuleSet2-->>Engine: Result 2
|
||||
Engine-->>Test: Result 2
|
||||
|
||||
Note over Test,RuleSet2: Each RuleSet evaluates independently
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```csharp
|
||||
// RuleSet 1: Check if country is India
|
||||
var ruleSet1 = new RuleSet(
|
||||
new GroupAnd(new[] {
|
||||
new SingleRule("R1", inputParam.Property("country") == "india")
|
||||
}),
|
||||
Guid.NewGuid()
|
||||
);
|
||||
|
||||
// RuleSet 2: Check if country is USA
|
||||
var ruleSet2 = new RuleSet(
|
||||
new GroupAnd(new[] {
|
||||
new SingleRule("R2", inputParam.Property("country") == "usa")
|
||||
}),
|
||||
Guid.NewGuid()
|
||||
);
|
||||
|
||||
// Each evaluates independently
|
||||
var result1 = await engine.RunRules(ruleSet1, input); // Can be true or false
|
||||
var result2 = await engine.RunRules(ruleSet2, input); // Independent result
|
||||
```
|
||||
|
||||
### 9. Complex Real-World Scenarios
|
||||
|
||||
Demonstrates sophisticated business rules combining multiple patterns.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Eligibility Check] --> Type{Membership Type?}
|
||||
|
||||
Type -->|Premium| Eligible[Eligible ✓]
|
||||
|
||||
Type -->|Regular| Age{Account Age<br/>> 365 days?}
|
||||
Age -->|No| NotEligible[Not Eligible ✗]
|
||||
Age -->|Yes| Spent{Total Spent<br/>> $1000?}
|
||||
|
||||
Spent -->|No| NotEligible
|
||||
Spent -->|Yes| Reviews{Negative Reviews<br/>== 0?}
|
||||
|
||||
Reviews -->|No| NotEligible
|
||||
Reviews -->|Yes| Eligible
|
||||
|
||||
style Eligible fill:#c8e6c9
|
||||
style NotEligible fill:#ffcdd2
|
||||
style Start fill:#e1f5ff
|
||||
```
|
||||
|
||||
**Complex Business Rule Example:**
|
||||
```csharp
|
||||
// Eligible if: (Premium member) OR (Regular member with good history)
|
||||
var premiumRule = new SingleRule("PremiumMember",
|
||||
inputParam.Property("membershipType") == "Premium");
|
||||
|
||||
var regularWithHistoryExpr =
|
||||
(inputParam.Property("membershipType") == "Regular") &
|
||||
new GreaterThan(inputParam.Property("accountAge"), 365m) &
|
||||
new GreaterThan(inputParam.Property("totalSpent"), 1000m) &
|
||||
(inputParam.Property("negativeReviews") == 0m);
|
||||
|
||||
var regularRule = new SingleRule("RegularMemberGoodHistory", regularWithHistoryExpr);
|
||||
|
||||
var eligibilityGroup = new GroupOr(new IRule[] { premiumRule, regularRule });
|
||||
var ruleSet = new RuleSet(eligibilityGroup, Guid.NewGuid());
|
||||
```
|
||||
|
||||
## Rule Evaluation Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Input[Input Object]
|
||||
participant Param[Parameter]
|
||||
participant Prop[Property]
|
||||
participant Expr[Expression]
|
||||
participant Rule[SingleRule]
|
||||
participant Group[Group AND/OR]
|
||||
participant Set[RuleSet]
|
||||
participant Engine[RuleSetEngine]
|
||||
participant MSEngine[Microsoft RulesEngine]
|
||||
|
||||
Input->>Param: Provide data context
|
||||
Param->>Prop: Access property chain
|
||||
Prop->>Expr: Build expression tree
|
||||
Expr->>Rule: Create rule with name
|
||||
Rule->>Group: Add to group
|
||||
Group->>Set: Build RuleSet
|
||||
Set->>Engine: Submit for evaluation
|
||||
Engine->>MSEngine: Convert to RulesEngine format
|
||||
MSEngine->>Engine: Return evaluation result
|
||||
Engine->>Input: Return boolean result
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### Pattern 1: Simple Property Comparison
|
||||
```csharp
|
||||
var inputParam = new Parameter("input");
|
||||
var expression = inputParam.Property("propertyName") == value;
|
||||
var rule = new SingleRule("RuleName", expression);
|
||||
var ruleSet = new RuleSet(new GroupAnd(new[] { rule }), Guid.NewGuid());
|
||||
```
|
||||
|
||||
### Pattern 2: Multiple Conditions (AND)
|
||||
```csharp
|
||||
var expr1 = inputParam.Property("prop1") == value1;
|
||||
var expr2 = inputParam.Property("prop2") > value2;
|
||||
var combined = expr1 & expr2;
|
||||
```
|
||||
|
||||
### Pattern 3: Alternative Conditions (OR)
|
||||
```csharp
|
||||
var expr1 = inputParam.Property("prop1") == value1;
|
||||
var expr2 = inputParam.Property("prop2") == value2;
|
||||
var combined = expr1 | expr2;
|
||||
```
|
||||
|
||||
### Pattern 4: Multiple Rules in Groups
|
||||
```csharp
|
||||
var rule1 = new SingleRule("R1", expression1);
|
||||
var rule2 = new SingleRule("R2", expression2);
|
||||
|
||||
// All must pass
|
||||
var andGroup = new GroupAnd(new[] { rule1, rule2 });
|
||||
|
||||
// Any can pass
|
||||
var orGroup = new GroupOr(new[] { rule1, rule2 });
|
||||
```
|
||||
|
||||
### Pattern 5: Nested Property Access
|
||||
```csharp
|
||||
var parentProp = inputParam.Property("parent");
|
||||
var childProp = parentProp.Property("child");
|
||||
var expression = childProp == value;
|
||||
```
|
||||
|
||||
## Test Execution
|
||||
|
||||
All tests are executed asynchronously using NUnit:
|
||||
|
||||
```csharp
|
||||
[Test]
|
||||
public async Task TestName()
|
||||
{
|
||||
// Arrange - Set up rules and input
|
||||
var expression = /* create expression */;
|
||||
var ruleSet = /* create ruleset */;
|
||||
var input = /* create input object */;
|
||||
|
||||
// Act - Execute rule engine
|
||||
var result = await engine.RunRules(ruleSet, input);
|
||||
|
||||
// Assert - Verify expected outcome
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Strata.Rules**: Core rule engine library
|
||||
- **Strata.Rules.Rule**: Rule definitions (SingleRule, RuleSet, IRule)
|
||||
- **Strata.Rules.Rule.Expression**: Expression types (Parameter, Property, Equal, GreaterThan, etc.)
|
||||
- **Strata.Rules.Rule.Groups**: Grouping logic (And, Or)
|
||||
- **Microsoft RulesEngine**: Underlying evaluation engine
|
||||
- **NUnit**: Testing framework
|
||||
|
||||
## Test Statistics
|
||||
|
||||
- **Total Tests**: 34
|
||||
- **Test Categories**: 9
|
||||
- **Success Rate**: 100%
|
||||
- **Execution Time**: ~1 second
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [ClassDiagram.md](../Strata.Rules/ClassDiagram.md) - Expression class hierarchy
|
||||
- [RuleVisitorTests.md](RuleVisitorTests.md) - Visitor pattern tests
|
||||
@@ -0,0 +1,144 @@
|
||||
using Strata.SqlTools.Rules.Rule;
|
||||
using Strata.SqlTools.Rules.Rule.Expression;
|
||||
using ExpressionAnd = Strata.SqlTools.Rules.Rule.Expression.And;
|
||||
using ExpressionOr = Strata.SqlTools.Rules.Rule.Expression.Or;
|
||||
using GroupAnd = Strata.SqlTools.Rules.Rule.Groups.And;
|
||||
|
||||
namespace Strata.SqlTools.Rules.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class VisitorTests
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
|
||||
var eqExpr = new Equal(1, 3);
|
||||
var grExpr = new GreaterThan(20, 19);
|
||||
|
||||
var orExpr = new ExpressionOr(eqExpr, grExpr);
|
||||
|
||||
var propCharges = new Property("input", "TotalCharges");
|
||||
var grChargesExpr = new GreaterThan(propCharges, 100.0m);
|
||||
|
||||
var propName = new Property("input", "DepartmentName");
|
||||
var eqNameExpr = new Equal(propName, new StringLiteral("foobar"));
|
||||
|
||||
orExpr = new ExpressionOr(orExpr, grChargesExpr);
|
||||
var andExpr = new ExpressionAnd(orExpr, eqNameExpr);
|
||||
|
||||
var visitor = new TestModifierVisitor();
|
||||
var modified = visitor.Visit(andExpr);
|
||||
var writer = new LocalVisitor();
|
||||
|
||||
var output = writer.Visit(modified);
|
||||
|
||||
TestContext.WriteLine(output);
|
||||
Assert.That(modified, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test2()
|
||||
{
|
||||
System.Linq.Expressions.Expression<Func<string[], bool>> any = items => items.Any(s => s.Length >= 4);
|
||||
|
||||
TestContext.WriteLine(any);
|
||||
|
||||
var ruleSet = CreateRuleSet();
|
||||
|
||||
var visitor = new LocalVisitor();
|
||||
var condition = visitor.Visit(ruleSet.Expression);
|
||||
TestContext.WriteLine(condition);
|
||||
Assert.That(condition, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test3()
|
||||
{
|
||||
var input = new TestRuleInput()
|
||||
{
|
||||
DepartmentId = 3,
|
||||
PatientId = 5,
|
||||
Details = new TestRuleDetail[]
|
||||
{
|
||||
new() { CPTCode = "123", UBRevCode = "0999" },
|
||||
new() { CPTCode = "456", UBRevCode = "0250" }
|
||||
}
|
||||
};
|
||||
|
||||
var ruleSet = CreateWithRuleSet();
|
||||
|
||||
var rulesEngine = new RuleSetEngine();
|
||||
var success = await rulesEngine.RunRules(ruleSet, input);
|
||||
|
||||
var visitor = new LocalVisitor();
|
||||
var condition = visitor.Visit(ruleSet.Expression);
|
||||
TestContext.WriteLine(condition);
|
||||
|
||||
Assert.That(success, Is.True);
|
||||
}
|
||||
|
||||
private static RuleSet CreateRuleSet()
|
||||
{
|
||||
var parameter = new Parameter("input");
|
||||
var collectionProperty = parameter.CollectionProperty("Details");
|
||||
|
||||
var any = collectionProperty.Any(p => p.Property("CPTCode") == "123");
|
||||
|
||||
var rootGroup = new GroupAnd(new IRule[]
|
||||
{
|
||||
new SingleRule("R1", new Equal(parameter.Property("DepartmentId"), 3)),
|
||||
new SingleRule("R2", new Equal(parameter.Property("PatientId"), 5)),
|
||||
new SingleRule("R3", any)
|
||||
});
|
||||
|
||||
return new RuleSet(rootGroup, Guid.NewGuid());
|
||||
}
|
||||
|
||||
private static RuleSet CreateWithRuleSet()
|
||||
{
|
||||
var parameter = new Parameter("input");
|
||||
var details = parameter.CollectionProperty("Details");
|
||||
var any = details.Any(p => p.Property("CPTCode") == "123" & p.Property("UBRevCode") == "0999");
|
||||
|
||||
var rootGroup = new GroupAnd(new IRule[]
|
||||
{
|
||||
//new SingleRule("R1", new Equal(new Property(details, "UBRevCode"), "0999")),
|
||||
//new SingleRule("R2", new Equal(new Property(details, "CPTCode"), "123"))
|
||||
new SingleRule("R1", any)
|
||||
});
|
||||
|
||||
return new RuleSet(rootGroup, Guid.NewGuid());
|
||||
}
|
||||
}
|
||||
|
||||
public class TestModifierVisitor : Visitor
|
||||
{
|
||||
public override Strata.SqlTools.Rules.Rule.Expression.Expression Visit(Strata.SqlTools.Rules.Rule.Expression.IVisitable expression)
|
||||
{
|
||||
if (expression is NumberLiteral num)
|
||||
{
|
||||
var modified = new NumberLiteral(num + 100);
|
||||
return base.VisitLiteral(modified);
|
||||
}
|
||||
|
||||
return base.Visit(expression);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestRuleInput
|
||||
{
|
||||
public int DepartmentId { get; set; }
|
||||
public int PatientId { get; set; }
|
||||
|
||||
public string? ImGoingToMakeThisNull { get; set; } = null;
|
||||
|
||||
public TestRuleDetail[] Details { get; set; } = { };
|
||||
}
|
||||
|
||||
public class TestRuleDetail
|
||||
{
|
||||
public string? CPTCode { get; set; }
|
||||
public string? UBRevCode { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Strata.SqlTools.Rules\Strata.SqlTools.Rules.csproj" />
|
||||
<ProjectReference Include="..\..\src\Strata.SqlTools.SqlServer\Strata.SqlTools.SqlServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Data.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,183 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Strata.SqlTools.SqlBreakdown.Expressions;
|
||||
using LinqExpression = System.Linq.Expressions.Expression;
|
||||
|
||||
namespace Strata.SqlTools.Rules.Tests;
|
||||
|
||||
public class Tests
|
||||
{
|
||||
private static readonly Guid _pesDataTableGUID = new Guid("41639c8f-fecf-4449-b6e6-53f796c0c3e4");
|
||||
private const string EncounterIDKey = "DimPatientEnEncounterID";
|
||||
|
||||
public Dictionary<long, IHierarchicalData>? _encounterLookup;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
var allData = JsonSerializer.Deserialize<List<IHierarchicalData>>(File.ReadAllText(@"Data.json"), new JsonSerializerOptions
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
new HierarchicalDataConverter(),
|
||||
new FlatDataConverter(),
|
||||
new ObjectToInferredTypesConverter()
|
||||
}
|
||||
});
|
||||
|
||||
if (allData == null || allData.Count == 0)
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
var rootDatas = allData.Where(x => x.DataSourceGuid == _pesDataTableGUID).ToList();
|
||||
|
||||
foreach (var rootData in rootDatas)
|
||||
{
|
||||
var encounterID = (long)rootData.Data[EncounterIDKey];
|
||||
var childData = allData.Where(x => (long)x.Data[EncounterIDKey] == encounterID && x.DataSourceGuid != rootData.DataSourceGuid);
|
||||
rootData.AllChildData = childData;
|
||||
}
|
||||
|
||||
_encounterLookup = rootDatas.ToDictionary(x => (long)x.Data[EncounterIDKey]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test3()
|
||||
{
|
||||
var dictionary = new Dictionary<string, object>();
|
||||
dictionary["key1"] = "value1";
|
||||
dictionary["RowID"] = 123;
|
||||
|
||||
var dictionaryParam = LinqExpression.Parameter(typeof(Dictionary<string, object>));
|
||||
|
||||
var indexProperty = GetIndexProperty(dictionary.GetType());
|
||||
|
||||
var indexerExpr = LinqExpression.MakeIndex(dictionaryParam, indexProperty, new[] { LinqExpression.Constant("key1") });
|
||||
|
||||
var lambda = LinqExpression.Lambda<Func<Dictionary<string, object>, object>>(indexerExpr, dictionaryParam).Compile();
|
||||
|
||||
var result = lambda(dictionary);
|
||||
|
||||
Assert.That(result, Is.EqualTo("value1"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test4()
|
||||
{
|
||||
if (_encounterLookup == null || _encounterLookup.Count == 0)
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
IHierarchicalData encounter = _encounterLookup.First().Value;
|
||||
|
||||
var inputProperty = new InputPropertyExpression(Guid.Parse("41639c8f-fecf-4449-b6e6-53f796c0c3e4"), "DischargeDateTime");
|
||||
|
||||
var func = GetDataExpression(inputProperty);
|
||||
LambdaExpression lambda = func;
|
||||
var compiledLambda = lambda.Compile();
|
||||
compiledLambda.DynamicInvoke(encounter);
|
||||
}
|
||||
|
||||
private static Expression<Func<IHierarchicalData, object>> GetDataExpression(InputPropertyExpression Property)
|
||||
{
|
||||
if (Property.SecondaryDataSource == null)
|
||||
{
|
||||
Expression<Func<IHierarchicalData, object>> func = hd => hd.Data[Property.DataKeyLookup];
|
||||
return func;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("should not happen");
|
||||
}
|
||||
|
||||
private static PropertyInfo GetIndexProperty(Type type)
|
||||
{
|
||||
return type.GetProperties().First(x => x.GetIndexParameters().Length > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test1()
|
||||
{
|
||||
// Addition is an add expression for "1 + 2"
|
||||
var one = LinqExpression.Constant(1, typeof(int));
|
||||
var two = LinqExpression.Constant(2, typeof(int));
|
||||
var addition = LinqExpression.Add(one, two);
|
||||
|
||||
var fifty = LinqExpression.Constant(50, typeof(int));
|
||||
var lessThan = LinqExpression.LessThanOrEqual(addition, fifty);
|
||||
|
||||
var expr = lessThan;
|
||||
|
||||
// Print out the expression.
|
||||
var exprString = expr.ToString();
|
||||
TestContext.Out.WriteLine(exprString);
|
||||
|
||||
var lambda = LinqExpression.Lambda<Func<bool>>(expr).Compile();
|
||||
|
||||
var result = lambda();
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[TestCase("01", ExpectedResult = true)]
|
||||
[TestCase("012C", ExpectedResult = true)]
|
||||
[TestCase("012c", ExpectedResult = true)]
|
||||
[TestCase("oasd", ExpectedResult = false)]
|
||||
public bool Test2(string value)
|
||||
{
|
||||
var foobaz = new Foobaz
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
{ "FirstName", "Bob" }
|
||||
}
|
||||
};
|
||||
|
||||
var param = LinqExpression.Parameter(typeof(Foobaz), "input");
|
||||
var memberExpression = LinqExpression.Property(param, "DepartmentCode");
|
||||
|
||||
var startsWithCall = StartsWith(memberExpression, value);
|
||||
|
||||
Expression<Func<Foobaz, bool>> lambda = LinqExpression.Lambda<Func<Foobaz, bool>>(startsWithCall, param);
|
||||
|
||||
TestContext.WriteLine(lambda.ToString());
|
||||
|
||||
var result = lambda.Compile()(foobaz);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static System.Linq.Expressions.Expression StartsWith(System.Linq.Expressions.Expression expression, string value)
|
||||
{
|
||||
return StartsWith(expression, LinqExpression.Constant(value));
|
||||
}
|
||||
|
||||
static MethodCallExpression StartsWith(System.Linq.Expressions.Expression expression, System.Linq.Expressions.Expression valueExpression)
|
||||
{
|
||||
var startWithMethodInfo = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string), typeof(StringComparison) })!;
|
||||
//"".StartsWith("", StringComparison.OrdinalIgnoreCase)
|
||||
var call = LinqExpression.Call(expression, startWithMethodInfo, valueExpression, LinqExpression.Constant(StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return call;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class FoobarBase
|
||||
{
|
||||
public abstract string DepartmentCode { get; }
|
||||
}
|
||||
|
||||
public class Foobaz : FoobarBase
|
||||
{
|
||||
public Foobaz()
|
||||
{
|
||||
DepartmentId = DateTime.Now.Ticks;
|
||||
}
|
||||
|
||||
public override string DepartmentCode => "012C3";
|
||||
|
||||
public long DepartmentId { get; }
|
||||
|
||||
public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using NUnit.Framework;
|
||||
Reference in New Issue
Block a user