mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge pull request #2778 from ebean-orm/feature/metric-naming-convention
Support a naming convention function that can be applied to metrics names for reporting
This commit is contained in:
@@ -1,20 +1,9 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.meta.AbstractMetricVisitor;
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaCountMetric;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricData;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.meta.ServerMetricsAsJson;
|
||||
import io.ebean.meta.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* DefaultServer based implementation of MetaInfoManager.
|
||||
@@ -22,9 +11,11 @@ import java.util.List;
|
||||
final class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
|
||||
private final DefaultServer server;
|
||||
private final Function<String, String> naming;
|
||||
|
||||
DefaultMetaInfoManager(DefaultServer server) {
|
||||
DefaultMetaInfoManager(DefaultServer server, Function<String, String> naming) {
|
||||
this.server = server;
|
||||
this.naming = naming;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +40,7 @@ final class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
|
||||
@Override
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor(server.name());
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor(server.name(), naming);
|
||||
visitMetrics(basic);
|
||||
return basic;
|
||||
}
|
||||
@@ -68,6 +59,11 @@ final class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
super(true, true, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, String> namingConvention() {
|
||||
return MetricNamingMatch.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTimed(MetaTimedMetric metric) {
|
||||
// ignore
|
||||
|
||||
@@ -213,7 +213,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this, this.config.getMetricNaming());
|
||||
this.serverPlugins = config.getPlugins();
|
||||
this.ddlGenerator = config.initDdlGenerator(this);
|
||||
this.scriptRunner = new DScriptRunner(this);
|
||||
|
||||
@@ -1482,7 +1482,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
iudMetrics.visit(visitor);
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (!queryPlan.isEmptyStats()) {
|
||||
visitor.visitQuery(queryPlan.getSnapshot(visitor.reset()));
|
||||
visitor.visitQuery(queryPlan.visit(visitor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ final class DCountMetric implements CountMetric {
|
||||
|
||||
private final String name;
|
||||
private final LongAdder count = new LongAdder();
|
||||
private String reportName;
|
||||
|
||||
DCountMetric(String name) {
|
||||
this.name = name;
|
||||
@@ -50,10 +51,17 @@ final class DCountMetric implements CountMetric {
|
||||
public void visit(MetricVisitor visitor) {
|
||||
long val = visitor.reset() ? count.sumThenReset() : count.sum();
|
||||
if (val > 0) {
|
||||
final String name = reportName != null ? reportName : reportName(visitor);
|
||||
visitor.visitCount(new DCountMetricStats(name, val));
|
||||
}
|
||||
}
|
||||
|
||||
String reportName(MetricVisitor visitor) {
|
||||
final String tmp = visitor.namingConvention().apply(name);
|
||||
this.reportName = tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private static class DCountMetricStats implements CountMetricStats {
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -11,6 +11,7 @@ final class DQueryPlanMetric implements QueryPlanMetric {
|
||||
private final DQueryPlanMeta meta;
|
||||
private final DTimedMetric metric;
|
||||
private boolean collected;
|
||||
private String reportName;
|
||||
|
||||
DQueryPlanMetric(DQueryPlanMeta meta, DTimedMetric metric) {
|
||||
this.meta = meta;
|
||||
@@ -21,11 +22,18 @@ final class DQueryPlanMetric implements QueryPlanMetric {
|
||||
public void visit(MetricVisitor visitor) {
|
||||
TimedMetricStats stats = metric.collect(visitor.reset());
|
||||
if (stats != null) {
|
||||
visitor.visitQuery(new Stats(meta, stats, collected));
|
||||
String name = reportName != null ? reportName : reportName(visitor);
|
||||
visitor.visitQuery(new Stats(name, meta, stats, collected));
|
||||
collected = true;
|
||||
}
|
||||
}
|
||||
|
||||
String reportName(MetricVisitor visitor) {
|
||||
final String tmp = visitor.namingConvention().apply(meta.getName());
|
||||
this.reportName = tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric metric() {
|
||||
return metric;
|
||||
@@ -33,11 +41,13 @@ final class DQueryPlanMetric implements QueryPlanMetric {
|
||||
|
||||
private static class Stats implements MetaQueryMetric {
|
||||
|
||||
private final String name;
|
||||
private final DQueryPlanMeta meta;
|
||||
private final TimedMetricStats stats;
|
||||
private final boolean collected;
|
||||
|
||||
private Stats(DQueryPlanMeta meta, TimedMetricStats stats, boolean collected) {
|
||||
private Stats(String name, DQueryPlanMeta meta, TimedMetricStats stats, boolean collected) {
|
||||
this.name = name;
|
||||
this.meta = meta;
|
||||
this.stats = stats;
|
||||
this.collected = collected;
|
||||
@@ -75,7 +85,7 @@ final class DQueryPlanMetric implements QueryPlanMetric {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return meta.getName();
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,7 @@ final class DTimedMetric implements TimedMetric {
|
||||
private final LongAdder total = new LongAdder();
|
||||
private final LongAccumulator max = new LongAccumulator(Math::max, Long.MIN_VALUE);
|
||||
private boolean collected;
|
||||
private String reportName;
|
||||
|
||||
DTimedMetric(String name) {
|
||||
this.name = name;
|
||||
@@ -40,9 +41,6 @@ final class DTimedMetric implements TimedMetric {
|
||||
add((System.nanoTime() - startNanos) / 1000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value. Usually the value is Time or Bytes etc.
|
||||
*/
|
||||
@Override
|
||||
public void add(long value) {
|
||||
count.increment();
|
||||
@@ -55,9 +53,6 @@ final class DTimedMetric implements TimedMetric {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
max.reset();
|
||||
@@ -67,30 +62,42 @@ final class DTimedMetric implements TimedMetric {
|
||||
|
||||
@Override
|
||||
public void visit(MetricVisitor visitor) {
|
||||
DTimeMetricStats metric = collect(visitor.reset());
|
||||
if (metric != null) {
|
||||
visitor.visitTimed(metric);
|
||||
final long countSum = visitor.reset() ? count.sumThenReset() : count.sum();
|
||||
if (countSum > 0) {
|
||||
final String name = reportName != null ? reportName : reportName(visitor);
|
||||
visitor.visitTimed(stats(visitor.reset(), name, countSum));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DTimeMetricStats collect(boolean reset) {
|
||||
return (count.sum() == 0) ? null : getStatistics(reset);
|
||||
final long countSum = reset ? count.sumThenReset() : count.sum();
|
||||
if (countSum == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return stats(reset, name, countSum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current statistics resetting the internal values if reset is true.
|
||||
*/
|
||||
private DTimeMetricStats getStatistics(boolean reset) {
|
||||
private DTimeMetricStats stats(boolean reset, String name, long countSum) {
|
||||
try {
|
||||
if (reset) {
|
||||
return new DTimeMetricStats(name, collected, count.sumThenReset(), total.sumThenReset(), max.getThenReset());
|
||||
return new DTimeMetricStats(name, collected, countSum, total.sumThenReset(), max.getThenReset());
|
||||
} else {
|
||||
return new DTimeMetricStats(name, collected, count.sum(), total.sum(), max.get());
|
||||
return new DTimeMetricStats(name, collected, countSum, total.sum(), max.get());
|
||||
}
|
||||
} finally {
|
||||
collected = true;
|
||||
}
|
||||
}
|
||||
|
||||
String reportName(MetricVisitor visitor) {
|
||||
final String tmp = visitor.namingConvention().apply(name);
|
||||
this.reportName = tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-3
@@ -13,6 +13,7 @@ final class DTimedProfileLocation extends DProfileLocation implements TimedProfi
|
||||
private final TimedMetric timedMetric;
|
||||
private final boolean overrideMetricName;
|
||||
private String fullName;
|
||||
private String reportName;
|
||||
|
||||
DTimedProfileLocation(int lineNumber, String label, TimedMetric timedMetric) {
|
||||
super(lineNumber);
|
||||
@@ -47,11 +48,17 @@ final class DTimedProfileLocation extends DProfileLocation implements TimedProfi
|
||||
public void visit(MetricVisitor visitor) {
|
||||
TimedMetricStats collect = timedMetric.collect(visitor.reset());
|
||||
if (collect != null) {
|
||||
if (overrideMetricName) {
|
||||
collect.setName(fullName);
|
||||
}
|
||||
final String name = reportName != null ? reportName : reportName(visitor, collect.name());
|
||||
collect.setName(name);
|
||||
collect.setLocation(location());
|
||||
visitor.visitTimed(collect);
|
||||
}
|
||||
}
|
||||
|
||||
private String reportName(MetricVisitor visitor, String name) {
|
||||
final String defaultName = overrideMetricName ? fullName : name;
|
||||
final String tmp = visitor.namingConvention().apply(defaultName);
|
||||
this.reportName = tmp;
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.ebean.ProfileLocation;
|
||||
import io.ebean.config.dbplatform.SqlLimitResponse;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebeaninternal.api.*;
|
||||
@@ -279,8 +280,8 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
/**
|
||||
* Return a copy of the current query statistics.
|
||||
*/
|
||||
public final Snapshot getSnapshot(boolean reset) {
|
||||
return stats.getSnapshot(reset);
|
||||
public final Snapshot visit(MetricVisitor visitor) {
|
||||
return stats.visit(visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebean.metric.TimedMetricStats;
|
||||
|
||||
@@ -13,6 +14,7 @@ public final class CQueryPlanStats {
|
||||
private final TimedMetric timedMetric;
|
||||
private boolean collected;
|
||||
private long lastQueryTime;
|
||||
private String reportName;
|
||||
|
||||
/**
|
||||
* Construct for a given query plan.
|
||||
@@ -55,23 +57,32 @@ public final class CQueryPlanStats {
|
||||
/**
|
||||
* Return a Snapshot of the query execution statistics potentially resetting the internal counters.
|
||||
*/
|
||||
Snapshot getSnapshot(boolean reset) {
|
||||
TimedMetricStats collect = timedMetric.collect(reset);
|
||||
Snapshot snapshot = new Snapshot(collected, queryPlan, collect);
|
||||
Snapshot visit(MetricVisitor visitor) {
|
||||
TimedMetricStats collect = timedMetric.collect(visitor.reset());
|
||||
String name = reportName != null ? reportName : reportName(visitor);
|
||||
Snapshot snapshot = new Snapshot(name, collected, queryPlan, collect);
|
||||
collected = true;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
String reportName(MetricVisitor visitor) {
|
||||
final String tmp = visitor.namingConvention().apply(queryPlan.getName());
|
||||
this.reportName = tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* A snapshot of the current statistics for a query plan.
|
||||
*/
|
||||
static class Snapshot implements MetaQueryMetric {
|
||||
|
||||
private final String name;
|
||||
private final boolean collected;
|
||||
private final CQueryPlan queryPlan;
|
||||
private final TimedMetricStats metrics;
|
||||
|
||||
Snapshot(boolean collected, CQueryPlan queryPlan, TimedMetricStats metrics) {
|
||||
Snapshot(String name, boolean collected, CQueryPlan queryPlan, TimedMetricStats metrics) {
|
||||
this.name = name;
|
||||
this.collected = collected;
|
||||
this.queryPlan = queryPlan;
|
||||
this.metrics = metrics;
|
||||
@@ -94,7 +105,7 @@ public final class CQueryPlanStats {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return queryPlan.getName();
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+43
@@ -1,12 +1,55 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class BasicProfileLocationTest {
|
||||
|
||||
Function<String,String> naming = (String name) -> "prefix[" + name.replace('.', '-') + "]";
|
||||
|
||||
@Test
|
||||
void metricNameFromOverride() {
|
||||
DTimedProfileLocation loc = new DTimedProfileLocation(12, "", MetricFactory.get().createTimedMetric("a.b.c"));
|
||||
loc.initWith("foo.label");
|
||||
loc.add(42);
|
||||
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
loc.visit(visitor);
|
||||
|
||||
List<MetaTimedMetric> result = visitor.timedMetrics();
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[txn-named-foo-label]");
|
||||
assertThat(result.get(0).total()).isEqualTo(42);
|
||||
|
||||
loc.add(21);
|
||||
BasicMetricVisitor visitor2 = new BasicMetricVisitor("v", naming);
|
||||
loc.visit(visitor2);
|
||||
List<MetaTimedMetric> result2 = visitor2.timedMetrics();
|
||||
assertThat(result2).hasSize(1);
|
||||
assertThat(result2.get(0).name()).isEqualTo("prefix[txn-named-foo-label]");
|
||||
assertThat(result2.get(0).total()).isEqualTo(21);
|
||||
}
|
||||
|
||||
@Test
|
||||
void metricNameFromTimed() {
|
||||
DTimedProfileLocation loc = new DTimedProfileLocation(12, "foo", MetricFactory.get().createTimedMetric("a.b.c"));
|
||||
loc.add(42);
|
||||
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
loc.visit(visitor);
|
||||
|
||||
List<MetaTimedMetric> result = visitor.timedMetrics();
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[a-b-c]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void obtain() {
|
||||
DProfileLocation loc = new DTimedProfileLocation(12, "foo", MetricFactory.get().createTimedMetric("junk"));
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaCountMetric;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DCountMetricTest {
|
||||
|
||||
Function<String, String> naming = (String name) -> "prefix[" + name.replace('.', '-') + "]";
|
||||
|
||||
@Test
|
||||
void visit() {
|
||||
|
||||
DCountMetric counter = new DCountMetric("org.hello");
|
||||
counter.add(7);
|
||||
{
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
counter.visit(visitor);
|
||||
List<MetaCountMetric> result = visitor.countMetrics();
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[org-hello]");
|
||||
assertThat(result.get(0).count()).isEqualTo(7);
|
||||
}
|
||||
{
|
||||
// second collection
|
||||
counter.add(4);
|
||||
counter.add(8);
|
||||
BasicMetricVisitor visitor2 = new BasicMetricVisitor("v", naming);
|
||||
counter.visit(visitor2);
|
||||
|
||||
List<MetaCountMetric> result2 = visitor2.countMetrics();
|
||||
assertThat(result2).hasSize(1);
|
||||
assertThat(result2.get(0).name()).isEqualTo("prefix[org-hello]");
|
||||
assertThat(result2.get(0).count()).isEqualTo(12);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DQueryPlanMetricTest {
|
||||
|
||||
Function<String, String> naming = (String name) -> "prefix[" + name.replace('.', '-') + "]";
|
||||
|
||||
@Test
|
||||
void visit() {
|
||||
|
||||
DQueryPlanMeta meta = new DQueryPlanMeta(Object.class, "lab", null, "sql");
|
||||
DTimedMetric metric = new DTimedMetric("org.timed.plan");
|
||||
DQueryPlanMetric planMetric = new DQueryPlanMetric(meta, metric);
|
||||
|
||||
metric.add(560);
|
||||
metric.add(260);
|
||||
{
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
planMetric.visit(visitor);
|
||||
List<MetaQueryMetric> result = visitor.queryMetrics();
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[dto-Object_lab]");
|
||||
assertThat(result.get(0).count()).isEqualTo(2);
|
||||
assertThat(result.get(0).total()).isEqualTo(820);
|
||||
}
|
||||
metric.add(410);
|
||||
{
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
planMetric.visit(visitor);
|
||||
List<MetaQueryMetric> result = visitor.queryMetrics();
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[dto-Object_lab]");
|
||||
assertThat(result.get(0).count()).isEqualTo(1);
|
||||
assertThat(result.get(0).total()).isEqualTo(410);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DTimedMetricTest {
|
||||
@@ -52,4 +57,37 @@ public class DTimedMetricTest {
|
||||
assertThat(stats.total()).isGreaterThan(10000);
|
||||
assertThat(stats.max()).isEqualTo(stats.total() / 2);
|
||||
}
|
||||
|
||||
Function<String, String> naming = (String name) -> "prefix[" + name.replace('.', '-') + "]";
|
||||
|
||||
@Test
|
||||
void visit() {
|
||||
DTimedMetric metric = new DTimedMetric("org.timed");
|
||||
metric.add(560);
|
||||
metric.add(500);
|
||||
{
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
metric.visit(visitor);
|
||||
List<MetaTimedMetric> result = visitor.timedMetrics();
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[org-timed]");
|
||||
assertThat(result.get(0).count()).isEqualTo(2);
|
||||
assertThat(result.get(0).total()).isEqualTo(1060);
|
||||
}
|
||||
|
||||
metric.add(160);
|
||||
metric.add(100);
|
||||
metric.add(150);
|
||||
{
|
||||
BasicMetricVisitor visitor = new BasicMetricVisitor("v", naming);
|
||||
metric.visit(visitor);
|
||||
List<MetaTimedMetric> result = visitor.timedMetrics();
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).name()).isEqualTo("prefix[org-timed]");
|
||||
assertThat(result.get(0).count()).isEqualTo(3);
|
||||
assertThat(result.get(0).total()).isEqualTo(410);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user