Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,6 @@ private String describe(Document document, Optional<String> value) {

private Optional<String> capture(Pattern pattern, String content) {
Matcher matcher = pattern.matcher(content);
return matcher.find() ? Optional.of(matcher.group(1)) : Optional.empty();
return matcher.find() ? Optional.ofNullable(matcher.group(1)) : Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ public boolean hasBody(String heading) {
return index >= 0 && hasBodyAt(index);
}

/** The headings from {@code wanted} that are present, in the order they appear in the document. */
/**
* The headings from {@code wanted} that are present, in the order they first
* appear in the document, each listed once. A required heading that appears
* more than once is reported by its first occurrence, so the order comparison
* matches the de-duplicated set of present sections rather than reporting a
* spurious out-of-order failure.
*/
public List<String> headingsInOrder(List<String> wanted) {
Set<String> required = new LinkedHashSet<>(wanted);
List<String> ordered = new ArrayList<>();
Expand All @@ -106,7 +112,7 @@ public List<String> headingsInOrder(List<String> wanted) {
}

private void addIfRequiredHeading(String line, boolean lineInsideFence, Set<String> required, List<String> ordered) {
if (!lineInsideFence && required.contains(line)) {
if (!lineInsideFence && required.remove(line)) {
ordered.add(line);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,20 @@ void failsWhenRequiredSectionsAppearInTheWrongOrder() {
assertTrue(exception.getMessage().contains("out of order"), exception.getMessage());
}

@Test
void doesNotReportOutOfOrderWhenARequiredSectionAppearsTwice() {
String duplicated = VALID_CONTENT + """
## Testing
More notes on testing.
""";
ClaudeMdFormatRule rule = ruleFor(duplicated);
rule.setEnforceSectionOrder(true);

// The sections are in the configured order; a repeated "## Testing" must be
// counted once by its first occurrence, not flagged as out of order.
assertDoesNotThrow(rule::execute);
}

@Test
void failsWhenALineExceedsTheMaximumLength() {
ClaudeMdFormatRule rule = ruleFor(VALID_CONTENT + "\n" + "x".repeat(200) + "\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ void failsWithAClearMessageWhenAPatternHasNoCapturingGroup() {
assertTrue(exception.getMessage().contains("must declare a capturing group"), exception.getMessage());
}

@Test
void treatsANonParticipatingOptionalGroupAsAbsent() {
CrossDocConsistencyRule rule = ruleFor("Uses proto3 here.", "No proto mentioned.");
rule.setConsistentPatterns(List.of("proto(\\d)?"));

// The group is optional, so a bare "proto" match captures null. That must be
// treated as a mismatch against the concrete "3", not throw a NullPointerException.
EnforcerRuleException exception = assertThrows(EnforcerRuleException.class, rule::execute);
assertTrue(exception.getMessage().contains("'3'"), exception.getMessage());
}

@Test
void failsWhenAFileIsMissing() {
CrossDocConsistencyRule rule = new CrossDocConsistencyRule();
Expand Down
Loading