|
1
|
|
package com.rbctcsworld.ecommerce.audit; |
|
2
|
|
|
|
3
|
|
import java.math.BigDecimal; |
|
4
|
|
import java.util.ArrayList; |
|
5
|
|
import java.util.LinkedHashMap; |
|
6
|
|
import java.util.List; |
|
7
|
|
import java.util.Map; |
|
8
|
|
import java.util.Objects; |
|
9
|
|
|
|
10
|
|
/** One changed field of one record: what the audit trail stores per row. */ |
|
11
|
|
public record AuditChange(String entityType, long entityId, String action, String field, String oldValue, |
|
12
|
|
String newValue) { |
|
13
|
|
|
|
14
|
|
public static final String PRODUCT = "PRODUCT"; |
|
15
|
|
public static final String ORDER = "ORDER"; |
|
16
|
|
public static final String CUSTOMER = "CUSTOMER"; |
|
17
|
|
public static final String COUPON = "COUPON"; |
|
18
|
|
public static final String WAREHOUSE = "WAREHOUSE"; |
|
19
|
|
public static final int MAX_VALUE = 500; |
|
20
|
|
|
|
21
|
|
/** |
|
22
|
|
* The fields whose value differs between before and after, in the order of {@code after}. |
|
23
|
|
* {@code before} = null means the record was created (every field with a value is listed). |
|
24
|
|
* Numbers are compared by value: 12.5 and 12.50 are the same price. |
|
25
|
|
*/ |
|
26
|
|
public static List<AuditChange> diff(String entityType, long entityId, String action, |
|
27
|
|
Map<String, Object> before, Map<String, Object> after) { |
|
28
|
|
List<AuditChange> changes = new ArrayList<>(); |
|
29
|
|
for (Map.Entry<String, Object> e : after.entrySet()) { |
|
30
|
1
1. diff : removed conditional - replaced equality check with false → KILLED
|
Object old = before == null ? null : before.get(e.getKey()); |
|
31
|
1
1. diff : removed conditional - replaced equality check with false → KILLED
|
if (!same(old, e.getValue())) { |
|
32
|
|
changes.add(new AuditChange(entityType, entityId, action, e.getKey(), text(old), text(e.getValue()))); |
|
33
|
|
} |
|
34
|
|
} |
|
35
|
1
1. diff : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/audit/AuditChange::diff → KILLED
|
return changes; |
|
36
|
|
} |
|
37
|
|
|
|
38
|
|
/** Convenience for building the before/after maps in field order. */ |
|
39
|
|
public static Map<String, Object> fields(Object... namesAndValues) { |
|
40
|
|
Map<String, Object> m = new LinkedHashMap<>(); |
|
41
|
2
1. fields : removed conditional - replaced comparison check with false → KILLED
2. fields : changed conditional boundary → KILLED
|
for (int i = 0; i < namesAndValues.length; i += 2) { |
|
42
|
1
1. fields : Replaced integer addition with subtraction → KILLED
|
m.put((String) namesAndValues[i], namesAndValues[i + 1]); |
|
43
|
|
} |
|
44
|
1
1. fields : replaced return value with Collections.emptyMap for com/rbctcsworld/ecommerce/audit/AuditChange::fields → KILLED
|
return m; |
|
45
|
|
} |
|
46
|
|
|
|
47
|
|
static boolean same(Object a, Object b) { |
|
48
|
2
1. same : removed conditional - replaced equality check with false → KILLED
2. same : removed conditional - replaced equality check with false → KILLED
|
if (a instanceof BigDecimal x && b instanceof BigDecimal y) { |
|
49
|
2
1. same : replaced boolean return with true for com/rbctcsworld/ecommerce/audit/AuditChange::same → KILLED
2. same : removed conditional - replaced equality check with false → KILLED
|
return x.compareTo(y) == 0; |
|
50
|
|
} |
|
51
|
2
1. same : replaced boolean return with true for com/rbctcsworld/ecommerce/audit/AuditChange::same → KILLED
2. same : replaced boolean return with false for com/rbctcsworld/ecommerce/audit/AuditChange::same → KILLED
|
return Objects.equals(a, b); |
|
52
|
|
} |
|
53
|
|
|
|
54
|
|
/** As stored: plain text, BigDecimal without exponent, at most MAX_VALUE characters, null stays null. */ |
|
55
|
|
static String text(Object value) { |
|
56
|
2
1. text : replaced return value with "" for com/rbctcsworld/ecommerce/audit/AuditChange::text → KILLED
2. text : removed conditional - replaced equality check with false → KILLED
|
if (value == null) return null; |
|
57
|
1
1. text : removed conditional - replaced equality check with false → KILLED
|
String s = value instanceof BigDecimal d ? d.toPlainString() : String.valueOf(value); |
|
58
|
3
1. text : changed conditional boundary → SURVIVED
2. text : replaced return value with "" for com/rbctcsworld/ecommerce/audit/AuditChange::text → KILLED
3. text : removed conditional - replaced comparison check with false → KILLED
|
return s.length() > MAX_VALUE ? s.substring(0, MAX_VALUE) : s; |
|
59
|
|
} |
|
60
|
|
} |
| | Mutations |
| 30 |
|
1.1 Location : diff Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:aCreatedRecordListsEveryFieldThatHasAValue()] removed conditional - replaced equality check with false → KILLED
|
| 31 |
|
1.1 Location : diff Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:aCreatedRecordListsEveryFieldThatHasAValue()] removed conditional - replaced equality check with false → KILLED
|
| 35 |
|
1.1 Location : diff Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:aCreatedRecordListsEveryFieldThatHasAValue()] replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/audit/AuditChange::diff → KILLED
|
| 41 |
|
1.1 Location : fields Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:fieldsKeepsTheGivenOrder()] removed conditional - replaced comparison check with false → KILLED
2.2 Location : fields Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:fieldsKeepsTheGivenOrder()] changed conditional boundary → KILLED
|
| 42 |
|
1.1 Location : fields Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:fieldsKeepsTheGivenOrder()] Replaced integer addition with subtraction → KILLED
|
| 44 |
|
1.1 Location : fields Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:fieldsKeepsTheGivenOrder()] replaced return value with Collections.emptyMap for com/rbctcsworld/ecommerce/audit/AuditChange::fields → KILLED
|
| 48 |
|
1.1 Location : same Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:theSamePriceWrittenDifferentlyIsNoChange()] removed conditional - replaced equality check with false → KILLED
2.2 Location : same Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:theSamePriceWrittenDifferentlyIsNoChange()] removed conditional - replaced equality check with false → KILLED
|
| 49 |
|
1.1 Location : same Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:theSamePriceWrittenDifferentlyIsNoChange()] replaced boolean return with true for com/rbctcsworld/ecommerce/audit/AuditChange::same → KILLED
2.2 Location : same Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:theSamePriceWrittenDifferentlyIsNoChange()] removed conditional - replaced equality check with false → KILLED
|
| 51 |
|
1.1 Location : same Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:theSamePriceWrittenDifferentlyIsNoChange()] replaced boolean return with true for com/rbctcsworld/ecommerce/audit/AuditChange::same → KILLED
2.2 Location : same Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:theSamePriceWrittenDifferentlyIsNoChange()] replaced boolean return with false for com/rbctcsworld/ecommerce/audit/AuditChange::same → KILLED
|
| 56 |
|
1.1 Location : text Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:valuesAreStoredAsPlainTextOfAtMostFiveHundredCharacters()] replaced return value with "" for com/rbctcsworld/ecommerce/audit/AuditChange::text → KILLED
2.2 Location : text Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:valuesAreStoredAsPlainTextOfAtMostFiveHundredCharacters()] removed conditional - replaced equality check with false → KILLED
|
| 57 |
|
1.1 Location : text Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:valuesAreStoredAsPlainTextOfAtMostFiveHundredCharacters()] removed conditional - replaced equality check with false → KILLED
|
| 58 |
|
1.1 Location : text Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:valuesAreStoredAsPlainTextOfAtMostFiveHundredCharacters()] replaced return value with "" for com/rbctcsworld/ecommerce/audit/AuditChange::text → KILLED
2.2 Location : text Killed by : none changed conditional boundary → SURVIVED
Covering tests
Covered by tests:
- com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:valuesAreStoredAsPlainTextOfAtMostFiveHundredCharacters()]
- com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:aCreatedRecordListsEveryFieldThatHasAValue()]
- com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:aClearedValueIsStoredAsNull()]
- com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:onlyTheFieldsThatChangedAreListedInOrder()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:aDeletedProductIsAuditedAsNoLongerActive()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:deleteIsSoftDelete()]
- com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest]/[method:switchingOnAgainNeedsNoChecks()]
- com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest]/[method:anEditIsAuditedWithOnlyWhatChanged()]
- com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest]/[method:aWarehouseIsSwitchedOffOnlyWhenEmptyAndNotTheLastOne()]
- com.rbctcsworld.ecommerce.coupon.CouponServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.coupon.CouponServiceTest]/[method:aNewCouponIsAuditedWithItsTerms()]
- com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.inventory.WarehouseServiceTest]/[method:aNewWarehouseGetsANormalisedCodeAndStateAndIsAudited()]
- com.rbctcsworld.ecommerce.coupon.CouponServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.coupon.CouponServiceTest]/[method:anEditChangesTheTermsKeepsTheCodeAndAuditsOnlyWhatChanged()]
- com.rbctcsworld.ecommerce.coupon.CouponServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.coupon.CouponServiceTest]/[method:createStoresANormalisedCouponAndAllowsExactly100Percent()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:aNewProductsStockIsPlacedInTheDefaultWarehouse()]
- com.rbctcsworld.ecommerce.product.ProductVariantTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductVariantTest]/[method:aProductWithoutVariantFieldsStaysASingleProduct()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:theProductFormsStockGoesThroughTheInventoryBeforeTheOtherFields()]
- com.rbctcsworld.ecommerce.coupon.CouponServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.coupon.CouponServiceTest]/[method:createRejectsDuplicateCodeAndEndBeforeStart()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:createKeepsThePicture()]
- com.rbctcsworld.ecommerce.product.ProductVariantTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductVariantTest]/[method:onlyASizeOrOnlyAColourIsEnough()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:aNewProductIsAuditedWithEveryValue()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:updateReplacesOrRemovesThePicture()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:productMayKeepItsOwnSkuOnUpdate()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:anUpdateIsAuditedFieldByFieldWithOnlyWhatChanged()]
- com.rbctcsworld.ecommerce.product.ProductServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductServiceTest]/[method:updateChangesEveryField_regressionForStockOnlyBug()]
- com.rbctcsworld.ecommerce.product.ProductVariantTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductVariantTest]/[method:aNewVariantIsSavedWithItsGroupInUpperCaseAndTidySizeAndColour()]
- com.rbctcsworld.ecommerce.product.ProductVariantTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductVariantTest]/[method:theVariantFieldsAreAudited()]
- com.rbctcsworld.ecommerce.product.ProductVariantTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductVariantTest]/[method:aDifferentSizeOrColourInTheSameGroupIsFine()]
- com.rbctcsworld.ecommerce.product.ProductVariantTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.product.ProductVariantTest]/[method:anUpdateMayKeepItsOwnSizeAndColourAndAPutWithoutThemEndsTheVariant()]
- com.rbctcsworld.ecommerce.coupon.CouponServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.coupon.CouponServiceTest]/[method:anEditFollowsTheSameRulesAsCreate()]
3.3 Location : text Killed by : com.rbctcsworld.ecommerce.audit.AuditChangeTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.audit.AuditChangeTest]/[method:valuesAreStoredAsPlainTextOfAtMostFiveHundredCharacters()] removed conditional - replaced comparison check with false → KILLED
|