diff --git a/internal/core/source/source_test.go b/internal/core/source/source_test.go index 1a21698..a7e2865 100644 --- a/internal/core/source/source_test.go +++ b/internal/core/source/source_test.go @@ -35,6 +35,11 @@ func TestValidateDocumentMissingFields(t *testing.T) { mutate: func(doc *SourceDocument) { doc.ID = " \t" }, wantErr: "source document id must not be empty", }, + { + name: "id surrounding whitespace", + mutate: func(doc *SourceDocument) { doc.ID = " source-1 " }, + wantErr: "source document id \" source-1 \" must not contain leading or trailing whitespace", + }, { name: "kind", mutate: func(doc *SourceDocument) { doc.Kind = "" }, @@ -94,6 +99,11 @@ func TestValidateDocumentMissingUnitFields(t *testing.T) { mutate: func(doc *SourceDocument) { doc.Units[1].ID = "" }, wantErr: "source unit[1].id must not be empty", }, + { + name: "id surrounding whitespace", + mutate: func(doc *SourceDocument) { doc.Units[1].ID = " u2 " }, + wantErr: "source unit[1].id \" u2 \" must not contain leading or trailing whitespace", + }, { name: "kind", mutate: func(doc *SourceDocument) { doc.Units[1].Kind = " " }, @@ -125,7 +135,7 @@ func TestValidateDocumentMissingUnitFields(t *testing.T) { func TestValidateDocumentDuplicateUnitIDs(t *testing.T) { doc := validDocument() - doc.Units[1].ID = " u1 " + doc.Units[1].ID = "u1" err := ValidateDocument(doc) @@ -179,16 +189,31 @@ func TestValidateRefMissingUnitIDs(t *testing.T) { ref: SourceRef{StartUnitID: "u1", EndUnitID: "u2"}, wantErr: "source ref source_id must not be empty", }, + { + name: "source id surrounding whitespace", + ref: SourceRef{SourceID: " source-1 ", StartUnitID: "u1", EndUnitID: "u2"}, + wantErr: "source ref source_id \" source-1 \" must not contain leading or trailing whitespace", + }, { name: "missing start id", ref: SourceRef{SourceID: "source-1", EndUnitID: "u2"}, wantErr: "source ref start_unit_id must not be empty", }, + { + name: "start id surrounding whitespace", + ref: SourceRef{SourceID: "source-1", StartUnitID: " u1 ", EndUnitID: "u2"}, + wantErr: "source ref start_unit_id \" u1 \" must not contain leading or trailing whitespace", + }, { name: "missing end id", ref: SourceRef{SourceID: "source-1", StartUnitID: "u1"}, wantErr: "source ref end_unit_id must not be empty", }, + { + name: "end id surrounding whitespace", + ref: SourceRef{SourceID: "source-1", StartUnitID: "u1", EndUnitID: " u2 "}, + wantErr: "source ref end_unit_id \" u2 \" must not contain leading or trailing whitespace", + }, { name: "unknown start id", ref: SourceRef{SourceID: "source-1", StartUnitID: "u9", EndUnitID: "u2"}, diff --git a/internal/core/source/validation.go b/internal/core/source/validation.go index df38d32..dac150a 100644 --- a/internal/core/source/validation.go +++ b/internal/core/source/validation.go @@ -12,6 +12,9 @@ func ValidateDocument(doc *SourceDocument) error { if isBlank(doc.ID) { return fmt.Errorf("source document id must not be empty") } + if hasSurroundingWhitespace(doc.ID) { + return fmt.Errorf("source document id %q must not contain leading or trailing whitespace", doc.ID) + } if isBlank(doc.Kind) { return fmt.Errorf("source document kind must not be empty") } @@ -27,20 +30,22 @@ func ValidateDocument(doc *SourceDocument) error { seenUnitIDs := make(map[string]struct{}, len(doc.Units)) for i, unit := range doc.Units { - unitID := strings.TrimSpace(unit.ID) - if unitID == "" { + if isBlank(unit.ID) { return fmt.Errorf("source unit[%d].id must not be empty", i) } + if hasSurroundingWhitespace(unit.ID) { + return fmt.Errorf("source unit[%d].id %q must not contain leading or trailing whitespace", i, unit.ID) + } if isBlank(unit.Kind) { return fmt.Errorf("source unit[%d].kind must not be empty", i) } if isBlank(unit.Text) { return fmt.Errorf("source unit[%d].text must not be empty", i) } - if _, ok := seenUnitIDs[unitID]; ok { - return fmt.Errorf("source unit id %q is duplicated", unitID) + if _, ok := seenUnitIDs[unit.ID]; ok { + return fmt.Errorf("source unit id %q is duplicated", unit.ID) } - seenUnitIDs[unitID] = struct{}{} + seenUnitIDs[unit.ID] = struct{}{} } return nil @@ -53,12 +58,21 @@ func ValidateRef(doc *SourceDocument, ref SourceRef) error { if isBlank(ref.SourceID) { return fmt.Errorf("source ref source_id must not be empty") } + if hasSurroundingWhitespace(ref.SourceID) { + return fmt.Errorf("source ref source_id %q must not contain leading or trailing whitespace", ref.SourceID) + } if isBlank(ref.StartUnitID) { return fmt.Errorf("source ref start_unit_id must not be empty") } + if hasSurroundingWhitespace(ref.StartUnitID) { + return fmt.Errorf("source ref start_unit_id %q must not contain leading or trailing whitespace", ref.StartUnitID) + } if isBlank(ref.EndUnitID) { return fmt.Errorf("source ref end_unit_id must not be empty") } + if hasSurroundingWhitespace(ref.EndUnitID) { + return fmt.Errorf("source ref end_unit_id %q must not contain leading or trailing whitespace", ref.EndUnitID) + } if ref.SourceID != doc.ID { return fmt.Errorf("source ref source_id %q does not match document id %q", ref.SourceID, doc.ID) } @@ -93,3 +107,7 @@ func UnitIndex(doc *SourceDocument, unitID string) (int, bool) { func isBlank(value string) bool { return strings.TrimSpace(value) == "" } + +func hasSurroundingWhitespace(value string) bool { + return strings.TrimSpace(value) != value +}