/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.jmeter.visualizers.backend.graphite

import java.time.Instant

import org.apache.commons.pool2.impl.GenericKeyedObjectPool

import spock.lang.Specification

class PickleGraphiteMetricsSenderSpec extends Specification {

    def sut = new PickleGraphiteMetricsSender()

    def "new sender has no metrics"() {
        expect:
            sut.metrics.isEmpty()
    }

    def "adding metric to sender creates correct MetricTuple"() {
        given:
            def expectedName = "prefix-contextName.metricName"
            def expectedTS = 1000000
            def expectedVal = "value"

            sut.setup("host", 1024, "prefix-")
        when:
            sut.addMetric(expectedTS, "contextName", "metricName", expectedVal)
        then:
            def actualMetrics = sut.metrics
            actualMetrics.size() == 1
            def actualMetric = actualMetrics.get(0)
            actualMetric.name == expectedName
            actualMetric.timestamp == expectedTS
            actualMetric.value == expectedVal
    }

    def "writeAndSendMetrics does not attempt connection if there's nothing to send"() {
        given:
            sut.setup("non-existant-host", 1024, "prefix-")
        when:
            sut.writeAndSendMetrics()
        then:
            sut.metrics.isEmpty()
            noExceptionThrown()
    }

    def "writeAndSendMetrics connects and sends if there's something to send, dropping metrics on connection failure, without throwing exceptions"() {
        given:
            SocketConnectionInfos socketConnInfoMock = Mock()
            GenericKeyedObjectPool<SocketConnectionInfos, SocketOutputStream> objectPoolStub = Mock()
            sut.setup(socketConnInfoMock, objectPoolStub, "prefix-")
            sut.addMetric(1, "contextName", "metricName", "val")
        when:
            sut.writeAndSendMetrics()
        then:
            1 * objectPoolStub.borrowObject(socketConnInfoMock)
            sut.metrics.isEmpty()
            noExceptionThrown()
    }

    def "destroy closes outputStreamPool"() {
        given:
            GenericKeyedObjectPool<SocketConnectionInfos, SocketOutputStream> objectPoolStub = Mock()
            sut.setup(Mock(SocketConnectionInfos), objectPoolStub, "prefix-")
            sut.addMetric(1, "contextName", "metricName", "val")
        when:
            sut.destroy()
        then:
            1 * objectPoolStub.close()
            // TODO: should destroy also set metrics to null or are we relying on the original reference to be removed after destroy is called?
            sut.metrics.size() == 1
            noExceptionThrown()
    }

    def static newMetric(String name, long timestamp, String value) {
        return new GraphiteMetricsSender.MetricTuple(name, timestamp, value)
    }

    def "convertMetricsToPickleFormat produces expected result for one metric"() {
        given:
            def name = "name"
            def timeStamp = Instant.now().getEpochSecond()
            def value = "value-1.23"
            def metric = newMetric(name, timeStamp, value)
            def metrics = Collections.singletonList(metric)
        when:
            def result = sut.convertMetricsToPickleFormat(metrics)
        then:
            result == "(l(S'${name}'\n(L${timeStamp}L\nS'${value}'\ntta."
    }

    def "convertMetricsToPickleFormat produces expected result for multiple metrics"() {
        given:
            def name = "name"
            def timeStamp = Instant.now().getEpochSecond()
            def value = "value-1.23"
            def metric = newMetric(name, timeStamp, value)
            def metrics = Arrays.asList(metric, metric)
        when:
            def result = sut.convertMetricsToPickleFormat(metrics)
        then:
            result == "(l" +
                    "(S'${name}'\n(L${timeStamp}L\nS'${value}'\ntta" +
                    "(S'${name}'\n(L${timeStamp}L\nS'${value}'\ntta."
    }
}
