/**
* This method will validate the given device association details.
* In case the validation fails, an exception will be thrown with a relevant error message.
* @param workspace - the shop workspace
* @param deviceAssociationDto - shop device DAO
* @param action - indicates which action type is handled
* @param accountId - the id of the account
* @param customerRefIdsForAccount
* @throws ValidationException
*/
private List<ValidationExceptionMessages> validateAction(Long workspace, DeviceAssociationDetailsDto deviceAssociationDto, DeviceAssociationSaveAction action, String accountId, List<String> customerRefIdsForAccount) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT);
List<ValidationExceptionMessages> validationExceptionMessages = new ArrayList<>();
if (action != null && action.equals(DeviceAssociationSaveAction.RMA_ASSIGN_DEVICE)) {
return validationExceptionMessages;
}
if (StringUtils.isEmpty(deviceAssociationDto.getSerial())) {
validationExceptionMessages.add(ValidationExceptionMessages.SERIAL_IS_MANDATORY);
}
if(StringUtils.isNotEmpty(deviceAssociationDto.getSerial()) && !verifySerialExistInCustomer(accountId, deviceAssociationDto.getSerial(), customerRefIdsForAccount)){
validationExceptionMessages.add(ValidationExceptionMessages.INVALID_SERIAL);
}
// if (StringUtils.isEmpty(deviceAssociationDto.getShopCode()) && deviceAssociationDto.getSubRegionId()==null && deviceAssociationDto.getRegionId()==0) {
if (deviceAssociationDto.getEntityId() == 0) {
validationExceptionMessages.add(ValidationExceptionMessages.ILLEGAL_REGION); // At least region id should be filled
}
try {
if (StringUtils.isEmpty(deviceAssociationDto.getFrom())) {
validationExceptionMessages.add(ValidationExceptionMessages.FROM_DATE_MANDATORY);
} else if(!TimeUtils.isValidDateFormat(deviceAssociationDto.getFrom(), DATE_FORMAT)) {
validationExceptionMessages.add(ValidationExceptionMessages.INVALID_DATE_FORMAT);
} else if(action.equals(DeviceAssociationSaveAction.ADD_ASSIGNMENT_HISTORY)){
Date from = simpleDateFormat.parse(deviceAssociationDto.getFrom());
Date to = simpleDateFormat.parse(deviceAssociationDto.getTo());
if(!(from.before(new Date()) || from.equals(new Date()))) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_HISTORY_CAN_NOT_BE_IN_THE_FUTURE);
}
if (to.before(from)) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_HISTORY_TO_BEFORE_FROM);
}
}
// Partial mandatory fields validations, if mandatory field is missing --> no need to continue the validation
if (!validationExceptionMessages.isEmpty()) {
return validationExceptionMessages;
}
CustomerElementType elementType = CustomerElementType.valueOf(deviceAssociationDto.getAssignmentEntityType());
if(elementType.equals(CustomerElementType.REGION) && deviceAssociationDto.getEntityId()==0) {
validationExceptionMessages.add(ValidationExceptionMessages.ILLEGAL_REGION);
} else if(elementType.equals(CustomerElementType.SUB_REGION) && deviceAssociationDto.getEntityId()==0) {
// Long shopIdByCode = organizationStructureService.getShopIdByCode(deviceAssociationDto.getShopCode(), workspace);
// if (StringUtils.isNotEmpty(deviceAssociationDto.getShopCode()) && (shopIdByCode == null)) {
validationExceptionMessages.add(ValidationExceptionMessages.SHOP_CODE_NOT_EXIST);
// }
}
DeviceDto device = deviceService.getDeviceBySerial(deviceAssociationDto.getSerial());
if (device == null) {
validationExceptionMessages.add(ValidationExceptionMessages.DEVICE_ASSOCIATION_NOT_EXIST);
}
if(action.equals(DeviceAssociationSaveAction.ADD_ASSIGNMENT_HISTORY)){
if(StringUtils.isEmpty(deviceAssociationDto.getTo())) {
validationExceptionMessages.add(ValidationExceptionMessages.TO_DATE_MANDATORY);
} else if(!TimeUtils.isValidDateFormat(deviceAssociationDto.getTo(), DATE_FORMAT)) {
validationExceptionMessages.add(ValidationExceptionMessages.INVALID_DATE_FORMAT);
}else {
Date to = null;
if (StringUtils.isNotEmpty(deviceAssociationDto.getTo())) {
to = simpleDateFormat.parse(deviceAssociationDto.getTo());
}
if(!(to.before(new Date()) || to.equals(new Date()))) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_HISTORY_CAN_NOT_BE_IN_THE_FUTURE);
}
}
}
List<DeviceAssociation> deviceAssociationHistories = accountDeviceDao.getDeviceAssociationHistory(accountId, deviceAssociationDto.getSerial());
if (CollectionUtils.isNotEmpty(deviceAssociationHistories)) {
Date from = simpleDateFormat.parse(deviceAssociationDto.getFrom());
Date to = null;
if (StringUtils.isNotEmpty(deviceAssociationDto.getTo())) {
to = simpleDateFormat.parse(deviceAssociationDto.getTo());
}
for (DeviceAssociation deviceAssociationHistory : deviceAssociationHistories) {
// Avoid comparing range to itself
if(deviceAssociationHistory.getId() == deviceAssociationDto.getId()) {
continue;
}
// Verify there is no conflict with current shop assignment
if(deviceAssociationHistory.getToDate()==null && to!=null) {
// [from]=currentAssignmentFrom
if (from.equals(deviceAssociationHistory.getFromDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
// [to]>=currentAssignmentFrom
if (to.equals(deviceAssociationHistory.getFromDate()) || to.after(deviceAssociationHistory.getFromDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
}
// [from]=rangeFrom or [from]=rangeTo
if (from.equals(deviceAssociationHistory.getFromDate()) || from.equals(deviceAssociationHistory.getToDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
// Verify there is no conflict with history shop assignment
if (deviceAssociationHistory.getToDate() != null && to != null) {
// [to]=rangeFrom or [to]=rangeTo
if (to.equals(deviceAssociationHistory.getFromDate()) || to.equals(deviceAssociationHistory.getToDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
// rangeFrom < [from] < rangeTo
if (from.after(deviceAssociationHistory.getFromDate()) && from.before(deviceAssociationHistory.getToDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
// rangeFrom < [to] < rangeTo
if (to.after(deviceAssociationHistory.getFromDate()) && to.before(deviceAssociationHistory.getToDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
// [from] < rangeFrom and rangeTo < [to]
if (from.before(deviceAssociationHistory.getFromDate()) && to.after(deviceAssociationHistory.getToDate())) {
validationExceptionMessages.add(ValidationExceptionMessages.ASSIGNMENT_ALREADY_EXIST_FOR_DEVICE);
}
}
}
}
} catch (ParseException e) {
e.printStackTrace();
}
return validationExceptionMessages;
}154 lines of code in a single method...
AccountDeviceServiceImpl.java
Or...
Got a bug
Knew exactly where the issue was
Only to realize...
EVEN IF THEY AGREE THEY ARE IMPORTANT
“Why do most developers fear making continuous changes to their code? They are afraid they’ll break it! Why are they afraid they’ll break it? Because they don’t have tests.”
~ Robert C Martin (Clean Code)
it's more about designing with tests.
Collaboration
Unit Tests
Integration tests
e2e
mocks
fakes
Test doubles
stubs
refactoring
contract tests
example/properties tests
mocking
spys
dummy objects
Acceptance testing
Behavior-driven development (BDD)
Continuous testing
Happy Path
TDD helps with but does not guarantee, good design & good code. Skill, talent, and expertise remain necessary
Keep tests small and focused.
Use meaningful names.
Test behavior, not implementation
Critical for UI testing
Maintain a fast test suite.
Don’t skip the "Refactor" step!
Baby steps - instead of large-scale changes
Continuous refactoring - instead of late quality improvements
Evolutionary design - instead of big upfront
Executable documentation - instead of static documents
Minimalist code - instead of a gold-plated solution