From cbeb3046fe8c1dc753451930eab325f6713aa7b5 Mon Sep 17 00:00:00 2001
From: mmaehren <32199075+mmaehren@users.noreply.github.com>
Date: Mon, 14 Sep 2026 12:26:37 +0200
Subject: [PATCH 1/2] Added FaultListResult
---
.../core/probe/result/FaultListResult.java | 158 ++++++++++++++++++
.../probe/result/FaultListResultTest.java | 153 +++++++++++++++++
2 files changed, 311 insertions(+)
create mode 100644 src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java
create mode 100644 src/test/java/de/rub/nds/scanner/core/probe/result/FaultListResultTest.java
diff --git a/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java b/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java
new file mode 100644
index 00000000..040f284c
--- /dev/null
+++ b/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java
@@ -0,0 +1,158 @@
+/*
+ * Scanner Core - A Modular Framework for Probe Definition, Execution, and Result Analysis.
+ *
+ * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
+ *
+ * Licensed under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+package de.rub.nds.scanner.core.probe.result;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIncludeProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import de.rub.nds.scanner.core.probe.AnalyzedProperty;
+import java.util.List;
+
+/**
+ * Represents {@link TestResult}s which list the features for which a fault has been observed, e.g.
+ * the named groups for which a peer reused its key. The list holds the affected features only, the
+ * summary states whether any fault was found at all: an empty list summarizes to {@link
+ * TestResults#FALSE}, a non-empty list to {@link TestResults#TRUE}. Since the summary is derived
+ * from the list, the result can be used in requirements and rating influencers that expect a plain
+ * {@link TestResults} (cf. {@link SummarizableTestResult#equalsExpectedResult(TestResult)}).
+ *
+ *
If the features could not be examined at all, e.g. because a precondition of the probe was not
+ * met, an explicit summary such as {@link TestResults#CANNOT_BE_TESTED} can be set instead. In this
+ * case no list is collected and {@link #getList()} returns null, so that the absence of faults is
+ * not confused with an actual absence of faults.
+ *
+ *
Note that a probe must set this result explicitly (i.e. {@code put(property, new
+ * FaultListResult<>(property, faults))}), as passing a bare {@link List} yields a plain {@link
+ * ListResult}.
+ *
+ * @param the type of the listed faulty features.
+ */
+@JsonIncludeProperties({"type", "value", "summary"})
+@JsonPropertyOrder({"type", "value", "summary"})
+public class FaultListResult extends ListResult implements SummarizableTestResult {
+
+ private final TestResults explicitSummary;
+
+ @SuppressWarnings("unused")
+ private FaultListResult() {
+ // Default constructor for deserialization
+ this(null, null, null);
+ }
+
+ /**
+ * Constructs a FaultListResult which summarizes the listed faulty features.
+ *
+ * @param property the analyzed property associated with this result
+ * @param faultyFeatures the features for which a fault has been observed, may be empty but
+ * should not be null
+ */
+ public FaultListResult(AnalyzedProperty property, List faultyFeatures) {
+ this(property, faultyFeatures, null);
+ }
+
+ /**
+ * Constructs a FaultListResult which reports the given summary instead of summarizing listed
+ * faulty features. Use this if the features could not be examined, e.g. {@link
+ * TestResults#CANNOT_BE_TESTED} if a precondition of the probe was not met.
+ *
+ * @param property the analyzed property associated with this result
+ * @param explicitSummary the summary to report, must not be null
+ */
+ public FaultListResult(AnalyzedProperty property, TestResults explicitSummary) {
+ this(property, null, explicitSummary);
+ }
+
+ /**
+ * Constructs a FaultListResult with the specified property, list of faulty features and
+ * explicit summary. If the explicit summary is null, the summary is derived from the listed
+ * faulty features, otherwise the explicit summary takes precedence.
+ *
+ * @param property the analyzed property associated with this result
+ * @param faultyFeatures the features for which a fault has been observed
+ * @param explicitSummary the summary to report, or null to derive it from the listed features
+ */
+ public FaultListResult(
+ AnalyzedProperty property, List faultyFeatures, TestResults explicitSummary) {
+ super(property, faultyFeatures);
+ this.explicitSummary = explicitSummary;
+ }
+
+ /**
+ * Restores a FaultListResult from its serialized form. The analyzed property is not part of the
+ * serialized form and is therefore not restored.
+ *
+ * @param faultyFeatures the features for which a fault has been observed
+ * @param explicitSummary the name of the explicitly set summary, or null if the summary is
+ * derived from the listed faulty features
+ * @return the deserialized FaultListResult
+ */
+ @JsonCreator
+ private static FaultListResult fromJson(
+ @JsonProperty("value") List faultyFeatures,
+ @JsonProperty("summary") String explicitSummary) {
+ return new FaultListResult<>(
+ null,
+ faultyFeatures,
+ explicitSummary == null ? null : TestResults.valueOf(explicitSummary));
+ }
+
+ /**
+ * Returns the explicitly set summary of this result.
+ *
+ * @return the explicit summary, or null if the summary is derived from the listed faulty
+ * features
+ */
+ @JsonGetter("summary")
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+ public TestResults getExplicitSummary() {
+ return explicitSummary;
+ }
+
+ /**
+ * Summarizes the listed faults. Returns the explicit summary if one has been set. Otherwise
+ * returns {@link TestResults#TRUE} if at least one faulty feature has been listed, {@link
+ * TestResults#FALSE} if the list is empty and {@link TestResults#NOT_TESTED_YET} if no list has
+ * been set at all.
+ *
+ * @return the summarized TestResults value
+ */
+ @Override
+ public TestResults getSummarizedResult() {
+ if (explicitSummary != null) {
+ return explicitSummary;
+ }
+ if (collection == null) {
+ return TestResults.NOT_TESTED_YET;
+ }
+ return TestResults.of(!collection.isEmpty());
+ }
+
+ /**
+ * Indicates whether the summary was explicitly set.
+ *
+ * @return true if an explicit summary has been set, false if the summary is derived from the
+ * listed faulty features
+ */
+ @Override
+ @JsonIgnore
+ public boolean isExplicitSummary() {
+ return explicitSummary != null;
+ }
+
+ @Override
+ public String getName() {
+ return SummarizableTestResult.super.getName();
+ }
+}
diff --git a/src/test/java/de/rub/nds/scanner/core/probe/result/FaultListResultTest.java b/src/test/java/de/rub/nds/scanner/core/probe/result/FaultListResultTest.java
new file mode 100644
index 00000000..da9563e3
--- /dev/null
+++ b/src/test/java/de/rub/nds/scanner/core/probe/result/FaultListResultTest.java
@@ -0,0 +1,153 @@
+/*
+ * Scanner Core - A Modular Framework for Probe Definition, Execution, and Result Analysis.
+ *
+ * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
+ *
+ * Licensed under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+package de.rub.nds.scanner.core.probe.result;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.rub.nds.scanner.core.TestAnalyzedProperty;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class FaultListResultTest {
+
+ private static FaultListResult faultsFor(List faultyFeatures) {
+ return new FaultListResult<>(TestAnalyzedProperty.TEST_ANALYZED_PROPERTY, faultyFeatures);
+ }
+
+ private static FaultListResult summarizedAs(TestResults explicitSummary) {
+ return new FaultListResult<>(TestAnalyzedProperty.TEST_ANALYZED_PROPERTY, explicitSummary);
+ }
+
+ @Test
+ void emptyListSummarizesToFalse() {
+ assertEquals(TestResults.FALSE, faultsFor(List.of()).getSummarizedResult());
+ }
+
+ @Test
+ void nonEmptyListSummarizesToTrue() {
+ assertEquals(TestResults.TRUE, faultsFor(List.of("SECP256R1")).getSummarizedResult());
+ }
+
+ @Test
+ void missingListSummarizesToNotTestedYet() {
+ assertEquals(TestResults.NOT_TESTED_YET, faultsFor(null).getSummarizedResult());
+ }
+
+ @Test
+ void summaryIsNotExplicit() {
+ assertFalse(faultsFor(List.of()).isExplicitSummary());
+ assertNull(faultsFor(List.of()).getExplicitSummary());
+ }
+
+ @Test
+ void explicitSummaryIsReportedAsSet() {
+ FaultListResult cannotBeTested = summarizedAs(TestResults.CANNOT_BE_TESTED);
+
+ assertTrue(cannotBeTested.isExplicitSummary());
+ assertEquals(TestResults.CANNOT_BE_TESTED, cannotBeTested.getExplicitSummary());
+ assertEquals(TestResults.CANNOT_BE_TESTED, cannotBeTested.getSummarizedResult());
+ assertEquals(TestResults.CANNOT_BE_TESTED.getName(), cannotBeTested.getName());
+ }
+
+ @Test
+ void explicitSummaryCollectsNoFaults() {
+ assertNull(summarizedAs(TestResults.CANNOT_BE_TESTED).getList());
+ }
+
+ @Test
+ void explicitSummaryTakesPrecedenceOverListedFaults() {
+ FaultListResult result =
+ new FaultListResult<>(
+ TestAnalyzedProperty.TEST_ANALYZED_PROPERTY,
+ List.of("SECP256R1"),
+ TestResults.ERROR_DURING_TEST);
+
+ assertTrue(result.isExplicitSummary());
+ assertEquals(TestResults.ERROR_DURING_TEST, result.getSummarizedResult());
+ assertEquals(List.of("SECP256R1"), result.getList());
+ }
+
+ @Test
+ void explicitSummaryMatchesExpectedTestResults() {
+ FaultListResult cannotBeTested = summarizedAs(TestResults.CANNOT_BE_TESTED);
+
+ assertTrue(cannotBeTested.equalsExpectedResult(TestResults.CANNOT_BE_TESTED));
+ assertFalse(cannotBeTested.equalsExpectedResult(TestResults.FALSE));
+ assertFalse(cannotBeTested.equalsExpectedResult(TestResults.TRUE));
+ }
+
+ @Test
+ void nameIsTakenFromSummary() {
+ assertEquals(TestResults.TRUE.getName(), faultsFor(List.of("SECP256R1")).getName());
+ assertEquals(TestResults.FALSE.getName(), faultsFor(List.of()).getName());
+ }
+
+ @Test
+ void matchesExpectedTestResultsOfSummary() {
+ FaultListResult withFaults = faultsFor(List.of("SECP256R1"));
+ FaultListResult withoutFaults = faultsFor(List.of());
+
+ assertTrue(withFaults.equalsExpectedResult(TestResults.TRUE));
+ assertFalse(withFaults.equalsExpectedResult(TestResults.FALSE));
+ assertTrue(withoutFaults.equalsExpectedResult(TestResults.FALSE));
+ assertFalse(withoutFaults.equalsExpectedResult(TestResults.TRUE));
+ }
+
+ @Test
+ void listStaysAccessible() {
+ assertEquals(
+ List.of("SECP256R1", "SECP384R1"),
+ faultsFor(List.of("SECP256R1", "SECP384R1")).getList());
+ }
+
+ @Test
+ void roundTripKeepsFaultsAndSummary() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ String json = mapper.writeValueAsString(faultsFor(List.of("SECP256R1")));
+
+ FaultListResult> restored = mapper.readValue(json, FaultListResult.class);
+
+ assertNotNull(restored.getList());
+ assertEquals(List.of("SECP256R1"), restored.getList());
+ assertEquals(TestResults.TRUE, restored.getSummarizedResult());
+ }
+
+ @Test
+ void roundTripKeepsExplicitSummary() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ String json = mapper.writeValueAsString(summarizedAs(TestResults.CANNOT_BE_TESTED));
+
+ FaultListResult> restored = mapper.readValue(json, FaultListResult.class);
+
+ assertTrue(restored.isExplicitSummary());
+ assertEquals(TestResults.CANNOT_BE_TESTED, restored.getSummarizedResult());
+ assertNull(restored.getList());
+ }
+
+ @Test
+ void roundTripKeepsEmptyFaultList() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ String json = mapper.writeValueAsString(faultsFor(List.of()));
+
+ FaultListResult> restored = mapper.readValue(json, FaultListResult.class);
+
+ assertNotNull(restored.getList());
+ assertTrue(restored.getList().isEmpty());
+ assertEquals(TestResults.FALSE, restored.getSummarizedResult());
+ }
+}
From bc8c3783577e5770671f28e189be82433c0367f6 Mon Sep 17 00:00:00 2001
From: mmaehren <32199075+mmaehren@users.noreply.github.com>
Date: Tue, 15 Sep 2026 14:57:22 +0200
Subject: [PATCH 2/2] Ensure we can compare FaultListResults against expected
TestResults
---
.../core/probe/result/FaultListResult.java | 20 +---
.../core/report/rating/RatingInfluencer.java | 8 +-
.../core/report/rating/Recommendation.java | 8 +-
.../core/report/rating/ResultMatcher.java | 46 +++++++++
.../report/rating/RatingInfluencerTest.java | 98 +++++++++++++++++++
.../report/rating/RecommendationTest.java | 61 ++++++++++++
6 files changed, 220 insertions(+), 21 deletions(-)
create mode 100644 src/main/java/de/rub/nds/scanner/core/report/rating/ResultMatcher.java
create mode 100644 src/test/java/de/rub/nds/scanner/core/report/rating/RatingInfluencerTest.java
create mode 100644 src/test/java/de/rub/nds/scanner/core/report/rating/RecommendationTest.java
diff --git a/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java b/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java
index 040f284c..a105e4ff 100644
--- a/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java
+++ b/src/main/java/de/rub/nds/scanner/core/probe/result/FaultListResult.java
@@ -20,21 +20,11 @@
import java.util.List;
/**
- * Represents {@link TestResult}s which list the features for which a fault has been observed, e.g.
- * the named groups for which a peer reused its key. The list holds the affected features only, the
- * summary states whether any fault was found at all: an empty list summarizes to {@link
- * TestResults#FALSE}, a non-empty list to {@link TestResults#TRUE}. Since the summary is derived
- * from the list, the result can be used in requirements and rating influencers that expect a plain
- * {@link TestResults} (cf. {@link SummarizableTestResult#equalsExpectedResult(TestResult)}).
- *
- * If the features could not be examined at all, e.g. because a precondition of the probe was not
- * met, an explicit summary such as {@link TestResults#CANNOT_BE_TESTED} can be set instead. In this
- * case no list is collected and {@link #getList()} returns null, so that the absence of faults is
- * not confused with an actual absence of faults.
- *
- *
Note that a probe must set this result explicitly (i.e. {@code put(property, new
- * FaultListResult<>(property, faults))}), as passing a bare {@link List} yields a plain {@link
- * ListResult}.
+ * Represents {@link TestResult}s which list the features for which a fault has been observed. The
+ * list holds the affected features only, the summary states whether any fault was found at all: an
+ * empty list summarizes to {@link TestResults#FALSE}, a non-empty list to {@link TestResults#TRUE}.
+ * An explicit TestResult can be set, for example, to communicate that the test could not be
+ * applied.
*
* @param the type of the listed faulty features.
*/
diff --git a/src/main/java/de/rub/nds/scanner/core/report/rating/RatingInfluencer.java b/src/main/java/de/rub/nds/scanner/core/report/rating/RatingInfluencer.java
index 6a003633..1f6c5403 100644
--- a/src/main/java/de/rub/nds/scanner/core/report/rating/RatingInfluencer.java
+++ b/src/main/java/de/rub/nds/scanner/core/report/rating/RatingInfluencer.java
@@ -109,8 +109,10 @@ public void addPropertyRatingInfluencer(PropertyResultRatingInfluencer ratingInf
}
/**
- * Gets the property rating influencer for a specific test result. If no influencer is found for
- * the given result, returns a new influencer with zero influence.
+ * Gets the property rating influencer for a specific test result. A complex result which
+ * summarizes to a plain {@link de.rub.nds.scanner.core.probe.result.TestResults} is matched by
+ * its summary. If no influencer is found for the given result, returns a new influencer with
+ * zero influence.
*
* @param result the test result to find an influencer for
* @return the matching property rating influencer, or a new one with zero influence if not
@@ -118,7 +120,7 @@ public void addPropertyRatingInfluencer(PropertyResultRatingInfluencer ratingInf
*/
public PropertyResultRatingInfluencer getPropertyRatingInfluencer(TestResult result) {
for (PropertyResultRatingInfluencer ri : propertyRatingInfluencers) {
- if (ri.getResult().equalsExpectedResult(result)) {
+ if (ResultMatcher.matches(result, ri.getResult())) {
return ri;
}
}
diff --git a/src/main/java/de/rub/nds/scanner/core/report/rating/Recommendation.java b/src/main/java/de/rub/nds/scanner/core/report/rating/Recommendation.java
index d92f6faa..dbe6637b 100644
--- a/src/main/java/de/rub/nds/scanner/core/report/rating/Recommendation.java
+++ b/src/main/java/de/rub/nds/scanner/core/report/rating/Recommendation.java
@@ -308,15 +308,17 @@ public void setPropertyRecommendations(
}
/**
- * Gets the recommendation for a specific test result. If no recommendation is found, returns a
- * default recommendation with no information available message.
+ * Gets the recommendation for a specific test result. A complex result which summarizes to a
+ * plain {@link de.rub.nds.scanner.core.probe.result.TestResults} is matched by its summary. If
+ * no recommendation is found, returns a default recommendation with no information available
+ * message.
*
* @param result the test result to find a recommendation for
* @return the matching recommendation or a default recommendation if not found
*/
public PropertyResultRecommendation getPropertyResultRecommendation(TestResult result) {
for (PropertyResultRecommendation r : propertyRecommendations) {
- if (r.getResult() == result) {
+ if (ResultMatcher.matches(result, r.getResult())) {
return r;
}
}
diff --git a/src/main/java/de/rub/nds/scanner/core/report/rating/ResultMatcher.java b/src/main/java/de/rub/nds/scanner/core/report/rating/ResultMatcher.java
new file mode 100644
index 00000000..4b375b50
--- /dev/null
+++ b/src/main/java/de/rub/nds/scanner/core/report/rating/ResultMatcher.java
@@ -0,0 +1,46 @@
+/*
+ * Scanner Core - A Modular Framework for Probe Definition, Execution, and Result Analysis.
+ *
+ * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
+ *
+ * Licensed under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+package de.rub.nds.scanner.core.report.rating;
+
+import de.rub.nds.scanner.core.probe.result.SummarizableTestResult;
+import de.rub.nds.scanner.core.probe.result.TestResult;
+
+/**
+ * Matches the {@link TestResult} a scan yielded for a property against the result a {@link
+ * RatingInfluencer} or {@link Recommendation} has been configured for.
+ */
+final class ResultMatcher {
+
+ private ResultMatcher() {
+ // Private constructor to prevent instantiation of utility class
+ }
+
+ /**
+ * Determines whether the actual result of a property matches the result a rating influencer or
+ * recommendation has been configured for. The comparison is performed by the actual result, as
+ * this may be a {@link SummarizableTestResult}) which compares against its own summary result.
+ *
+ * @param actualResult the result the scan yielded, may be null
+ * @param configuredResult the result the influencer or recommendation is configured for, may be
+ * null
+ * @return true if the actual result matches the configured result
+ */
+ static boolean matches(TestResult actualResult, TestResult configuredResult) {
+ if (actualResult == null || configuredResult == null) {
+ return false;
+ }
+ try {
+ return actualResult.equalsExpectedResult(configuredResult);
+ } catch (IllegalArgumentException e) {
+ // The actual result is a complex result which does not know how to compare itself to
+ // the configured result.
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/de/rub/nds/scanner/core/report/rating/RatingInfluencerTest.java b/src/test/java/de/rub/nds/scanner/core/report/rating/RatingInfluencerTest.java
new file mode 100644
index 00000000..84f1268f
--- /dev/null
+++ b/src/test/java/de/rub/nds/scanner/core/report/rating/RatingInfluencerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Scanner Core - A Modular Framework for Probe Definition, Execution, and Result Analysis.
+ *
+ * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
+ *
+ * Licensed under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+package de.rub.nds.scanner.core.report.rating;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import de.rub.nds.scanner.core.TestAnalyzedProperty;
+import de.rub.nds.scanner.core.probe.result.FaultListResult;
+import de.rub.nds.scanner.core.probe.result.ListResult;
+import de.rub.nds.scanner.core.probe.result.TestResult;
+import de.rub.nds.scanner.core.probe.result.TestResults;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+class RatingInfluencerTest {
+
+ private static final RatingInfluencer INFLUENCER =
+ new RatingInfluencer(
+ TestAnalyzedProperty.TEST_ANALYZED_PROPERTY,
+ new PropertyResultRatingInfluencer(TestResults.TRUE, -200),
+ new PropertyResultRatingInfluencer(TestResults.FALSE, 50),
+ new PropertyResultRatingInfluencer(TestResults.CANNOT_BE_TESTED, -10));
+
+ private static int influenceFor(TestResult result) {
+ return INFLUENCER.getPropertyRatingInfluencer(result).getInfluence();
+ }
+
+ private static FaultListResult faultsFor(List faultyFeatures) {
+ return new FaultListResult<>(TestAnalyzedProperty.TEST_ANALYZED_PROPERTY, faultyFeatures);
+ }
+
+ @Test
+ void plainResultsAreMatched() {
+ assertEquals(-200, influenceFor(TestResults.TRUE));
+ assertEquals(50, influenceFor(TestResults.FALSE));
+ }
+
+ @Test
+ void listedFaultsAreMatchedAsTrue() {
+ assertEquals(-200, influenceFor(faultsFor(List.of("SECP256R1"))));
+ }
+
+ @Test
+ void absentFaultsAreMatchedAsFalse() {
+ assertEquals(50, influenceFor(faultsFor(List.of())));
+ }
+
+ @Test
+ void explicitSummaryIsMatched() {
+ assertEquals(
+ -10,
+ influenceFor(
+ new FaultListResult(
+ TestAnalyzedProperty.TEST_ANALYZED_PROPERTY,
+ TestResults.CANNOT_BE_TESTED)));
+ }
+
+ @Test
+ void unconfiguredResultYieldsNeutralInfluence() {
+ assertEquals(0, influenceFor(TestResults.PARTIALLY));
+ }
+
+ @Test
+ void resultWithoutSummaryYieldsNeutralInfluence() {
+ // a plain ListResult cannot be compared to the TestResults of the configuration, which
+ // must not fail the rating of the report as a whole
+ assertEquals(
+ 0,
+ influenceFor(
+ new ListResult<>(
+ TestAnalyzedProperty.TEST_ANALYZED_PROPERTY,
+ List.of("SECP256R1"))));
+ }
+
+ @Test
+ void faultsInfluenceTheScore() {
+ SiteReportRater rater =
+ new SiteReportRater(
+ new RatingInfluencers(new LinkedList<>(List.of(INFLUENCER))),
+ new Recommendations(List.of()));
+
+ assertEquals(
+ -200,
+ rater.getScoreReport(
+ Map.of(
+ TestAnalyzedProperty.TEST_ANALYZED_PROPERTY,
+ faultsFor(List.of("SECP256R1"))))
+ .getScore());
+ }
+}
diff --git a/src/test/java/de/rub/nds/scanner/core/report/rating/RecommendationTest.java b/src/test/java/de/rub/nds/scanner/core/report/rating/RecommendationTest.java
new file mode 100644
index 00000000..1ed5d135
--- /dev/null
+++ b/src/test/java/de/rub/nds/scanner/core/report/rating/RecommendationTest.java
@@ -0,0 +1,61 @@
+/*
+ * Scanner Core - A Modular Framework for Probe Definition, Execution, and Result Analysis.
+ *
+ * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
+ *
+ * Licensed under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+package de.rub.nds.scanner.core.report.rating;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import de.rub.nds.scanner.core.TestAnalyzedProperty;
+import de.rub.nds.scanner.core.probe.result.FaultListResult;
+import de.rub.nds.scanner.core.probe.result.TestResult;
+import de.rub.nds.scanner.core.probe.result.TestResults;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class RecommendationTest {
+
+ private static final Recommendation RECOMMENDATION =
+ new Recommendation(
+ TestAnalyzedProperty.TEST_ANALYZED_PROPERTY,
+ List.of(
+ new PropertyResultRecommendation(
+ TestResults.TRUE, "A fault was observed", "Fix it"),
+ new PropertyResultRecommendation(
+ TestResults.FALSE,
+ "No fault was observed",
+ "Keep it that way")));
+
+ private static String statusFor(TestResult result) {
+ return RECOMMENDATION.getPropertyResultRecommendation(result).getShortDescription();
+ }
+
+ private static FaultListResult faultsFor(List faultyFeatures) {
+ return new FaultListResult<>(TestAnalyzedProperty.TEST_ANALYZED_PROPERTY, faultyFeatures);
+ }
+
+ @Test
+ void plainResultsAreMatched() {
+ assertEquals("A fault was observed", statusFor(TestResults.TRUE));
+ assertEquals("No fault was observed", statusFor(TestResults.FALSE));
+ }
+
+ @Test
+ void listedFaultsAreMatchedAsTrue() {
+ assertEquals("A fault was observed", statusFor(faultsFor(List.of("SECP256R1"))));
+ }
+
+ @Test
+ void absentFaultsAreMatchedAsFalse() {
+ assertEquals("No fault was observed", statusFor(faultsFor(List.of())));
+ }
+
+ @Test
+ void unconfiguredResultYieldsDefaultRecommendation() {
+ assertEquals(Recommendation.NO_INFORMATION_FOUND, statusFor(TestResults.PARTIALLY));
+ }
+}