@Test
    void testValueAggregation() throws SQLException {
        final StatisticType statisticType = StatisticType.VALUE;
        // final long startDateMs =
        // DateUtil.parseNormalDateTimeString("2015-01-01T00:00:00.000Z");
        //Use a fixed start date to avoid any oddities caused by the power of 10 rounding
        final Instant startDate = LocalDateTime.of(
                2016, 12, 13, 11, 59, 3).toInstant(ZoneOffset.UTC);
        final int statNameCount = 4;
        final int timesCount = 10;
        final int numberOfDifferentPrecisions = 3;
        final int totalFlushCount = statNameCount * timesCount * numberOfDifferentPrecisions;

        final long expectedCountTotalByPrecision = statNameCount * timesCount
                * (statisticType.equals(StatisticType.COUNT)
                ? STAT_VALUE
                : 1);
        final long expectedValueTotalByPrecision = statNameCount * timesCount * STAT_VALUE;

        final long expectedCountTotal = expectedCountTotalByPrecision * numberOfDifferentPrecisions;
        final long expectedValueTotal = expectedValueTotalByPrecision * numberOfDifferentPrecisions;

        // Ensure we exercise batching with two full and one partial batch
        final int stage1BatchSize = (int) (totalFlushCount * 0.4);
        final int stage2BatchSize = (int) (timesCount * 0.4);
        sqlStatisticAggregationManager.setStage1BatchSize(stage1BatchSize);
        sqlStatisticAggregationManager.setStage2BatchSize(stage2BatchSize);
        LOGGER.info("Stage 1 batch size: {}", stage1BatchSize);
        LOGGER.info("Stage 2 batch size: {}", stage2BatchSize);

        final LogExecutionTime time = new LogExecutionTime();

        loadData(startDate, statNameCount, timesCount, statisticType);

        LOGGER.info("First aggregation run");
        LOGGER.info("startDate: " + startDate);
        runAggregation(startDate);

        assertThat(getAggregateTotal(COL_NAME_CNT))
                .isEqualTo(expectedCountTotal);
        assertThat(getAggregateTotal(COL_NAME_VAL))
                .isEqualTo(expectedValueTotal);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision);

        LOGGER.info("run aggregation again with no new data in SVS");
        runAggregation(startDate);

        assertThat(getAggregateTotal(COL_NAME_CNT))
                .isEqualTo(expectedCountTotal);
        assertThat(getAggregateTotal(COL_NAME_VAL))
                .isEqualTo(expectedValueTotal);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision);

        LOGGER.info("run aggregation again but pretend we are 2days in the future");
        Instant futureDate = startDate.plus(2, ChronoUnit.DAYS);
        LOGGER.info("futureDate: " + futureDate);
        // load more data into each precision so it has to merge existing
        // and roll up one new in each precision one default from above
        // moves into day
        loadData(futureDate, statNameCount, timesCount, statisticType);
        runAggregation(futureDate);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 3);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 2);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 3);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 2);

        assertThat(getAggregateTotal(COL_NAME_CNT))
                .isEqualTo(expectedCountTotal * 2);
        assertThat(getAggregateTotal(COL_NAME_VAL))
                .isEqualTo(expectedValueTotal * 2);

        LOGGER.info("run aggregation again but pretend we are 32days in the future");
        futureDate = startDate.plus(65, ChronoUnit.DAYS);
        LOGGER.info("futureDate: " + futureDate);
        // load more data into each precision so it has to merge existing
        // and roll up one new in each precision existing default moves to
        // hour existing hour moves to day
        loadData(futureDate, statNameCount, timesCount, statisticType);
        runAggregation(futureDate);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 7);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 7);

        assertThat(getAggregateTotal(COL_NAME_CNT))
                .isEqualTo(expectedCountTotal * 3);
        assertThat(getAggregateTotal(COL_NAME_VAL))
                .isEqualTo(expectedValueTotal * 3);

        LOGGER.info("run aggregation again with no new data so day data can roll up to month");
        LOGGER.info("futureDate: " + futureDate);
        runAggregation(futureDate);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_VAL, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedValueTotalByPrecision * 7);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DEFAULT_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.DAY_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 1);

        assertThat(getAggregateByPrecision(COL_NAME_CNT, SQLStatisticAggregationTransactionHelper.MONTH_PRECISION))
                .isEqualTo(expectedCountTotalByPrecision * 7);

        assertThat(getAggregateTotal(COL_NAME_CNT))
                .isEqualTo(expectedCountTotal * 3);
        assertThat(getAggregateTotal(COL_NAME_VAL))
                .isEqualTo(expectedValueTotal * 3);

        LOGGER.info("Test ran in {}", time);
    }
Test loads data into stat val src, with stats aged so that there are
equal numbers falling into each precision. It then does runs the
aggregation process and checks the right data is in stat_val. Following
this it does further iterations of loading more data and running
aggregation to verify the new data is being merged in and the old data is
rolled up. It uses a future 'current time' to replicate aggregation
running against stats that have been in stat_val for a while.
stroom-app/src/test/java/stroom/statistics/impl/sql/TestSQLStatisticAggregationManager.java