From 984e61de792fa171d3b3dec3d996bbf1adfd7bc2 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 19 Oct 2016 23:39:29 +0200 Subject: [PATCH] SONAR-8233 WS components/search_projects return facets for desired metrics --- .../component/es/ProjectMeasuresIndex.java | 68 +++++-- .../main/java/org/sonar/server/es/Facets.java | 8 +- .../es/ProjectMeasuresIndexTest.java | 185 +++++++++++++++++- .../ws/SearchProjectsActionTest.java | 5 +- 4 files changed, 242 insertions(+), 24 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/es/ProjectMeasuresIndex.java b/server/sonar-server/src/main/java/org/sonar/server/component/es/ProjectMeasuresIndex.java index c8160b08959..21d522208a2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/es/ProjectMeasuresIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/es/ProjectMeasuresIndex.java @@ -19,6 +19,9 @@ */ package org.sonar.server.component.es; +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.stream.IntStream; import java.util.Set; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.index.query.BoolQueryBuilder; @@ -26,6 +29,7 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.aggregations.bucket.range.RangeBuilder; import org.elasticsearch.search.sort.SortOrder; import org.sonar.server.component.es.ProjectMeasuresQuery.MetricCriterion; import org.sonar.server.es.BaseIndex; @@ -40,7 +44,13 @@ import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.QueryBuilders.termsQuery; +import static org.elasticsearch.search.aggregations.AggregationBuilders.filters; +import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY; +import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY; import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY; +import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING_KEY; +import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY; +import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY; import static org.sonar.server.component.es.ProjectMeasuresIndexDefinition.FIELD_MEASURES; import static org.sonar.server.component.es.ProjectMeasuresIndexDefinition.FIELD_MEASURES_KEY; import static org.sonar.server.component.es.ProjectMeasuresIndexDefinition.FIELD_MEASURES_VALUE; @@ -64,19 +74,6 @@ public class ProjectMeasuresIndex extends BaseIndex { public SearchIdResult search(ProjectMeasuresQuery query, SearchOptions searchOptions) { QueryBuilder esQuery = createEsQuery(query); - AggregationBuilder locAggregation = AggregationBuilders.nested("nested_" + NCLOC_KEY) - .path("measures") - .subAggregation( - AggregationBuilders.filter("filter_" + NCLOC_KEY) - .filter(termsQuery("measures.key", NCLOC_KEY)) - .subAggregation(AggregationBuilders.range(NCLOC_KEY) - .field("measures.value") - .addUnboundedTo(1_000d) - .addRange(1_000d, 10_000d) - .addRange(10_000d, 100_000d) - .addRange(100_000d, 500_000d) - .addUnboundedFrom(500_000))); - SearchRequestBuilder request = getClient() .prepareSearch(INDEX_PROJECT_MEASURES) .setTypes(TYPE_PROJECT_MEASURES) @@ -84,12 +81,55 @@ public class ProjectMeasuresIndex extends BaseIndex { .setQuery(esQuery) .setFrom(searchOptions.getOffset()) .setSize(searchOptions.getLimit()) - .addAggregation(locAggregation) + .addAggregation(createRangeFacet(DUPLICATED_LINES_DENSITY_KEY, ImmutableList.of(3d, 5d, 10d, 20d))) + .addAggregation(createRangeFacet(COVERAGE_KEY, ImmutableList.of(30d, 50d, 70d, 80d))) + .addAggregation(createRangeFacet(NCLOC_KEY, ImmutableList.of(1_000d, 10_000d, 100_000d, 500_000d))) + .addAggregation(createRatingFacet(SQALE_RATING_KEY)) + .addAggregation(createRatingFacet(RELIABILITY_RATING_KEY)) + .addAggregation(createRatingFacet(SECURITY_RATING_KEY)) .addSort(FIELD_NAME + "." + SORT_SUFFIX, SortOrder.ASC); return new SearchIdResult<>(request.get(), id -> id); } + private static AggregationBuilder createRangeFacet(String metricKey, List thresholds) { + RangeBuilder rangeAgg = AggregationBuilders.range(metricKey).field(FIELD_VALUE); + final int lastIndex = thresholds.size() - 1; + IntStream.range(0, thresholds.size()) + .forEach(i -> { + if (i == 0) { + rangeAgg.addUnboundedTo(thresholds.get(0)); + rangeAgg.addRange(thresholds.get(0), thresholds.get(1)); + } else if (i == lastIndex) { + rangeAgg.addUnboundedFrom(thresholds.get(lastIndex)); + } else { + rangeAgg.addRange(thresholds.get(i), thresholds.get(i + 1)); + } + }); + + return AggregationBuilders.nested("nested_" + metricKey) + .path(FIELD_MEASURES) + .subAggregation( + AggregationBuilders.filter("filter_" + metricKey) + .filter(termsQuery(FIELD_KEY, metricKey)) + .subAggregation(rangeAgg)); + } + + private static AggregationBuilder createRatingFacet(String metricKey) { + return AggregationBuilders.nested("nested_" + metricKey) + .path(FIELD_MEASURES) + .subAggregation( + AggregationBuilders.filter("filter_" + metricKey) + .filter(termsQuery(FIELD_KEY, metricKey)) + .subAggregation(filters(metricKey) + .filter("1", termQuery(FIELD_VALUE, 1d)) + .filter("2", termQuery(FIELD_VALUE, 2d)) + .filter("3", termQuery(FIELD_VALUE, 3d)) + .filter("4", termQuery(FIELD_VALUE, 4d)) + .filter("5", termQuery(FIELD_VALUE, 5d)) + )); + } + private QueryBuilder createEsQuery(ProjectMeasuresQuery query) { BoolQueryBuilder filters = boolQuery() .must(createAuthorizationFilter()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java b/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java index e82d8eeadf7..252446d5f8b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java +++ b/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java @@ -30,9 +30,9 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.HasAggregations; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.missing.Missing; -import org.elasticsearch.search.aggregations.bucket.range.Range; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.sum.Sum; @@ -69,8 +69,8 @@ public class Facets { processDateHistogram((Histogram) aggregation); } else if (Sum.class.isAssignableFrom(aggregation.getClass())) { processSum((Sum) aggregation); - } else if (Range.class.isAssignableFrom(aggregation.getClass())) { - processRange((Range) aggregation); + } else if (MultiBucketsAggregation.class.isAssignableFrom(aggregation.getClass())) { + processMultiBucketAggregation((MultiBucketsAggregation) aggregation); } else { throw new IllegalArgumentException("Aggregation type not supported yet: " + aggregation.getClass()); } @@ -126,7 +126,7 @@ public class Facets { getOrCreateFacet(aggregation.getName()).put(TOTAL, Math.round(aggregation.getValue())); } - private void processRange(Range aggregation) { + private void processMultiBucketAggregation(MultiBucketsAggregation aggregation) { LinkedHashMap facet = getOrCreateFacet(aggregation.getName()); aggregation.getBuckets().forEach(bucket -> facet.put(bucket.getKeyAsString(), bucket.getDocCount())); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/es/ProjectMeasuresIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/es/ProjectMeasuresIndexTest.java index c38a1d5135c..58874673cf1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/es/ProjectMeasuresIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/es/ProjectMeasuresIndexTest.java @@ -49,7 +49,11 @@ import static org.sonar.server.component.es.ProjectMeasuresIndexDefinition.TYPE_ public class ProjectMeasuresIndexTest { + private static final String MAINTAINABILITY_RATING = "sqale_rating"; + private static final String RELIABILITY_RATING = "reliability_rating"; + private static final String SECURITY_RATING = "security_rating"; private static final String COVERAGE = "coverage"; + private static final String DUPLICATION = "duplicated_lines_density"; private static final String NCLOC = "ncloc"; @Rule @@ -235,8 +239,7 @@ public class ProjectMeasuresIndexTest { newDoc("P52", "K3", "N3").setMeasures(newArrayList(newMeasure(NCLOC, 100_000_000d))), newDoc("P53", "K3", "N3").setMeasures(newArrayList(newMeasure(NCLOC, 500_000d))), newDoc("P54", "K3", "N3").setMeasures(newArrayList(newMeasure(NCLOC, 1_000_000d))), - newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(NCLOC, 100_000_000_000d))) - ); + newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(NCLOC, 100_000_000_000d)))); Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets(); @@ -245,8 +248,182 @@ public class ProjectMeasuresIndexTest { entry("1000.0-10000.0", 2L), entry("10000.0-100000.0", 4L), entry("100000.0-500000.0", 2L), - entry("500000.0-*", 5L) - ); + entry("500000.0-*", 5L)); + } + + @Test + public void facet_coverage() { + addDocs( + // 3 docs with coverage<30% + newDoc("P11", "K1", "N1").setMeasures(newArrayList(newMeasure(COVERAGE, 0d))), + newDoc("P12", "K1", "N1").setMeasures(newArrayList(newMeasure(COVERAGE, 0d))), + newDoc("P13", "K1", "N1").setMeasures(newArrayList(newMeasure(COVERAGE, 29d))), + // 2 docs with coverage>=30% and coverage<50% + newDoc("P21", "K2", "N2").setMeasures(newArrayList(newMeasure(COVERAGE, 30d))), + newDoc("P22", "K2", "N2").setMeasures(newArrayList(newMeasure(COVERAGE, 49d))), + // 4 docs with coverage>=50% and coverage<70% + newDoc("P31", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 50d))), + newDoc("P32", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 60d))), + newDoc("P33", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 60d))), + newDoc("P34", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 69d))), + // 2 docs with coverage>=70% and coverage<80% + newDoc("P41", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 70d))), + newDoc("P42", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 79d))), + // 5 docs with coverage>= 80% + newDoc("P51", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 80d))), + newDoc("P52", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 80d))), + newDoc("P53", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 90d))), + newDoc("P54", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 90.5d))), + newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(COVERAGE, 100d)))); + + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets(); + + assertThat(facets.get(COVERAGE)).containsExactly( + entry("*-30.0", 3L), + entry("30.0-50.0", 2L), + entry("50.0-70.0", 4L), + entry("70.0-80.0", 2L), + entry("80.0-*", 5L)); + } + + @Test + public void facet_duplicated_lines_density() { + addDocs( + // 3 docs with duplication<3% + newDoc("P11", "K1", "N1").setMeasures(newArrayList(newMeasure(DUPLICATION, 0d))), + newDoc("P12", "K1", "N1").setMeasures(newArrayList(newMeasure(DUPLICATION, 0d))), + newDoc("P13", "K1", "N1").setMeasures(newArrayList(newMeasure(DUPLICATION, 2.9d))), + // 2 docs with duplication>=3% and duplication<5% + newDoc("P21", "K2", "N2").setMeasures(newArrayList(newMeasure(DUPLICATION, 3d))), + newDoc("P22", "K2", "N2").setMeasures(newArrayList(newMeasure(DUPLICATION, 4.9d))), + // 4 docs with duplication>=5% and duplication<10% + newDoc("P31", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 5d))), + newDoc("P32", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 6d))), + newDoc("P33", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 6d))), + newDoc("P34", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 9.9d))), + // 2 docs with duplication>=10% and duplication<20% + newDoc("P41", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 10d))), + newDoc("P42", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 19.9d))), + // 5 docs with duplication>= 20% + newDoc("P51", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 20d))), + newDoc("P52", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 20d))), + newDoc("P53", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 50d))), + newDoc("P54", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 80d))), + newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(DUPLICATION, 100d)))); + + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets(); + + assertThat(facets.get(DUPLICATION)).containsExactly( + entry("*-3.0", 3L), + entry("3.0-5.0", 2L), + entry("5.0-10.0", 4L), + entry("10.0-20.0", 2L), + entry("20.0-*", 5L)); + } + + @Test + public void facet_maintainability_rating() { + addDocs( + // 3 docs with rating A + newDoc("P11", "K1", "N1").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 1d))), + newDoc("P12", "K1", "N1").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 1d))), + newDoc("P13", "K1", "N1").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 1d))), + // 2 docs with rating B + newDoc("P21", "K2", "N2").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 2d))), + newDoc("P22", "K2", "N2").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 2d))), + // 4 docs with rating C + newDoc("P31", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 3d))), + newDoc("P32", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 3d))), + newDoc("P33", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 3d))), + newDoc("P34", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 3d))), + // 2 docs with rating D + newDoc("P41", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 4d))), + newDoc("P42", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 4d))), + // 5 docs with rating E + newDoc("P51", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 5d))), + newDoc("P52", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 5d))), + newDoc("P53", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 5d))), + newDoc("P54", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 5d))), + newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(MAINTAINABILITY_RATING, 5d)))); + + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets(); + + assertThat(facets.get(MAINTAINABILITY_RATING)).containsExactly( + entry("1", 3L), + entry("2", 2L), + entry("3", 4L), + entry("4", 2L), + entry("5", 5L)); + } + + @Test + public void facet_reliability_rating() { + addDocs( + // 3 docs with rating A + newDoc("P11", "K1", "N1").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 1d))), + newDoc("P12", "K1", "N1").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 1d))), + newDoc("P13", "K1", "N1").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 1d))), + // 2 docs with rating B + newDoc("P21", "K2", "N2").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 2d))), + newDoc("P22", "K2", "N2").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 2d))), + // 4 docs with rating C + newDoc("P31", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 3d))), + newDoc("P32", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 3d))), + newDoc("P33", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 3d))), + newDoc("P34", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 3d))), + // 2 docs with rating D + newDoc("P41", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 4d))), + newDoc("P42", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 4d))), + // 5 docs with rating E + newDoc("P51", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 5d))), + newDoc("P52", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 5d))), + newDoc("P53", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 5d))), + newDoc("P54", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 5d))), + newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(RELIABILITY_RATING, 5d)))); + + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets(); + + assertThat(facets.get(RELIABILITY_RATING)).containsExactly( + entry("1", 3L), + entry("2", 2L), + entry("3", 4L), + entry("4", 2L), + entry("5", 5L)); + } + + @Test + public void facet_security_rating() { + addDocs( + // 3 docs with rating A + newDoc("P11", "K1", "N1").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 1.0d))), + newDoc("P12", "K1", "N1").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 1.0d))), + newDoc("P13", "K1", "N1").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 1.0d))), + // 2 docs with rating B + newDoc("P21", "K2", "N2").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 2.0d))), + newDoc("P22", "K2", "N2").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 2.0d))), + // 4 docs with rating C + newDoc("P31", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 3.0d))), + newDoc("P32", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 3.0d))), + newDoc("P33", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 3.0d))), + newDoc("P34", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 3.0d))), + // 2 docs with rating D + newDoc("P41", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 4.0d))), + newDoc("P42", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 4.0d))), + // 5 docs with rating E + newDoc("P51", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 5.0d))), + newDoc("P52", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 5.0d))), + newDoc("P53", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 5.0d))), + newDoc("P54", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 5.0d))), + newDoc("P55", "K3", "N3").setMeasures(newArrayList(newMeasure(SECURITY_RATING, 5.0d)))); + + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions()).getFacets(); + + assertThat(facets.get(SECURITY_RATING)).containsExactly( + entry("1", 3L), + entry("2", 2L), + entry("3", 4L), + entry("4", 2L), + entry("5", 5L)); } private void addDocs(ProjectMeasuresDoc... docs) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java index 09b6832ceca..3e56eea30be 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java @@ -199,8 +199,9 @@ public class SearchProjectsActionTest { SearchProjectsWsResponse result = call(request); - assertThat(result.getFacets().getFacetsCount()).isEqualTo(1); - Common.Facet facet = result.getFacets().getFacets(0); + Common.Facet facet = result.getFacets().getFacetsList().stream() + .filter(oneFacet -> NCLOC.equals(oneFacet.getProperty())) + .findFirst().orElseThrow(IllegalStateException::new); assertThat(facet.getProperty()).isEqualTo(NCLOC); assertThat(facet.getValuesCount()).isEqualTo(5); assertThat(facet.getValuesList()) -- 2.39.5