From 2a29bb0cdb8a991e716bd88f7d6a332dcc336340 Mon Sep 17 00:00:00 2001 From: l646505418 <646505418@qq.com> Date: Mon, 15 May 2023 20:28:23 +0800 Subject: [PATCH] fix:add translation field_name and field_value validation --- ...TranslationFieldAndReferenceValidator.java | 493 +++++++++++------- ...slationFieldAndReferenceValidatorTest.java | 475 +++++++++-------- 2 files changed, 580 insertions(+), 388 deletions(-) diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java index 65c4e75cf9..c13efe6df3 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java @@ -19,8 +19,16 @@ import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.WARNING; import com.google.common.collect.ImmutableList; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.inject.Inject; + import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice.FileRefs; import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator; @@ -46,217 +54,336 @@ @GtfsValidator public class TranslationFieldAndReferenceValidator extends FileValidator { - private final GtfsTranslationTableContainer translationTable; - - private final GtfsFeedContainer feedContainer; + private final GtfsTranslationTableContainer translationTable; - @Inject - TranslationFieldAndReferenceValidator( - GtfsTranslationTableContainer translationTable, GtfsFeedContainer feedContainer) { - this.translationTable = translationTable; - this.feedContainer = feedContainer; - } + private final GtfsFeedContainer feedContainer; - @Override - public void validate(NoticeContainer noticeContainer) { - // The legacy Google translation format does not define `translations.table_name` field. - if (!translationTable.getHeader().hasColumn(GtfsTranslation.TABLE_NAME_FIELD_NAME)) { - // Skip validation if legacy Google translation format is detected. - return; + @Inject + TranslationFieldAndReferenceValidator( + GtfsTranslationTableContainer translationTable, GtfsFeedContainer feedContainer) { + this.translationTable = translationTable; + this.feedContainer = feedContainer; } - // If GtfsTranslationSchema.java is patched to enable the legacy Google format, then - // fields field_name, language and table_name become optional. Here we have detected that - // table_name header is present, so this is the standard GTFS translation format. Check that the - // standard required fields are present. - if (!validateStandardRequiredFields(noticeContainer)) { - return; + + @Override + public void validate(NoticeContainer noticeContainer) { + // The legacy Google translation format does not define `translations.table_name` field. + if (!translationTable.getHeader().hasColumn(GtfsTranslation.TABLE_NAME_FIELD_NAME)) { + // Skip validation if legacy Google translation format is detected. + return; + } + // If GtfsTranslationSchema.java is patched to enable the legacy Google format, then + // fields field_name, language and table_name become optional. Here we have detected that + // table_name header is present, so this is the standard GTFS translation format. Check that the + // standard required fields are present. + if (!validateStandardRequiredFields(noticeContainer)) { + return; + } + for (GtfsTranslation translation : translationTable.getEntities()) { + validateTranslation(translation, noticeContainer); + } } - for (GtfsTranslation translation : translationTable.getEntities()) { - validateTranslation(translation, noticeContainer); + + /** + * Emits errors for missing required fields field_name, language and table_name. + * + * @return true if all required fields are set for all entities, false otherwise + */ + private boolean validateStandardRequiredFields(NoticeContainer noticeContainer) { + boolean isValid = true; + for (GtfsTranslation translation : translationTable.getEntities()) { + if (!translation.hasFieldName()) { + noticeContainer.addValidationNotice( + new MissingRequiredFieldNotice( + GtfsTranslation.FILENAME, + translation.csvRowNumber(), + GtfsTranslation.FIELD_NAME_FIELD_NAME)); + isValid = false; + } + if (!translation.hasLanguage()) { + noticeContainer.addValidationNotice( + new MissingRequiredFieldNotice( + GtfsTranslation.FILENAME, + translation.csvRowNumber(), + GtfsTranslation.LANGUAGE_FIELD_NAME)); + isValid = false; + } + if (!translation.hasTableName()) { + noticeContainer.addValidationNotice( + new MissingRequiredFieldNotice( + GtfsTranslation.FILENAME, + translation.csvRowNumber(), + GtfsTranslation.TABLE_NAME_FIELD_NAME)); + isValid = false; + } + } + return isValid; } - } - - /** - * Emits errors for missing required fields field_name, language and table_name. - * - * @return true if all required fields are set for all entities, false otherwise - */ - private boolean validateStandardRequiredFields(NoticeContainer noticeContainer) { - boolean isValid = true; - for (GtfsTranslation translation : translationTable.getEntities()) { - if (!translation.hasFieldName()) { - noticeContainer.addValidationNotice( - new MissingRequiredFieldNotice( - GtfsTranslation.FILENAME, - translation.csvRowNumber(), - GtfsTranslation.FIELD_NAME_FIELD_NAME)); - isValid = false; - } - if (!translation.hasLanguage()) { - noticeContainer.addValidationNotice( - new MissingRequiredFieldNotice( - GtfsTranslation.FILENAME, - translation.csvRowNumber(), - GtfsTranslation.LANGUAGE_FIELD_NAME)); - isValid = false; - } - if (!translation.hasTableName()) { - noticeContainer.addValidationNotice( - new MissingRequiredFieldNotice( - GtfsTranslation.FILENAME, - translation.csvRowNumber(), - GtfsTranslation.TABLE_NAME_FIELD_NAME)); - isValid = false; - } + + /** + * Validates a single row in {@code translations.txt}. + */ + private void validateTranslation(GtfsTranslation translation, NoticeContainer noticeContainer) { + if (translation.hasFieldValue()) { + if (translation.hasRecordId()) { + noticeContainer.addValidationNotice( + new TranslationUnexpectedValueNotice( + translation, GtfsTranslation.RECORD_ID_FIELD_NAME, translation.recordId())); + } + if (translation.hasRecordSubId()) { + noticeContainer.addValidationNotice( + new TranslationUnexpectedValueNotice( + translation, GtfsTranslation.RECORD_SUB_ID_FIELD_NAME, translation.recordSubId())); + } + } + Optional> parentTable = + feedContainer.getTableForFilename(translation.tableName() + ".txt"); + if (parentTable.isEmpty() || parentTable.get().isMissingFile()) { + noticeContainer.addValidationNotice(new TranslationUnknownTableNameNotice(translation)); + } else if (!translation.hasFieldValue()) { + validateReferenceIntegrity(translation, parentTable.get(), noticeContainer); + } else { + validateFiledValueReferenceIntegrity(translation, parentTable.get(), noticeContainer); + } } - return isValid; - } - - /** Validates a single row in {@code translations.txt}. */ - private void validateTranslation(GtfsTranslation translation, NoticeContainer noticeContainer) { - if (translation.hasFieldValue()) { - if (translation.hasRecordId()) { - noticeContainer.addValidationNotice( - new TranslationUnexpectedValueNotice( - translation, GtfsTranslation.RECORD_ID_FIELD_NAME, translation.recordId())); - } - if (translation.hasRecordSubId()) { - noticeContainer.addValidationNotice( - new TranslationUnexpectedValueNotice( - translation, GtfsTranslation.RECORD_SUB_ID_FIELD_NAME, translation.recordSubId())); - } + + /** + * Checks that {@code record_id, record_sub_id} fields are properly assigned and reference an + * existing row in the parent table. + */ + private void validateReferenceIntegrity( + GtfsTranslation translation, + GtfsTableContainer parentTable, + NoticeContainer noticeContainer) { + ImmutableList keyColumnNames = parentTable.getKeyColumnNames(); + if (isMissingOrUnexpectedField( + translation, + translation.hasRecordId(), + keyColumnNames.size() >= 1, + GtfsTranslation.RECORD_ID_FIELD_NAME, + translation.recordId(), + noticeContainer) + || isMissingOrUnexpectedField( + translation, + translation.hasRecordSubId(), + keyColumnNames.size() >= 2, + GtfsTranslation.RECORD_SUB_ID_FIELD_NAME, + translation.recordSubId(), + noticeContainer)) { + return; + } + Optional entity = + parentTable.byTranslationKey(translation.recordId(), translation.recordSubId()); + if (entity.isEmpty()) { + noticeContainer.addValidationNotice(new TranslationForeignKeyViolationNotice(translation)); + } } - Optional> parentTable = - feedContainer.getTableForFilename(translation.tableName() + ".txt"); - if (parentTable.isEmpty() || parentTable.get().isMissingFile()) { - noticeContainer.addValidationNotice(new TranslationUnknownTableNameNotice(translation)); - } else if (!translation.hasFieldValue()) { - validateReferenceIntegrity(translation, parentTable.get(), noticeContainer); + + /** + * Checks that {@code field_name} is present in parent table and {@code field_value} reference an + * existing row in the parent table. + */ + private static void validateFiledValueReferenceIntegrity( + GtfsTranslation translation, + GtfsTableContainer parentTable, + NoticeContainer noticeContainer) { + + try { + Method getFieldValueMethod = parentTable.getEntityClass().getMethod(underlineToCamel(translation.fieldName())); + List entitiesFindByValue = parentTable.getEntities().stream().parallel().filter(gtfsEntity -> { + try { + return getFieldValueMethod.invoke(gtfsEntity).toString().equals(translation.fieldValue()); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }).collect(Collectors.toList()); + if (entitiesFindByValue.size() == 0) + noticeContainer.addValidationNotice(new TranslationNotFoundValueNotice(translation, translation.fieldName(), translation.fieldValue())); + } catch (NoSuchMethodException e) { + noticeContainer.addValidationNotice(new TranslationUnexpectedNameNotice(translation, translation.fieldName())); + } } - } - - /** - * Checks that {@code record_id, record_sub_id} fields are properly assigned and reference an - * existing row in the parent table. - */ - private void validateReferenceIntegrity( - GtfsTranslation translation, - GtfsTableContainer parentTable, - NoticeContainer noticeContainer) { - ImmutableList keyColumnNames = parentTable.getKeyColumnNames(); - if (isMissingOrUnexpectedField( - translation, - translation.hasRecordId(), - keyColumnNames.size() >= 1, - GtfsTranslation.RECORD_ID_FIELD_NAME, - translation.recordId(), - noticeContainer) - || isMissingOrUnexpectedField( - translation, - translation.hasRecordSubId(), - keyColumnNames.size() >= 2, - GtfsTranslation.RECORD_SUB_ID_FIELD_NAME, - translation.recordSubId(), - noticeContainer)) { - return; + + /** + * A translation references an missing row according to field_value in parent table. + */ + @GtfsValidationNotice(severity = ERROR, files = @FileRefs(GtfsTranslationSchema.class)) + static class TranslationUnexpectedNameNotice extends ValidationNotice { + + /** + * The row number of the faulty record. + */ + private final int csvRowNumber; + + /** + * The name of the field that didn't exist in table. + */ + private final String fieldName; + + TranslationUnexpectedNameNotice( + GtfsTranslation translation, String fieldName) { + super(SeverityLevel.ERROR); + this.csvRowNumber = translation.csvRowNumber(); + this.fieldName = fieldName; + } } - Optional entity = - parentTable.byTranslationKey(translation.recordId(), translation.recordSubId()); - if (entity.isEmpty()) { - noticeContainer.addValidationNotice(new TranslationForeignKeyViolationNotice(translation)); + + /** + * Checks that a field is present (or missing) as expected and emits errors if not. + * + * @return whether the field is missing or unexpected + */ + private static boolean isMissingOrUnexpectedField( + GtfsTranslation translation, + boolean actualPresence, + boolean expectedPresence, + String fieldName, + String fieldValue, + NoticeContainer noticeContainer) { + if (expectedPresence == actualPresence) { + return false; + } + if (actualPresence) { + noticeContainer.addValidationNotice( + new TranslationUnexpectedValueNotice(translation, fieldName, fieldValue)); + } else { + noticeContainer.addValidationNotice( + new MissingRequiredFieldNotice( + GtfsTranslation.FILENAME, translation.csvRowNumber(), fieldName)); + } + return true; } - } - - /** - * Checks that a field is present (or missing) as expected and emits errors if not. - * - * @return whether the field is missing or unexpected - */ - private static boolean isMissingOrUnexpectedField( - GtfsTranslation translation, - boolean actualPresence, - boolean expectedPresence, - String fieldName, - String fieldValue, - NoticeContainer noticeContainer) { - if (expectedPresence == actualPresence) { - return false; + + private static String underlineToCamel(String str) { + + StringBuffer sb = new StringBuffer(); + str = str.toLowerCase(); + Matcher matcher = Pattern.compile("_[a-z]").matcher(str); + while (matcher.find()) { + matcher.appendReplacement(sb, matcher.group(0).toUpperCase().replace("_", "")); + } + matcher.appendTail(sb); + return sb.toString(); } - if (actualPresence) { - noticeContainer.addValidationNotice( - new TranslationUnexpectedValueNotice(translation, fieldName, fieldValue)); - } else { - noticeContainer.addValidationNotice( - new MissingRequiredFieldNotice( - GtfsTranslation.FILENAME, translation.csvRowNumber(), fieldName)); + + + /** + * A translation references an unknown field in parent table. + */ + @GtfsValidationNotice(severity = ERROR, files = @FileRefs(GtfsTranslationSchema.class)) + static class TranslationNotFoundValueNotice extends ValidationNotice { + + /** + * The row number of the faulty record. + */ + private final int csvRowNumber; + + /** + * The name of the field that cannot find entities by value. + */ + private final String fieldName; + /** + * The value of the field that not be found in table. + */ + private final String fieldValue; + + TranslationNotFoundValueNotice( + GtfsTranslation translation, String fieldName, String fieldValue) { + super(SeverityLevel.ERROR); + this.csvRowNumber = translation.csvRowNumber(); + this.fieldValue = fieldValue; + this.fieldName = fieldName; + } } - return true; - } - /** A field in a translations row has value but must be empty. */ - @GtfsValidationNotice(severity = ERROR, files = @FileRefs(GtfsTranslationSchema.class)) - static class TranslationUnexpectedValueNotice extends ValidationNotice { + /** + * A field in a translations row has value but must be empty. + */ + @GtfsValidationNotice(severity = ERROR, files = @FileRefs(GtfsTranslationSchema.class)) + static class TranslationUnexpectedValueNotice extends ValidationNotice { - /** The row number of the faulty record. */ - private final int csvRowNumber; + /** + * The row number of the faulty record. + */ + private final int csvRowNumber; - /** The name of the field that was expected to be empty. */ - private final String fieldName; + /** + * The name of the field that was expected to be empty. + */ + private final String fieldName; - /** Actual value of the field that was expected to be empty. */ - private final String fieldValue; + /** + * Actual value of the field that was expected to be empty. + */ + private final String fieldValue; - TranslationUnexpectedValueNotice( - GtfsTranslation translation, String fieldName, String fieldValue) { - super(SeverityLevel.ERROR); - this.csvRowNumber = translation.csvRowNumber(); - this.fieldValue = fieldValue; - this.fieldName = fieldName; + TranslationUnexpectedValueNotice( + GtfsTranslation translation, String fieldName, String fieldValue) { + super(SeverityLevel.ERROR); + this.csvRowNumber = translation.csvRowNumber(); + this.fieldValue = fieldValue; + this.fieldName = fieldName; + } } - } - /** A translation references an unknown or missing GTFS table. */ - @GtfsValidationNotice(severity = WARNING, files = @FileRefs(GtfsTranslationSchema.class)) - static class TranslationUnknownTableNameNotice extends ValidationNotice { + /** + * A translation references an unknown or missing GTFS table. + */ + @GtfsValidationNotice(severity = WARNING, files = @FileRefs(GtfsTranslationSchema.class)) + static class TranslationUnknownTableNameNotice extends ValidationNotice { - /** The row number of the faulty record. */ - private final int csvRowNumber; + /** + * The row number of the faulty record. + */ + private final int csvRowNumber; - /** `table_name` of the faulty record. */ - private final String tableName; + /** + * `table_name` of the faulty record. + */ + private final String tableName; - TranslationUnknownTableNameNotice(GtfsTranslation translation) { - super(SeverityLevel.WARNING); - this.csvRowNumber = translation.csvRowNumber(); - this.tableName = translation.tableName(); + TranslationUnknownTableNameNotice(GtfsTranslation translation) { + super(SeverityLevel.WARNING); + this.csvRowNumber = translation.csvRowNumber(); + this.tableName = translation.tableName(); + } } - } - /** - * An entity with the given {@code record_id, record_sub_id} cannot be found in the referenced - * table. - */ - @GtfsValidationNotice(severity = ERROR, files = @FileRefs(GtfsTranslationSchema.class)) - static class TranslationForeignKeyViolationNotice extends ValidationNotice { + /** + * An entity with the given {@code record_id, record_sub_id} cannot be found in the referenced + * table. + */ + @GtfsValidationNotice(severity = ERROR, files = @FileRefs(GtfsTranslationSchema.class)) + static class TranslationForeignKeyViolationNotice extends ValidationNotice { - /** The row number of the faulty record. */ - private final int csvRowNumber; + /** + * The row number of the faulty record. + */ + private final int csvRowNumber; - /** `table_name` of the faulty record. */ - private final String tableName; + /** + * `table_name` of the faulty record. + */ + private final String tableName; - /** `record_id` of the faulty record. */ - private final String recordId; + /** + * `record_id` of the faulty record. + */ + private final String recordId; - /** `record_sub_id` of the faulty record. */ - private final String recordSubId; + /** + * `record_sub_id` of the faulty record. + */ + private final String recordSubId; - TranslationForeignKeyViolationNotice(GtfsTranslation translation) { - super(SeverityLevel.ERROR); - this.csvRowNumber = translation.csvRowNumber(); - this.tableName = translation.tableName(); - this.recordId = translation.recordId(); - this.recordSubId = translation.recordSubId(); + TranslationForeignKeyViolationNotice(GtfsTranslation translation) { + super(SeverityLevel.ERROR); + this.csvRowNumber = translation.csvRowNumber(); + this.tableName = translation.tableName(); + this.recordId = translation.recordId(); + this.recordSubId = translation.recordSubId(); + } } - } } diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidatorTest.java index 56ae7c366c..c7f68d0863 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidatorTest.java @@ -19,8 +19,10 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; + import java.util.List; import java.util.Locale; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -43,224 +45,287 @@ @RunWith(JUnit4.class) public final class TranslationFieldAndReferenceValidatorTest { - private static final GtfsAgency AGENCY = new GtfsAgency.Builder().setAgencyId("agency0").build(); - private static final GtfsStopTime STOP_TIME = - new GtfsStopTime.Builder().setTripId("trip0").setStopSequence(0).build(); - private static final GtfsFeedInfo FEED_INFO = - new GtfsFeedInfo.Builder().setFeedLang(Locale.CANADA).build(); + private static final GtfsAgency AGENCY = new GtfsAgency.Builder().setAgencyId("agency0").setAgencyEmail("test@gmail.com").build(); + private static final GtfsStopTime STOP_TIME = + new GtfsStopTime.Builder().setTripId("trip0").setStopSequence(0).build(); + private static final GtfsFeedInfo FEED_INFO = + new GtfsFeedInfo.Builder().setFeedLang(Locale.CANADA).build(); + + private static final String[] NEW_FORMAT_CSV_HEADERS = + new String[]{ + "table_name", + "field_name", + "language", + "translation", + "record_id", + "record_sub_id", + "field_value", + }; + + private static List generateNotices( + CsvHeader translationHeader, List translations) { + NoticeContainer noticeContainer = new NoticeContainer(); + GtfsTranslationTableContainer translationTable = + GtfsTranslationTableContainer.forHeaderAndEntities( + translationHeader, translations, noticeContainer); + new TranslationFieldAndReferenceValidator( + translationTable, + new GtfsFeedContainer( + ImmutableList.of( + translationTable, + GtfsAgencyTableContainer.forEntities(ImmutableList.of(AGENCY), noticeContainer), + GtfsStopTimeTableContainer.forEntities( + ImmutableList.of(STOP_TIME), noticeContainer), + GtfsFeedInfoTableContainer.forEntities( + ImmutableList.of(FEED_INFO), noticeContainer)))) + .validate(noticeContainer); + return noticeContainer.getValidationNotices(); + } + + @Test + public void legacyFormat_yieldsNoNotice() { + GtfsTranslation translation = new GtfsTranslation.Builder().setCsvRowNumber(2).build(); + assertThat(generateNotices(CsvHeader.EMPTY, ImmutableList.of(translation))).isEmpty(); + } + + @Test + public void missingRequiredStandardFields_yieldsNotice() { + GtfsTranslation translation = new GtfsTranslation.Builder().setCsvRowNumber(2).build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly( + new MissingRequiredFieldNotice("translations.txt", 2, "table_name"), + new MissingRequiredFieldNotice("translations.txt", 2, "field_name"), + new MissingRequiredFieldNotice("translations.txt", 2, "language")); + } - private static final String[] NEW_FORMAT_CSV_HEADERS = - new String[] { - "table_name", - "field_name", - "language", - "translation", - "record_id", - "record_sub_id", - "field_value", - }; + @Test + public void wrongTableName_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("wrong") + .setFieldName("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly(new TranslationUnknownTableNameNotice(translation)); + } - private static List generateNotices( - CsvHeader translationHeader, List translations) { - NoticeContainer noticeContainer = new NoticeContainer(); - GtfsTranslationTableContainer translationTable = - GtfsTranslationTableContainer.forHeaderAndEntities( - translationHeader, translations, noticeContainer); - new TranslationFieldAndReferenceValidator( - translationTable, - new GtfsFeedContainer( - ImmutableList.of( - translationTable, - GtfsAgencyTableContainer.forEntities(ImmutableList.of(AGENCY), noticeContainer), - GtfsStopTimeTableContainer.forEntities( - ImmutableList.of(STOP_TIME), noticeContainer), - GtfsFeedInfoTableContainer.forEntities( - ImmutableList.of(FEED_INFO), noticeContainer)))) - .validate(noticeContainer); - return noticeContainer.getValidationNotices(); - } + @Test + public void fieldValueDefined_yieldsNoNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setFieldValue("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .isEmpty(); + } - @Test - public void legacyFormat_yieldsNoNotice() { - GtfsTranslation translation = new GtfsTranslation.Builder().setCsvRowNumber(2).build(); - assertThat(generateNotices(CsvHeader.EMPTY, ImmutableList.of(translation))).isEmpty(); - } + @Test + public void recordIdAndFieldValueDefined_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setRecordId("id") + .setFieldValue("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly(new TranslationUnexpectedValueNotice(translation, "record_id", "id")); + } - @Test - public void missingRequiredStandardFields_yieldsNotice() { - GtfsTranslation translation = new GtfsTranslation.Builder().setCsvRowNumber(2).build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly( - new MissingRequiredFieldNotice("translations.txt", 2, "table_name"), - new MissingRequiredFieldNotice("translations.txt", 2, "field_name"), - new MissingRequiredFieldNotice("translations.txt", 2, "language")); - } + @Test + public void recordSubIdAndFieldValueDefined_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setRecordSubId("sub_id") + .setFieldValue("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly( + new TranslationUnexpectedValueNotice(translation, "record_sub_id", "sub_id")); + } - @Test - public void wrongTableName_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("wrong") - .setFieldName("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly(new TranslationUnknownTableNameNotice(translation)); - } + @Test + public void noRecordIdForStopTable_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly(new MissingRequiredFieldNotice("translations.txt", 2, "record_id")); + } - @Test - public void fieldValueDefined_yieldsNoNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("agency") - .setFieldName("any") - .setFieldValue("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .isEmpty(); - } + @Test + public void wrongRecordIdForStopTable_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setRecordId("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly(new TranslationForeignKeyViolationNotice(translation)); + } - @Test - public void recordIdAndFieldValueDefined_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("agency") - .setFieldName("any") - .setRecordId("id") - .setFieldValue("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly(new TranslationUnexpectedValueNotice(translation, "record_id", "id")); - } + @Test + public void feedInfoTranslation_yieldsNoNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("feed_info") + .setFieldName("any") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .isEmpty(); + } - @Test - public void recordSubIdAndFieldValueDefined_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("agency") - .setFieldName("any") - .setRecordSubId("sub_id") - .setFieldValue("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly( - new TranslationUnexpectedValueNotice(translation, "record_sub_id", "sub_id")); - } + @Test + public void unexpectedRecordIdForFeedInfo_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("feed_info") + .setFieldName("any") + .setLanguage(Locale.forLanguageTag("en")) + .setRecordId("feed-id") + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly(new TranslationUnexpectedValueNotice(translation, "record_id", "feed-id")); + } - @Test - public void noRecordIdForStopTable_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("agency") - .setFieldName("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly(new MissingRequiredFieldNotice("translations.txt", 2, "record_id")); - } + @Test + public void agencyTranslation_yieldsNoNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setRecordId("agency0") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .isEmpty(); + } - @Test - public void wrongRecordIdForStopTable_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("agency") - .setFieldName("any") - .setRecordId("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly(new TranslationForeignKeyViolationNotice(translation)); - } + @Test + public void stopTimeTranslation_yieldsNoNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("stop_times") + .setFieldName("any") + .setRecordId("trip0") + .setRecordSubId("0") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .isEmpty(); + } - @Test - public void feedInfoTranslation_yieldsNoNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("feed_info") - .setFieldName("any") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .isEmpty(); - } + @Test + public void unparsableIntegerForStopTime_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("stop_times") + .setFieldName("any") + .setRecordId("trip0") + .setRecordSubId("not-an-int") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly(new TranslationForeignKeyViolationNotice(translation)); + } - @Test - public void unexpectedRecordIdForFeedInfo_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("feed_info") - .setFieldName("any") - .setLanguage(Locale.forLanguageTag("en")) - .setRecordId("feed-id") - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly(new TranslationUnexpectedValueNotice(translation, "record_id", "feed-id")); - } + @Test + public void wrongFieldName_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("any") + .setFieldValue("anyAgency") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly( + new TranslationFieldAndReferenceValidator.TranslationUnexpectedNameNotice(translation, translation.fieldName())); + } - @Test - public void agencyTranslation_yieldsNoNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("agency") - .setFieldName("any") - .setRecordId("agency0") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .isEmpty(); - } + @Test + public void valueNotFound_yieldsNotice_1() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("agency_email") + .setFieldValue("anyAgency") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly( + new TranslationFieldAndReferenceValidator.TranslationNotFoundValueNotice(translation, translation.fieldName(), translation.fieldValue())); + } - @Test - public void stopTimeTranslation_yieldsNoNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("stop_times") - .setFieldName("any") - .setRecordId("trip0") - .setRecordSubId("0") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .isEmpty(); - } + @Test + public void valueNotFound_yieldsNotice_2() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("agency_url") + .setFieldValue("test@gmail.com") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .containsExactly( + new TranslationFieldAndReferenceValidator.TranslationNotFoundValueNotice(translation, translation.fieldName(), translation.fieldValue())); + } - @Test - public void unparsableIntegerForStopTime_yieldsNotice() { - GtfsTranslation translation = - new GtfsTranslation.Builder() - .setCsvRowNumber(2) - .setTableName("stop_times") - .setFieldName("any") - .setRecordId("trip0") - .setRecordSubId("not-an-int") - .setLanguage(Locale.forLanguageTag("en")) - .build(); - assertThat( - generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) - .containsExactly(new TranslationForeignKeyViolationNotice(translation)); - } + @Test + public void valueFound_yieldsNotice() { + GtfsTranslation translation = + new GtfsTranslation.Builder() + .setCsvRowNumber(2) + .setTableName("agency") + .setFieldName("agency_email") + .setFieldValue("test@gmail.com") + .setLanguage(Locale.forLanguageTag("en")) + .build(); + assertThat( + generateNotices(new CsvHeader(NEW_FORMAT_CSV_HEADERS), ImmutableList.of(translation))) + .isEmpty(); + } }