void testBucketWriteHelper()

in lib/api/unittest/CJsonOutputWriterTest.cc [42:506]


void testBucketWriteHelper(bool isInterim) {
    // groups output by bucket/detector

    std::ostringstream sstream;

    // The output writer won't close the JSON structures until is is destroyed
    {
        ml::core::CJsonOutputStreamWrapper outputStream(sstream);
        ml::api::CJsonOutputWriter writer("job", outputStream);

        std::string partitionFieldName("tfn");
        std::string partitionFieldValue("");
        std::string overFieldName("pfn");
        std::string overFieldValue("pfv");
        std::string byFieldName("airline");
        std::string byFieldValue("GAL");
        std::string correlatedByFieldValue("BAW");
        std::string fieldName("responsetime");
        std::string function("mean");
        std::string functionDescription("mean(responsetime)");
        std::string emptyString;
        ml::api::CHierarchicalResultsWriter::TOptionalStrOptionalStrPrDoublePrVec influences;

        {
            ml::api::CHierarchicalResultsWriter::SResults result11(
                false, false, partitionFieldName, partitionFieldValue,
                overFieldName, overFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 1, function, functionDescription,
                TDouble1Vec(1, 10090.0), TDouble1Vec(1, 6953.0), 2.24, 0.5, 0.0,
                79, fieldName, influences, false, false, 1, 100);

            ml::api::CHierarchicalResultsWriter::SResults result112(
                false, true, partitionFieldName, partitionFieldValue,
                overFieldName, overFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 1, function, functionDescription,
                TDouble1Vec(1, 10090.0), TDouble1Vec(1, 6953.0), 2.24, 0.5, 0.0,
                79, fieldName, influences, false, false, 1, 100);

            ml::api::CHierarchicalResultsWriter::SResults result12(
                ml::api::CHierarchicalResultsWriter::E_Result, partitionFieldName,
                partitionFieldValue, byFieldName, byFieldValue, correlatedByFieldValue,
                1, function, functionDescription, 42.0, 79, TDouble1Vec(1, 6953.0),
                TDouble1Vec(1, 10090.0), 2.24, 0.8, 0.0, -5.0, fieldName,
                influences, false, true, 2, 100, EMPTY_STRING_LIST, {});

            ml::api::CHierarchicalResultsWriter::SResults result13(
                ml::api::CHierarchicalResultsWriter::E_SimpleCountResult,
                partitionFieldName, partitionFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 1, function, functionDescription, 42.0, 79,
                TDouble1Vec(1, 6953.0), TDouble1Vec(1, 10090.0), 2.24, 0.5, 0.0, -5.0,
                fieldName, influences, false, false, 3, 100, EMPTY_STRING_LIST, {});

            ml::api::CHierarchicalResultsWriter::SResults result14(
                ml::api::CHierarchicalResultsWriter::E_Result, partitionFieldName,
                partitionFieldValue, byFieldName, byFieldValue, correlatedByFieldValue,
                1, function, functionDescription, 42.0, 79, TDouble1Vec(1, 6953.0),
                TDouble1Vec(1, 10090.0), 2.24, 0.0, 0.0, -5.0, fieldName,
                influences, false, false, 4, 100, EMPTY_STRING_LIST, {});

            // 1st bucket
            BOOST_TEST_REQUIRE(writer.acceptResult(result11));
            BOOST_TEST_REQUIRE(writer.acceptResult(result11));
            BOOST_TEST_REQUIRE(writer.acceptResult(result112));
            BOOST_TEST_REQUIRE(writer.acceptResult(result12));
            BOOST_TEST_REQUIRE(writer.acceptResult(result12));
            BOOST_TEST_REQUIRE(writer.acceptResult(result13));
            BOOST_TEST_REQUIRE(writer.acceptResult(result13));
            BOOST_TEST_REQUIRE(writer.acceptResult(result14));
            BOOST_TEST_REQUIRE(writer.acceptResult(result14));
            writer.acceptBucketTimeInfluencer(1, 0.01, 13.44, 70.0);
        }

        {
            ml::api::CHierarchicalResultsWriter::SResults result21(
                false, false, partitionFieldName, partitionFieldValue,
                overFieldName, overFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 2, function, functionDescription,
                TDouble1Vec(1, 10090.0), TDouble1Vec(1, 6953.0), 2.24, 0.6, 0.0,
                79, fieldName, influences, false, false, 1, 100);

            ml::api::CHierarchicalResultsWriter::SResults result212(
                false, true, partitionFieldName, partitionFieldValue,
                overFieldName, overFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 2, function, functionDescription,
                TDouble1Vec(1, 10090.0), TDouble1Vec(1, 6953.0), 2.24, 0.6, 0.0,
                79, fieldName, influences, false, false, 1, 100);

            ml::api::CHierarchicalResultsWriter::SResults result22(
                ml::api::CHierarchicalResultsWriter::E_Result, partitionFieldName,
                partitionFieldValue, byFieldName, byFieldValue, correlatedByFieldValue,
                2, function, functionDescription, 42.0, 79, TDouble1Vec(1, 6953.0),
                TDouble1Vec(1, 10090.0), 2.24, 0.8, 0.0, -5.0, fieldName,
                influences, false, true, 2, 100, EMPTY_STRING_LIST, {});

            ml::api::CHierarchicalResultsWriter::SResults result23(
                ml::api::CHierarchicalResultsWriter::E_SimpleCountResult,
                partitionFieldName, partitionFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 2, function, functionDescription, 42.0, 79,
                TDouble1Vec(1, 6953.0), TDouble1Vec(1, 10090.0), 2.24, 0.0, 0.0, -5.0,
                fieldName, influences, false, false, 3, 100, EMPTY_STRING_LIST, {});

            ml::api::CHierarchicalResultsWriter::SResults result24(
                ml::api::CHierarchicalResultsWriter::E_Result, partitionFieldName,
                partitionFieldValue, byFieldName, byFieldValue, correlatedByFieldValue,
                2, function, functionDescription, 42.0, 79, TDouble1Vec(1, 6953.0),
                TDouble1Vec(1, 10090.0), 2.24, 0.0, 0.0, -5.0, fieldName,
                influences, false, false, 4, 100, EMPTY_STRING_LIST, {});

            // 2nd bucket
            BOOST_TEST_REQUIRE(writer.acceptResult(result21));
            BOOST_TEST_REQUIRE(writer.acceptResult(result21));
            BOOST_TEST_REQUIRE(writer.acceptResult(result212));
            BOOST_TEST_REQUIRE(writer.acceptResult(result22));
            BOOST_TEST_REQUIRE(writer.acceptResult(result22));
            BOOST_TEST_REQUIRE(writer.acceptResult(result23));
            BOOST_TEST_REQUIRE(writer.acceptResult(result23));
            BOOST_TEST_REQUIRE(writer.acceptResult(result24));
            BOOST_TEST_REQUIRE(writer.acceptResult(result24));
            writer.acceptBucketTimeInfluencer(2, 0.01, 13.44, 70.0);
        }

        {
            ml::api::CHierarchicalResultsWriter::SResults result31(
                false, false, partitionFieldName, partitionFieldValue,
                overFieldName, overFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 3, function, functionDescription,
                TDouble1Vec(1, 10090.0), TDouble1Vec(1, 6953.0), 2.24, 0.8, 0.0,
                79, fieldName, influences, false, false, 1, 100);

            ml::api::CHierarchicalResultsWriter::SResults result312(
                false, true, partitionFieldName, partitionFieldValue,
                overFieldName, overFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 3, function, functionDescription,
                TDouble1Vec(1, 10090.0), TDouble1Vec(1, 6953.0), 2.24, 0.8, 0.0,
                79, fieldName, influences, false, false, 1, 100);

            ml::api::CHierarchicalResultsWriter::SResults result32(
                ml::api::CHierarchicalResultsWriter::E_Result, partitionFieldName,
                partitionFieldValue, byFieldName, byFieldValue, correlatedByFieldValue,
                3, function, functionDescription, 42.0, 79, TDouble1Vec(1, 6953.0),
                TDouble1Vec(1, 10090.0), 2.24, 0.0, 0.0, -5.0, fieldName,
                influences, false, true, 2, 100, EMPTY_STRING_LIST, {});

            ml::api::CHierarchicalResultsWriter::SResults result33(
                ml::api::CHierarchicalResultsWriter::E_SimpleCountResult,
                partitionFieldName, partitionFieldValue, byFieldName, byFieldValue,
                correlatedByFieldValue, 3, function, functionDescription, 42.0, 79,
                TDouble1Vec(1, 6953.0), TDouble1Vec(1, 10090.0), 2.24, 0.0, 0.0, -5.0,
                fieldName, influences, false, false, 3, 100, EMPTY_STRING_LIST, {});

            ml::api::CHierarchicalResultsWriter::SResults result34(
                ml::api::CHierarchicalResultsWriter::E_Result, partitionFieldName,
                partitionFieldValue, byFieldName, byFieldValue, correlatedByFieldValue,
                3, function, functionDescription, 42.0, 79, TDouble1Vec(1, 6953.0),
                TDouble1Vec(1, 10090.0), 2.24, 0.0, 0.0, -5.0, fieldName,
                influences, false, false, 4, 100, EMPTY_STRING_LIST, {});

            // 3rd bucket
            BOOST_TEST_REQUIRE(writer.acceptResult(result31));
            BOOST_TEST_REQUIRE(writer.acceptResult(result31));
            BOOST_TEST_REQUIRE(writer.acceptResult(result312));
            BOOST_TEST_REQUIRE(writer.acceptResult(result32));
            BOOST_TEST_REQUIRE(writer.acceptResult(result32));
            BOOST_TEST_REQUIRE(writer.acceptResult(result33));
            BOOST_TEST_REQUIRE(writer.acceptResult(result33));
            BOOST_TEST_REQUIRE(writer.acceptResult(result34));
            BOOST_TEST_REQUIRE(writer.acceptResult(result34));
            writer.acceptBucketTimeInfluencer(3, 0.01, 13.44, 70.0);
        }

        // Finished adding results
        BOOST_TEST_REQUIRE(writer.endOutputBatch(isInterim, 10U));
    }

    json::error_code ec;
    json::value arrayDoc = json::parse(sstream.str(), ec);
    BOOST_TEST_REQUIRE(ec.failed() == false);
    BOOST_TEST_REQUIRE(arrayDoc.is_array());

    LOG_DEBUG(<< "Results:\n" << arrayDoc);

    // There are 3 buckets and 3 record arrays in the order: r1, b1, r2, b2, r3, b3
    BOOST_REQUIRE_EQUAL(6, arrayDoc.as_array().size());

    int bucketTimes[] = {1000, 1000, 2000, 2000, 3000, 3000};

    // Assert buckets
    for (std::size_t i = 1; i < arrayDoc.as_array().size(); i = i + 2) {
        int buckettime = bucketTimes[i];
        const json::value& bucketWrapper_ = arrayDoc.as_array().at(i);
        const json::object& bucketWrapper = bucketWrapper_.as_object();
        BOOST_TEST_REQUIRE(bucketWrapper.contains("bucket"));

        const json::value& bucket_ = bucketWrapper.at("bucket");
        BOOST_TEST_REQUIRE(bucket_.is_object());
        const json::object& bucket = bucket_.as_object();
        BOOST_TEST_REQUIRE(bucket.contains("job_id"));
        BOOST_REQUIRE_EQUAL("job", bucket.at("job_id").as_string());

        // 3 detectors each have 2 records (simple count detector isn't added)
        // except the population detector which has a single record and clauses
        BOOST_REQUIRE_EQUAL(buckettime, bucket.at("timestamp").to_number<std::int64_t>());
        BOOST_TEST_REQUIRE(bucket.contains("bucket_influencers"));
        const json::value& bucketInfluencers_ = bucket.at("bucket_influencers");
        BOOST_TEST_REQUIRE(bucketInfluencers_.is_array());
        const json::array& bucketInfluencers = bucketInfluencers_.as_array();
        BOOST_REQUIRE_EQUAL(std::size_t(1), bucketInfluencers.size());
        const json::value& bucketInfluencer_ = bucketInfluencers[std::size_t(0)];
        const json::object& bucketInfluencer = bucketInfluencer_.as_object();
        BOOST_REQUIRE_CLOSE_ABSOLUTE(
            13.44, bucketInfluencer.at("raw_anomaly_score").to_number<double>(), 0.00001);
        BOOST_REQUIRE_CLOSE_ABSOLUTE(
            0.01, bucketInfluencer.at("probability").to_number<double>(), 0.00001);
        BOOST_REQUIRE_CLOSE_ABSOLUTE(
            70.0, bucketInfluencer.at("initial_anomaly_score").to_number<double>(), 0.00001);
        BOOST_TEST_REQUIRE(bucketInfluencer.contains("anomaly_score"));
        BOOST_REQUIRE_CLOSE_ABSOLUTE(
            70.0, bucketInfluencer.at("anomaly_score").to_number<double>(), 0.00001);
        BOOST_REQUIRE_EQUAL("bucket_time",
                            bucketInfluencer.at("influencer_field_name").as_string());

        BOOST_REQUIRE_EQUAL(79, bucket.at("event_count").to_number<std::int64_t>());
        BOOST_TEST_REQUIRE(bucket.contains("anomaly_score"));
        BOOST_REQUIRE_CLOSE_ABSOLUTE(
            70.0, bucket.at("anomaly_score").to_number<double>(), 0.00001);
        BOOST_TEST_REQUIRE(bucket.contains("initial_anomaly_score"));
        BOOST_REQUIRE_CLOSE_ABSOLUTE(
            70.0, bucket.at("initial_anomaly_score").to_number<double>(), 0.00001);
        if (isInterim) {
            BOOST_TEST_REQUIRE(bucket.contains("is_interim"));
            BOOST_REQUIRE_EQUAL(isInterim, bucket.at("is_interim").as_bool());
        } else {
            BOOST_TEST_REQUIRE(!bucket.contains("is_interim"));
        }

        BOOST_REQUIRE_EQUAL(std::uint64_t(10ll),
                            bucket.at("processing_time_ms").to_number<std::uint64_t>());
    }

    for (std::size_t i = 0; i < arrayDoc.as_array().size(); i = i + 2) {
        int buckettime = bucketTimes[i];

        const json::value& recordsWrapper_ = arrayDoc.as_array().at(i);
        const json::object& recordsWrapper = recordsWrapper_.as_object();

        BOOST_TEST_REQUIRE(recordsWrapper.contains("records"));

        const json::value& records_ = recordsWrapper.at("records");
        BOOST_TEST_REQUIRE(records_.is_array());
        const json::array& records = records_.as_array();

        BOOST_REQUIRE_EQUAL(std::size_t(5), records.size());

        // 1st record is for population detector
        {
            const json::value& record_ = records[std::size_t(0)];
            const json::object& record = record_.as_object();
            BOOST_TEST_REQUIRE(record.contains("job_id"));

            BOOST_REQUIRE_EQUAL("job", record.at("job_id").as_string());
            BOOST_TEST_REQUIRE(record.contains("detector_index"));
            BOOST_REQUIRE_EQUAL(1, record.at("detector_index").to_number<std::int64_t>());
            BOOST_TEST_REQUIRE(record.contains("timestamp"));
            BOOST_REQUIRE_EQUAL(buckettime,
                                record.at("timestamp").to_number<std::int64_t>());
            BOOST_TEST_REQUIRE(record.contains("probability"));
            BOOST_REQUIRE_EQUAL(0.0, record.at("probability").to_number<double>());
            BOOST_TEST_REQUIRE(record.contains("by_field_name"));
            BOOST_REQUIRE_EQUAL("airline", record.at("by_field_name").as_string());
            BOOST_TEST_REQUIRE(!record.contains("by_field_value"));
            BOOST_TEST_REQUIRE(!record.contains("correlated_by_field_value"));
            BOOST_TEST_REQUIRE(record.contains("function"));
            BOOST_REQUIRE_EQUAL("mean", record.at("function").as_string());
            BOOST_TEST_REQUIRE(record.contains("function_description"));
            BOOST_REQUIRE_EQUAL("mean(responsetime)",
                                record.at("function_description").as_string());
            BOOST_TEST_REQUIRE(record.contains("over_field_name"));
            BOOST_REQUIRE_EQUAL("pfn", record.at("over_field_name").as_string());
            BOOST_TEST_REQUIRE(record.contains("over_field_value"));
            BOOST_REQUIRE_EQUAL("pfv", record.at("over_field_value").as_string());
            BOOST_TEST_REQUIRE(record.contains("bucket_span"));
            BOOST_REQUIRE_EQUAL(100, record.at("bucket_span").to_number<std::int64_t>());
            // It's hard to predict what these will be, so just assert their
            // presence
            BOOST_TEST_REQUIRE(record.contains("initial_record_score"));
            BOOST_TEST_REQUIRE(record.contains("record_score"));
            if (isInterim) {
                BOOST_TEST_REQUIRE(record.contains("is_interim"));
                BOOST_REQUIRE_EQUAL(isInterim, record.at("is_interim").as_bool());
            } else {
                BOOST_TEST_REQUIRE(!record.contains("is_interim"));
            }

            BOOST_TEST_REQUIRE(record.contains("causes"));
            const json::value& causes_ = record.at("causes");
            BOOST_TEST_REQUIRE(causes_.is_array());
            const json::array& causes = causes_.as_array();

            BOOST_REQUIRE_EQUAL(std::size_t(2), causes.size());
            for (std::size_t k = 0; k < causes.size(); k++) {
                const json::value& cause_ = causes[k];
                const json::object& cause = cause_.as_object();
                BOOST_TEST_REQUIRE(cause.contains("probability"));
                BOOST_REQUIRE_EQUAL(0.0, cause.at("probability").to_number<double>());
                BOOST_TEST_REQUIRE(cause.contains("field_name"));
                BOOST_REQUIRE_EQUAL("responsetime", cause.at("field_name").as_string());
                BOOST_TEST_REQUIRE(cause.contains("by_field_name"));
                BOOST_REQUIRE_EQUAL("airline", cause.at("by_field_name").as_string());
                BOOST_TEST_REQUIRE(cause.contains("by_field_value"));
                BOOST_REQUIRE_EQUAL("GAL", cause.at("by_field_value").as_string());
                BOOST_TEST_REQUIRE(cause.contains("correlated_by_field_value"));
                BOOST_REQUIRE_EQUAL("BAW", cause.at("correlated_by_field_value").as_string());
                BOOST_TEST_REQUIRE(cause.contains("partition_field_name"));
                BOOST_REQUIRE_EQUAL("tfn", cause.at("partition_field_name").as_string());
                BOOST_TEST_REQUIRE(cause.contains("partition_field_value"));
                BOOST_REQUIRE_EQUAL("", cause.at("partition_field_value").as_string());
                BOOST_TEST_REQUIRE(cause.contains("function"));
                BOOST_REQUIRE_EQUAL("mean", cause.at("function").as_string());
                BOOST_TEST_REQUIRE(cause.contains("function_description"));
                BOOST_REQUIRE_EQUAL("mean(responsetime)",
                                    cause.at("function_description").as_string());
                BOOST_TEST_REQUIRE(cause.contains("typical"));
                BOOST_TEST_REQUIRE(cause.at("typical").is_array());
                BOOST_REQUIRE_EQUAL(std::size_t(1),
                                    cause.at("typical").as_array().size());
                BOOST_REQUIRE_EQUAL(
                    6953.0,
                    cause.at("typical").as_array().at(std::size_t(0)).to_number<double>());
                BOOST_TEST_REQUIRE(cause.contains("actual"));
                BOOST_TEST_REQUIRE(cause.at("actual").is_array());
                BOOST_REQUIRE_EQUAL(std::size_t(1),
                                    cause.at("actual").as_array().size());
                BOOST_REQUIRE_EQUAL(
                    10090.0,
                    cause.at("actual").as_array().at(std::size_t(0)).to_number<double>());
                BOOST_TEST_REQUIRE(cause.contains("function"));
            }
        }

        // Next 2 records are for metric detector
        {
            for (std::size_t k = 1; k < 3; k++) {
                const json::value& record_ = records[k];
                const json::object& record = record_.as_object();

                BOOST_TEST_REQUIRE(record.contains("job_id"));
                BOOST_REQUIRE_EQUAL("job", record.at("job_id").as_string());
                BOOST_TEST_REQUIRE(record.contains("detector_index"));
                BOOST_REQUIRE_EQUAL(
                    2, record.at("detector_index").to_number<std::int64_t>());
                BOOST_TEST_REQUIRE(record.contains("timestamp"));
                BOOST_REQUIRE_EQUAL(
                    buckettime, record.at("timestamp").to_number<std::int64_t>());
                BOOST_TEST_REQUIRE(record.contains("probability"));
                BOOST_REQUIRE_EQUAL(0.0, record.at("probability").to_number<double>());
                BOOST_TEST_REQUIRE(record.contains("by_field_name"));
                BOOST_REQUIRE_EQUAL("airline", record.at("by_field_name").as_string());
                BOOST_TEST_REQUIRE(record.contains("by_field_value"));
                BOOST_REQUIRE_EQUAL("GAL", record.at("by_field_value").as_string());
                BOOST_TEST_REQUIRE(record.contains("correlated_by_field_value"));
                BOOST_REQUIRE_EQUAL(
                    "BAW", record.at("correlated_by_field_value").as_string());
                BOOST_TEST_REQUIRE(record.contains("typical"));
                BOOST_TEST_REQUIRE(record.at("typical").is_array());
                BOOST_REQUIRE_EQUAL(std::size_t(1),
                                    record.at("typical").as_array().size());
                BOOST_REQUIRE_EQUAL(
                    6953.0,
                    record.at("typical").as_array().at(std::size_t(0)).to_number<double>());
                BOOST_TEST_REQUIRE(record.contains("actual"));
                BOOST_TEST_REQUIRE(record.at("actual").is_array());
                BOOST_REQUIRE_EQUAL(std::size_t(1),
                                    record.at("actual").as_array().size());
                BOOST_REQUIRE_EQUAL(
                    10090.0,
                    record.at("actual").as_array().at(std::size_t(0)).to_number<double>());
                BOOST_TEST_REQUIRE(record.contains("field_name"));
                BOOST_REQUIRE_EQUAL("responsetime", record.at("field_name").as_string());
                BOOST_TEST_REQUIRE(record.contains("function"));
                BOOST_REQUIRE_EQUAL("mean", record.at("function").as_string());
                BOOST_TEST_REQUIRE(record.contains("function_description"));
                BOOST_REQUIRE_EQUAL("mean(responsetime)",
                                    record.at("function_description").as_string());
                BOOST_TEST_REQUIRE(record.contains("partition_field_name"));
                BOOST_REQUIRE_EQUAL("tfn", record.at("partition_field_name").as_string());
                BOOST_TEST_REQUIRE(record.contains("partition_field_value"));
                BOOST_REQUIRE_EQUAL("", record.at("partition_field_value").as_string());
                BOOST_TEST_REQUIRE(record.contains("bucket_span"));
                BOOST_REQUIRE_EQUAL(100, record.at("bucket_span").to_number<std::int64_t>());
                // It's hard to predict what these will be, so just assert their
                // presence
                BOOST_TEST_REQUIRE(record.contains("initial_record_score"));
                BOOST_TEST_REQUIRE(record.contains("record_score"));
                if (isInterim) {
                    BOOST_TEST_REQUIRE(record.contains("is_interim"));
                    BOOST_REQUIRE_EQUAL(isInterim, record.at("is_interim").as_bool());
                } else {
                    BOOST_TEST_REQUIRE(!record.contains("is_interim"));
                }
            }
        }

        // Last 2 records are for event rate detector
        {
            for (std::size_t k = 3; k < 5; k++) {
                const json::value& record_ = records[k];
                const json::object& record = record_.as_object();

                BOOST_TEST_REQUIRE(record.contains("job_id"));
                BOOST_REQUIRE_EQUAL("job", record.at("job_id").as_string());
                BOOST_TEST_REQUIRE(record.contains("detector_index"));
                BOOST_REQUIRE_EQUAL(
                    4, record.at("detector_index").to_number<std::int64_t>());
                BOOST_TEST_REQUIRE(record.contains("timestamp"));
                BOOST_REQUIRE_EQUAL(
                    buckettime, record.at("timestamp").to_number<std::int64_t>());
                BOOST_TEST_REQUIRE(record.contains("probability"));
                BOOST_REQUIRE_EQUAL(0.0, record.at("probability").to_number<double>());
                BOOST_TEST_REQUIRE(record.contains("by_field_name"));
                BOOST_REQUIRE_EQUAL("airline", record.at("by_field_name").as_string());
                BOOST_TEST_REQUIRE(record.contains("by_field_value"));
                BOOST_REQUIRE_EQUAL("GAL", record.at("by_field_value").as_string());
                BOOST_TEST_REQUIRE(record.contains("correlated_by_field_value"));
                BOOST_REQUIRE_EQUAL(
                    "BAW", record.at("correlated_by_field_value").as_string());
                BOOST_TEST_REQUIRE(record.contains("typical"));
                BOOST_TEST_REQUIRE(record.at("typical").is_array());
                BOOST_REQUIRE_EQUAL(std::size_t(1),
                                    record.at("typical").as_array().size());
                BOOST_REQUIRE_EQUAL(
                    6953.0,
                    record.at("typical").as_array().at(std::size_t(0)).to_number<double>());
                BOOST_TEST_REQUIRE(record.contains("actual"));
                BOOST_TEST_REQUIRE(record.at("actual").is_array());
                BOOST_REQUIRE_EQUAL(std::size_t(1),
                                    record.at("actual").as_array().size());
                BOOST_REQUIRE_EQUAL(
                    10090.0,
                    record.at("actual").as_array().at(std::size_t(0)).to_number<double>());
                BOOST_TEST_REQUIRE(record.contains("function"));
                // This would be count in the real case with properly generated input data
                BOOST_REQUIRE_EQUAL("mean", record.at("function").as_string());
                BOOST_TEST_REQUIRE(record.contains("function_description"));
                BOOST_REQUIRE_EQUAL("mean(responsetime)",
                                    record.at("function_description").as_string());
                BOOST_TEST_REQUIRE(record.contains("partition_field_name"));
                BOOST_REQUIRE_EQUAL("tfn", record.at("partition_field_name").as_string());
                BOOST_TEST_REQUIRE(record.contains("partition_field_value"));
                BOOST_REQUIRE_EQUAL("", record.at("partition_field_value").as_string());
                BOOST_TEST_REQUIRE(record.contains("bucket_span"));
                BOOST_REQUIRE_EQUAL(100, record.at("bucket_span").to_number<std::int64_t>());
                // It's hard to predict what these will be, so just assert their
                // presence
                BOOST_TEST_REQUIRE(record.contains("initial_record_score"));
                BOOST_TEST_REQUIRE(record.contains("record_score"));
                if (isInterim) {
                    BOOST_TEST_REQUIRE(record.contains("is_interim"));
                    BOOST_REQUIRE_EQUAL(isInterim, record.at("is_interim").as_bool());
                } else {
                    BOOST_TEST_REQUIRE(!record.contains("is_interim"));
                }
            }
        }
    }
}