From 6836c5e5f1b8f53d032722dfc8cdb1d974bcfc91 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Fri, 22 Mar 2024 08:56:39 +0100 Subject: [PATCH] #523 Add validators for changes file (#557) --- doc/changes/changes_4.2.1.md | 4 + doc/requirements/design.md | 17 +++ project-keeper/error_code_config.yml | 2 +- .../validators/AbstractFileValidator.java | 5 +- .../changesfile/ChangesFileValidator.java | 79 ++++++++++--- .../release/ChangesFileReleaseValidator.java | 53 +++++++-- .../changesfile/ChangesFileValidatorTest.java | 106 +++++++++++++++--- .../ChangesFileReleaseValidatorTest.java | 55 ++++++++- .../release/ReleaseInspectorTest.java | 2 +- 9 files changed, 273 insertions(+), 50 deletions(-) diff --git a/doc/changes/changes_4.2.1.md b/doc/changes/changes_4.2.1.md index da3c9bab..5cc52818 100644 --- a/doc/changes/changes_4.2.1.md +++ b/doc/changes/changes_4.2.1.md @@ -4,6 +4,10 @@ Code name: ## Summary +## Features + +* #523: Added validation steps for changes file + ## Bugfixes * #546: Updated template for file `.settings/org.eclipse.jdt.core.prefs` diff --git a/doc/requirements/design.md b/doc/requirements/design.md index 132112c5..e8f0ee39 100644 --- a/doc/requirements/design.md +++ b/doc/requirements/design.md @@ -920,6 +920,23 @@ Covers: Needs: impl, utest, itest +#### PK Mode `verify-release` Verifies Changes File +`dsn~verify-release-mode.verify-changes-file~1` + +PK's `verify-release` mode verifies that the following parts of the current changes file (e.g. `doc/changes/changes_4.2.1.md`) are present: +* Project name +* Version +* Code name (= release title) +* Summary + +Rationale: +This ensures that the required information for the GitHub release notes are available. + +Covers: +* [`dsn~verify-release-mode~1`](#pk-mode-verify-release) + +Needs: impl, utest + #### PK Mode `verify-release` Checks All Issues are Closed `dsn~verify-release-mode.verify-issues-closed~1` diff --git a/project-keeper/error_code_config.yml b/project-keeper/error_code_config.yml index 4e10e748..6394d077 100644 --- a/project-keeper/error_code_config.yml +++ b/project-keeper/error_code_config.yml @@ -2,4 +2,4 @@ error-tags: PK-CORE: packages: - com.exasol.projectkeeper - highest-index: 196 + highest-index: 197 diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/AbstractFileValidator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/AbstractFileValidator.java index 72e4fa8b..1295bb21 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/AbstractFileValidator.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/AbstractFileValidator.java @@ -17,7 +17,8 @@ */ public abstract class AbstractFileValidator implements Validator { private final Path absoluteFilePath; - private final Path relativeFilePath; + /** Project-directory relative path to the validated file. This is useful for displaying in error messages. */ + protected final Path relativeFilePath; /** * Create a new instance of {@link AbstractFileValidator}. @@ -60,7 +61,7 @@ private ValidationFinding getMissingFileFinding() { * @return method (closure) */ protected SimpleValidationFinding.Fix getCreateFileFix() { - return (Logger log) -> { + return (final Logger log) -> { try { Files.createDirectories(this.absoluteFilePath.getParent()); writeTemplateFile(this.absoluteFilePath); diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java index 218c3897..4325c3dd 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidator.java @@ -2,8 +2,9 @@ import java.nio.file.Path; import java.time.LocalDateTime; -import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; import com.exasol.errorreporting.ExaError; import com.exasol.projectkeeper.Logger; @@ -11,6 +12,7 @@ import com.exasol.projectkeeper.sources.AnalyzedSource; import com.exasol.projectkeeper.validators.AbstractFileValidator; import com.exasol.projectkeeper.validators.finding.SimpleValidationFinding; +import com.exasol.projectkeeper.validators.finding.SimpleValidationFinding.Fix; import com.exasol.projectkeeper.validators.finding.ValidationFinding; /** @@ -24,6 +26,7 @@ public class ChangesFileValidator extends AbstractFileValidator { private final String projectName; private final List sources; private final String projectVersion; + private final ChangesFileIO changesFileIO; /** * Create a new instance of {@link ChangesFileValidator} @@ -35,15 +38,21 @@ public class ChangesFileValidator extends AbstractFileValidator { */ public ChangesFileValidator(final String projectVersion, final String projectName, final Path projectDirectory, final List sources) { + this(projectVersion, projectName, projectDirectory, sources, new ChangesFileIO()); + } + + ChangesFileValidator(final String projectVersion, final String projectName, final Path projectDirectory, + final List sources, final ChangesFileIO changesFileIO) { super(projectDirectory, ChangesFile.getPathForVersion(projectVersion)); this.projectVersion = projectVersion; this.projectName = projectName; this.sources = sources; + this.changesFileIO = changesFileIO; } @Override protected void writeTemplateFile(final Path target) { - new ChangesFileIO().write(getTemplate(), target); + changesFileIO.write(getTemplate(), target); } @Override @@ -51,26 +60,66 @@ protected boolean isValidationEnabled() { return !new ExasolVersionMatcher().isSnapshotVersion(this.projectVersion); } + // [impl->dsn~verify-release-mode.verify-changes-file~1] @Override protected List validateContent(final Path file) { - final var changesFile = new ChangesFileIO().read(file); - final var fixedSections = fixSections(changesFile); - if (!changesFile.equals(fixedSections)) { - return List.of(getWrongContentFinding(fixedSections, file)); + final ChangesFile changesFile = changesFileIO.read(file); + return Stream + .of(validateDependencySection(file, changesFile), validateSummarySection(changesFile), + validateProjectName(changesFile)) + .filter(Optional::isPresent) // + .map(Optional::get) // + .toList(); + } + + private Optional validateDependencySection(final Path file, final ChangesFile changesFile) { + final ChangesFile fixed = fixDependencyUpdateSection(changesFile); + if (changesFile.equals(fixed)) { + return noFinding(); + } else { + return outdatedDependencySectionFinding(fixed, file); + } + } + + private Optional outdatedDependencySectionFinding(final ChangesFile fixedSections, + final Path file) { + final String errorMessage = ExaError.messageBuilder("E-PK-CORE-40") + .message("Changes file is invalid.\nExpected content:\n{{expected content}}") + .parameter("expected content", fixedSections.toString()).toString(); + final Fix fix = (final Logger log) -> changesFileIO.write(fixedSections, file); + return Optional.of(SimpleValidationFinding.withMessage(errorMessage).andFix(fix).build()); + } + + private Optional validateSummarySection(final ChangesFile changesFile) { + final Optional summary = changesFile.getSummarySection(); + if (summary.isEmpty()) { + return finding(ExaError.messageBuilder("E-PK-CORE-193") + .message("Section '## Summary' is missing in {{path}}.", relativeFilePath) + .mitigation("Add section.").toString()); + } else { + return noFinding(); + } + } + + private Optional validateProjectName(final ChangesFile changesFile) { + if (changesFile.getProjectName() == null || changesFile.getProjectName().isBlank()) { + return finding(ExaError.messageBuilder("E-PK-CORE-195") + .message("Project name in {{path}} is missing.", relativeFilePath).mitigation("Add a project name.") + .toString()); } else { - return Collections.emptyList(); + return noFinding(); } } - private ValidationFinding getWrongContentFinding(final ChangesFile fixedSections, final Path file) { - return SimpleValidationFinding - .withMessage(ExaError.messageBuilder("E-PK-CORE-40") - .message("Changes file is invalid.\nExpected content:\n{{expected content}}") - .parameter("expected content", fixedSections.toString()).toString()) - .andFix(((final Logger log) -> new ChangesFileIO().write(fixedSections, file))).build(); + private Optional finding(final String message) { + return Optional.of(SimpleValidationFinding.withMessage(message).build()); + } + + private Optional noFinding() { + return Optional.empty(); } - private ChangesFile fixSections(final ChangesFile changesFile) { + private ChangesFile fixDependencyUpdateSection(final ChangesFile changesFile) { final var dependencySectionFixer = new DependencySectionFixer(this.sources); return dependencySectionFixer.fix(changesFile); } @@ -83,6 +132,6 @@ private ChangesFile getTemplate() { .summary(ChangesFileSection.builder("## Summary").build()) .addSection(ChangesFileSection.builder(FEATURES_SECTION).addLines("", FIXED_ISSUE_TEMPLATE, "").build()) // .build(); - return fixSections(changesFile); + return fixDependencyUpdateSection(changesFile); } } diff --git a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidator.java b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidator.java index cf57d50b..c5bed415 100644 --- a/project-keeper/src/main/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidator.java +++ b/project-keeper/src/main/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidator.java @@ -1,6 +1,6 @@ package com.exasol.projectkeeper.validators.release; -import static java.util.Collections.emptyList; +import static java.util.function.Predicate.not; import static java.util.stream.Collectors.toSet; import java.nio.file.Path; @@ -10,8 +10,7 @@ import com.exasol.errorreporting.ExaError; import com.exasol.projectkeeper.Validator; -import com.exasol.projectkeeper.validators.changesfile.ChangesFile; -import com.exasol.projectkeeper.validators.changesfile.FixedIssue; +import com.exasol.projectkeeper.validators.changesfile.*; import com.exasol.projectkeeper.validators.finding.SimpleValidationFinding; import com.exasol.projectkeeper.validators.finding.ValidationFinding; import com.exasol.projectkeeper.validators.release.github.GitHubAdapter; @@ -36,14 +35,46 @@ class ChangesFileReleaseValidator implements Validator { this.clock = clock; } + // [impl->dsn~verify-release-mode.verify-changes-file~1] @Override public List validate() { - return Stream.of(validateReleaseDate(), validateIssuesClosed()) // - .flatMap(List::stream).toList(); + return Stream.of(validateReleaseDate(), validateIssuesClosed(), validateSummary(), validateCodeName()) // + .filter(Optional::isPresent) // + .map(Optional::get) // + .toList(); + } + + private Optional validateSummary() { + final Optional summarySection = changesFile.getSummarySection(); + if (summarySection.isEmpty()) { + return noFindings(); // Already checked in ChangesFileValidator + } + if (hasEmptyContent(summarySection)) { + return finding(ExaError.messageBuilder("E-PK-CORE-194") + .message("Section '## Summary' is empty in {{path}}.", changesFilePath) + .mitigation("Add content to section.").toString()); + } + return noFindings(); + } + + private boolean hasEmptyContent(final Optional summarySection) { + return summarySection.orElseThrow().getContent().stream() // + .filter(not(String::isBlank)) // + .findAny() // + .isEmpty(); + } + + private Optional validateCodeName() { + if (changesFile.getCodeName() == null || changesFile.getCodeName().isBlank()) { + return finding(ExaError.messageBuilder("E-PK-CORE-197") + .message("Code name in {{path}} is missing.", changesFilePath).mitigation("Add a code name.") + .toString()); + } + return noFindings(); } // [impl->dsn~verify-release-mode.verify-release-date~1] - private List validateReleaseDate() { + private Optional validateReleaseDate() { final Optional releaseDate = changesFile.getParsedReleaseDate(); if (releaseDate.isEmpty()) { return finding(ExaError.messageBuilder("E-PK-CORE-182") @@ -62,7 +93,7 @@ private List validateReleaseDate() { } // [impl->dsn~verify-release-mode.verify-issues-closed~1] - private List validateIssuesClosed() { + private Optional validateIssuesClosed() { final List wrongIssues = getIssuesWronglyMarkedAsClosed(); if (!wrongIssues.isEmpty()) { return finding(ExaError.messageBuilder("E-PK-CORE-186").message( @@ -95,11 +126,11 @@ private LocalDate today() { return LocalDate.ofInstant(clock.instant(), UTC_ZONE); } - private List noFindings() { - return emptyList(); + private Optional noFindings() { + return Optional.empty(); } - private List finding(final String message) { - return List.of(SimpleValidationFinding.withMessage(message).build()); + private Optional finding(final String message) { + return Optional.of(SimpleValidationFinding.withMessage(message).build()); } } diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java index 62234975..5cd07fab 100644 --- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java +++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/changesfile/ChangesFileValidatorTest.java @@ -6,44 +6,55 @@ import static com.exasol.projectkeeper.HasValidationFindingWithMessageMatcher.hasValidationFindingWithMessage; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import static org.mockito.Mockito.mock; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import com.exasol.projectkeeper.Logger; +import com.exasol.projectkeeper.Validator; import com.exasol.projectkeeper.shared.dependencies.BaseDependency.Type; import com.exasol.projectkeeper.shared.dependencychanges.*; import com.exasol.projectkeeper.sources.AnalyzedMavenSource; +import com.exasol.projectkeeper.sources.AnalyzedMavenSource.AnalyzedMavenSourceBuilder; import com.exasol.projectkeeper.sources.AnalyzedSource; import com.exasol.projectkeeper.test.TestMavenModel; import com.exasol.projectkeeper.validators.FindingFixHelper; -import com.exasol.projectkeeper.validators.finding.FindingsFixer; -import com.exasol.projectkeeper.validators.finding.ValidationFinding; +import com.exasol.projectkeeper.validators.changesfile.ChangesFile.Builder; +import com.exasol.projectkeeper.validators.finding.*; +// [utest->dsn~verify-release-mode.verify-changes-file~1] @Tag("integration") +@ExtendWith(MockitoExtension.class) class ChangesFileValidatorTest { private static final String A_VERSION = "1.2.3"; private static final String A_PROJECT_NAME = "my-project"; private static final String LINE_SEPARATOR = "\n"; + private static final Path CHANGES_FILE_PATH = Path.of("doc", "changes", "changes_" + A_VERSION + ".md"); @TempDir Path tempDir; + @Mock + ChangesFileIO changesFileIOMock; @Test void testValidation() throws IOException { final AnalyzedMavenSource source = createTestSetup(); - assertThat(createValidator(source), - hasValidationFindingWithMessage("E-PK-CORE-56: Could not find required file 'doc" + File.separator - + "changes" + File.separator + "changes_1.2.3.md'.")); + assertThat(createValidator(source), hasValidationFindingWithMessage( + "E-PK-CORE-56: Could not find required file '" + CHANGES_FILE_PATH + "'.")); } @Test @@ -54,11 +65,10 @@ void testValidationForSnapshotVersion() throws IOException { } @Test - void noDependencyUpdates() throws IOException { + void noDependencyUpdates(@Mock final Logger log) throws IOException { final AnalyzedMavenSource source = createTestSetup(new TestMavenModel(), Collections.emptyList()); - final Logger log = mock(Logger.class); createValidator(source).validate().forEach(finding -> new FindingsFixer(log).fixFindings(List.of(finding))); - final Path changesFile = this.tempDir.resolve(Path.of("doc", "changes", "changes_1.2.3.md")); + final Path changesFile = this.tempDir.resolve(CHANGES_FILE_PATH); assertThat(changesFile, hasContent(equalTo("# my-project 1.2.3, released 2024-??-??" + LINE_SEPARATOR + LINE_SEPARATOR // + "Code name:" + LINE_SEPARATOR + LINE_SEPARATOR // @@ -73,11 +83,10 @@ void noDependencyUpdates() throws IOException { } @Test - void testFixCreatedTemplate() throws IOException { + void testFixCreatedTemplate(@Mock final Logger log) throws IOException { final AnalyzedMavenSource source = createTestSetup(); - final Logger log = mock(Logger.class); createValidator(source).validate().forEach(finding -> new FindingsFixer(log).fixFindings(List.of(finding))); - final Path changesFile = this.tempDir.resolve(Path.of("doc", "changes", "changes_1.2.3.md")); + final Path changesFile = this.tempDir.resolve(CHANGES_FILE_PATH); assertThat(changesFile, hasContent(equalTo("# my-project 1.2.3, released 2024-??-??" + LINE_SEPARATOR + LINE_SEPARATOR // + "Code name:" + LINE_SEPARATOR + LINE_SEPARATOR // @@ -97,7 +106,7 @@ void testFixContainsDependencyUpdates() throws IOException { model.addDependency(); final AnalyzedMavenSource source = createTestSetup(model); createValidator(source).validate().forEach(FindingFixHelper::fix); - final Path changesFile = this.tempDir.resolve(Path.of("doc", "changes", "changes_1.2.3.md")); + final Path changesFile = this.tempDir.resolve(CHANGES_FILE_PATH); assertThat(changesFile, hasContent(endsWith("## Dependency Updates" + LINE_SEPARATOR + LINE_SEPARATOR // + "### Compile Dependency Updates" + LINE_SEPARATOR + LINE_SEPARATOR // + "* Added `com.example:my-lib:1.2.3`" + LINE_SEPARATOR))); @@ -112,10 +121,79 @@ void testValidationOnFixedFile() throws IOException { assertThat(createValidator(source), hasNoMoreFindingsAfterApplyingFixes()); } - private ChangesFileValidator createValidator(final AnalyzedSource source) { + @Test + void testValidateChangesFileMissing() { + final List findings = createValidatorWithChangesFileMock( + AnalyzedMavenSource.builder().projectName("name").build()).validate(); + assertFinding(findings, "E-PK-CORE-56: Could not find required file '" + CHANGES_FILE_PATH + "'."); + } + + @Test + void testValidateChangesFileMissingSummary() throws IOException { + simulateChangesFile(ChangesFile.builder().projectName("name")); + final List findings = createValidatorWithChangesFileMock(defaultSource().build()).validate(); + assertFinding(findings, + "E-PK-CORE-193: Section '## Summary' is missing in '" + CHANGES_FILE_PATH + "'. Add section."); + } + + @Test + void testValidateChangesFileEmptySummary() throws IOException { + simulateChangesFile( + ChangesFile.builder().projectName("name").summary(ChangesFileSection.builder("## Summary").build())); + final List findings = createValidatorWithChangesFileMock(defaultSource().build()).validate(); + assertThat(findings, empty()); // This is validated in ChangesFileReleaseValidator + } + + @Test + void testValidateChangesFileSummaryWithBlankLines() throws IOException { + simulateChangesFile(ChangesFile.builder().projectName("name") + .summary(ChangesFileSection.builder("## Summary").addLine(" ").build())); + final List findings = createValidatorWithChangesFileMock(defaultSource().build()).validate(); + assertThat(findings, empty()); // This is validated in ChangesFileReleaseValidator + } + + @Test + void testValidateChangesFileMissingProjectName() throws IOException { + simulateChangesFile( + ChangesFile.builder().summary(ChangesFileSection.builder("## Summary").addLine("content").build())); + final List findings = createValidatorWithChangesFileMock(defaultSource().build()).validate(); + assertFinding(findings, + "E-PK-CORE-195: Project name in '" + CHANGES_FILE_PATH + "' is missing. Add a project name."); + } + + @Test + void testValidateChangesFileBlankProjectName() throws IOException { + simulateChangesFile(ChangesFile.builder().projectName(" ") + .summary(ChangesFileSection.builder("## Summary").addLine("content").build())); + final List findings = createValidatorWithChangesFileMock(defaultSource().build()).validate(); + assertFinding(findings, + "E-PK-CORE-195: Project name in '" + CHANGES_FILE_PATH + "' is missing. Add a project name."); + } + + private AnalyzedMavenSourceBuilder defaultSource() { + return AnalyzedMavenSource.builder().dependencyChanges(DependencyChangeReport.builder().build()); + } + + private void simulateChangesFile(final Builder builder) throws IOException { + final Path path = tempDir.resolve(CHANGES_FILE_PATH); + Files.createDirectories(path.getParent()); + Files.createFile(path); + when(changesFileIOMock.read(path)).thenReturn(builder.build()); + } + + private void assertFinding(final List findings, final String expectedMessage) { + assertAll(() -> assertThat(findings, hasSize(1)), + () -> assertThat(((SimpleValidationFinding) findings.get(0)).getMessage(), equalTo(expectedMessage))); + } + + private Validator createValidator(final AnalyzedSource source) { return new ChangesFileValidator(A_VERSION, A_PROJECT_NAME, this.tempDir, List.of(source)); } + private Validator createValidatorWithChangesFileMock(final AnalyzedSource source) { + return new ChangesFileValidator(A_VERSION, A_PROJECT_NAME, this.tempDir, List.of(source), changesFileIOMock); + } + private AnalyzedMavenSource createTestSetup() throws IOException { return createTestSetup(new TestMavenModel()); } diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidatorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidatorTest.java index f332d904..ddba4bee 100644 --- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidatorTest.java +++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ChangesFileReleaseValidatorTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.when; +import java.io.IOException; import java.nio.file.Path; import java.time.*; import java.util.List; @@ -21,11 +22,13 @@ import com.exasol.projectkeeper.validators.release.github.GitHubAdapter; import com.exasol.projectkeeper.validators.release.github.IssueState; +// [utest->dsn~verify-release-mode.verify-changes-file~1] @ExtendWith(MockitoExtension.class) class ChangesFileReleaseValidatorTest { private static final Path PATH = Path.of("changes.md"); private static final Instant NOW = Instant.parse("2007-12-03T10:15:30.00Z"); private static final String TODAY = "2007-12-03"; + private static final String CODE_NAME = "code name"; @Mock GitHubAdapter gitHubAdapterMock; @@ -40,7 +43,7 @@ class ChangesFileReleaseValidatorTest { "2007-12-04, E-PK-CORE-183: Release date 2007-12-04 must be today 2007-12-03 in 'changes.md'", "2007-12-03, NULL" }) void invalidReleaseDate(final String releaseDate, final String expectedError) { - final List findings = findings(ChangesFile.builder().releaseDate(releaseDate)); + final List findings = findings(ChangesFile.builder().releaseDate(releaseDate).codeName(CODE_NAME)); if (expectedError == null) { assertThat(findings, emptyIterable()); } else { @@ -50,7 +53,7 @@ void invalidReleaseDate(final String releaseDate, final String expectedError) { @Test void closedIssuesNoIssuesMentioned() { - final List findings = findings(ChangesFile.builder().releaseDate(TODAY)); + final List findings = findings(ChangesFile.builder().codeName(CODE_NAME).releaseDate(TODAY)); assertThat(findings, empty()); } @@ -59,22 +62,62 @@ void closedIssuesNoIssuesMentioned() { @CsvSource({ "OPEN", "MISSING" }) void closedIssuesIssueNotClosed(final IssueState state) { when(gitHubAdapterMock.getIssueState(42)).thenReturn(state); - final List findings = findings(ChangesFile.builder() + final List findings = findings(ChangesFile.builder().codeName(CODE_NAME) .addSection(ChangesFileSection.builder("## Bugfixes").addLine("- #42: Fixed a bug").build()) .releaseDate(TODAY)); - assertThat(findings, contains( - "E-PK-CORE-186: The following GitHub issues are marked as fixed in 'changes.md' but are not closed in GitHub: [42]")); + assertThat(findings, contains("E-PK-CORE-186: The following GitHub issues are marked as fixed in '" + PATH + + "' but are not closed in GitHub: [42]")); } @Test void closedIssuesIssueClosed() { when(gitHubAdapterMock.getIssueState(42)).thenReturn(IssueState.CLOSED); - final List findings = findings(ChangesFile.builder() + final List findings = findings(ChangesFile.builder().codeName(CODE_NAME) .addSection(ChangesFileSection.builder("## Bugfixes").addLine("- #42: Fixed a bug").build()) .releaseDate(TODAY)); assertThat(findings, empty()); } + @Test + void summarySectionEmpty() { + final List findings = findings(ChangesFile.builder().codeName(CODE_NAME) + .summary(ChangesFileSection.builder("## Summary").build()).releaseDate(TODAY)); + assertThat(findings, + contains("E-PK-CORE-194: Section '## Summary' is empty in '" + PATH + "'. Add content to section.")); + } + + @Test + void summarySectionBlank() { + final List findings = findings(ChangesFile.builder().codeName(CODE_NAME) + .summary(ChangesFileSection.builder("## Summary").addLine(" ").build()).releaseDate(TODAY)); + assertThat(findings, + contains("E-PK-CORE-194: Section '## Summary' is empty in '" + PATH + "'. Add content to section.")); + } + + @Test + void summarySectionNotBlank() { + final List findings = findings(ChangesFile.builder().codeName(CODE_NAME) + .summary(ChangesFileSection.builder("## Summary").addLine("non-blank content").build()) + .releaseDate(TODAY)); + assertThat(findings, empty()); + } + + @Test + void codeNameMissing() throws IOException { + final List findings = findings(ChangesFile.builder() + .summary(ChangesFileSection.builder("## Summary").addLine("non-blank content").build()) + .releaseDate(TODAY)); + assertThat(findings, contains("E-PK-CORE-197: Code name in '" + PATH + "' is missing. Add a code name.")); + } + + @Test + void codeNameBlank() throws IOException { + final List findings = findings(ChangesFile.builder() + .summary(ChangesFileSection.builder("## Summary").addLine("non-blank content").build()) + .releaseDate(TODAY)); + assertThat(findings, contains("E-PK-CORE-197: Code name in '" + PATH + "' is missing. Add a code name.")); + } + private List findings(final ChangesFile.Builder changesFile) { return testee(changesFile).validate().stream() // .map(SimpleValidationFinding.class::cast) // diff --git a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ReleaseInspectorTest.java b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ReleaseInspectorTest.java index 8a6e7817..d487b447 100644 --- a/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ReleaseInspectorTest.java +++ b/project-keeper/src/test/java/com/exasol/projectkeeper/validators/release/ReleaseInspectorTest.java @@ -35,7 +35,7 @@ void validatorCount() { // [utest->dsn~verify-release-mode.verify-release-date~1] @Test void verifyReleaseDate() { - simulateChangesFile(ChangesFile.builder().releaseDate("invalid")); + simulateChangesFile(ChangesFile.builder().codeName("code name").releaseDate("invalid")); final List findings = getFindings(); assertThat(findings, contains("E-PK-CORE-182: Release date 'invalid' has invalid format in '" + getChangesFilePath() + "'"));