<commit_before># Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Tests for the control service REST API.
"""

import socket
from contextlib import closing
from json import loads

from twisted.trial.unittest import TestCase
from twisted.python.filepath import FilePath
from twisted.internet.defer import gatherResults

from treq import get, content

from eliot import Message

from ..testtools import (
    loop_until, random_name,
)
from .testtools import (
    MONGO_IMAGE, require_mongo, get_mongo_client,
    require_cluster, require_moving_backend, create_dataset,
    create_python_container,
)

CURRENT_DIRECTORY = FilePath(__file__).parent()


def verify_socket(host, port):
    """
    Wait until the destionation can be connected to.

    :param bytes host: Host to connect to.
    :param int port: Port to connect to.

    :return Deferred: Firing when connection is possible.
    """
    def can_connect():
        with closing(socket.socket()) as s:
            conn = s.connect_ex((host, port))
            Message.new(
                message_type="acceptance:verify_socket",
                host=host,
                port=port,
                result=conn,
            ).write()
            return conn == 0

    dl = loop_until(can_connect)
    return dl


class ContainerAPITests(TestCase):
    """
    Tests for the container API.
    """
    def _create_container(self, cluster):
        """
        Create a container listening on port 8080.

        :return: ``Deferred`` firing with a container dictionary once the
            container is up and running.
        """
        d = create_python_container(
            self, cluster, {
                u"ports": [{u"internal": 8080, u"external": 8080}],
                u"node_uuid": cluster.nodes[0].uuid,
            }, CURRENT_DIRECTORY.child(b"hellohttp.py"))

        def check_result(response):
            dl = verify_socket(cluster.nodes[0].public_address, 8080)
            dl.addCallback(lambda _: response)
            return dl

        d.addCallback(check_result)
        return d

    @require_cluster(1)
    def test_create_container_with_ports(self, cluster):
        """
        Create a container including port mappings on a single-node cluster.
        """
        return self._create_container(cluster)

    @require_cluster(1)
    def test_create_container_with_environment(self, cluster):
        """
        Create a container including environment variables on a single-node
        cluster.
        """
        environment = {u"XBLOO": u"YBLAH", u"ZBLOO": u"ZEBRA"}

        d = create_python_container(
            self, cluster, {
                u"ports": [{u"internal": 8080, u"external": 8080}],
                u"node_uuid": cluster.nodes[0].uuid,
                u"environment": environment,
            }, CURRENT_DIRECTORY.child(b"envhttp.py"))

        def checked(_):
            host = cluster.nodes[0].public_address
            d = self.query_http_server(host, 8080)
            d.addCallback(lambda data: dict(loads(data)))
            return d
        d.addCallback(checked)

        d.addCallback(
            lambda response:
                self.assertDictContainsSubset(environment, response)
        )
        return d

    @require_moving_backend
    @require_mongo
    @require_cluster(2)
    def test_move_container_with_dataset(self, cluster):
        """<commit_msg>Address review comment: Better docstring.<commit_after># Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Tests for the control service REST API.
"""

import socket
from contextlib import closing
from json import loads

from twisted.trial.unittest import TestCase
from twisted.python.filepath import FilePath
from twisted.internet.defer import gatherResults

from treq import get, content

from eliot import Message

from ..testtools import (
    loop_until, random_name,
)
from .testtools import (
    MONGO_IMAGE, require_mongo, get_mongo_client,
    require_cluster, require_moving_backend, create_dataset,
    create_python_container,
)

CURRENT_DIRECTORY = FilePath(__file__).parent()


def verify_socket(host, port):
    """
    Wait until the destionation can be connected to.

    :param bytes host: Host to connect to.
    :param int port: Port to connect to.

    :return Deferred: Firing when connection is possible.
    """
    def can_connect():
        with closing(socket.socket()) as s:
            conn = s.connect_ex((host, port))
            Message.new(
                message_type="acceptance:verify_socket",
                host=host,
                port=port,
                result=conn,
            ).write()
            return conn == 0

    dl = loop_until(can_connect)
    return dl


class ContainerAPITests(TestCase):
    """
    Tests for the container API.
    """
    def _create_container(self, cluster):
        """
        Create a container listening on port 8080.

        :return: ``Deferred`` firing with a container dictionary once the
            container is up and running.
        """
        d = create_python_container(
            self, cluster, {
                u"ports": [{u"internal": 8080, u"external": 8080}],
                u"node_uuid": cluster.nodes[0].uuid,
            }, CURRENT_DIRECTORY.child(b"hellohttp.py"))

        def check_result(response):
            dl = verify_socket(cluster.nodes[0].public_address, 8080)
            dl.addCallback(lambda _: response)
            return dl

        d.addCallback(check_result)
        return d

    @require_cluster(1)
    def test_create_container_with_ports(self, cluster):
        """
        Create a container including port mappings on a single-node cluster.
        """
        return self._create_container(cluster)

    @require_cluster(1)
    def test_create_container_with_environment(self, cluster):
        """
        If environment variables are specified when creating a container,
        those variables are available in the container's environment.
        """
        environment = {u"XBLOO": u"YBLAH", u"ZBLOO": u"ZEBRA"}

        d = create_python_container(
            self, cluster, {
                u"ports": [{u"internal": 8080, u"external": 8080}],
                u"node_uuid": cluster.nodes[0].uuid,
                u"environment": environment,
            }, CURRENT_DIRECTORY.child(b"envhttp.py"))

        def checked(_):
            host = cluster.nodes[0].public_address
            d = self.query_http_server(host, 8080)
            d.addCallback(lambda data: dict(loads(data)))
            return d
        d.addCallback(checked)

        d.addCallback(
            lambda response:
                self.assertDictContainsSubset(environment, response)
        )
        return d

    @require_moving_backend
    @require_mongo
    @require_cluster(2)
    def test_move_container_with_dataset(self, cluster):
        """
{'fraction_non_alphanumeric': 0.09188561474814923, 'fraction_numerical': 0.01045144433154304, 'mean_word_length': 2.367546432062561, 'pattern_counts': {'":': 22, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '495691', 'n_tokens_mistral': 1975, 'n_tokens_neox': 1861, 'n_words': 509}