<commit_before># Copyright 2011 the Melange authors.
#
# Licensed 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.

"""Tests for accept_proposals task."""

import httplib
import urllib

from soc.modules.gsoc.models import project as project_model
from soc.modules.gsoc.models import proposal as proposal_model
from soc.modules.gsoc.models import organization as org_model

from soc.modules.seeder.logic.seeder import logic as seeder_logic

from tests import profile_utils
from tests import program_utils
from tests import test_utils

ACCEPT_URL = '/tasks/gsoc/accept_proposals/accept'
REJECT_URL = '/tasks/gsoc/accept_proposals/reject'
MAIN_URL = '/tasks/gsoc/accept_proposals/main'

class AcceptProposalsTest(
    test_utils.GSoCDjangoTestCase, test_utils.TaskQueueTestCase):
  """Tests for accept proposals task."""

  def setUp(self):
    super(AcceptProposalsTest, self).setUp()
    self.init()
    self._createHost()
    self._createMentor()
    self._acceptProposals()

  def _createHost(self):
    """Sets program host."""
    self.host = self.profile_helper
    self.host.createHost()
    self.host.createProfile()

  def _createMentor(self):
    """Creates a mentor for default organization."""
    self.mentor = profile_utils.seedGSoCProfile(
        self.program, mentor_for=[self.org.key()])

  def _createStudent(self, email, n_proposals):
    """Creates a student with proposals."""
    student = profile_utils.GSoCProfileHelper(self.gsoc, self.dev_test)
    student.createOtherUser(email)
    student.createStudentWithProposals(
        self.org, self.mentor, n=n_proposals)
    student.notificationSettings()
    return student

  def _acceptProposals(self):
    """Set student proposals' acceptance state and make sure the organization
    has slots available.
    """
    self.student1 = self._createStudent(
        'student1@example.com', n_proposals=2)
    self.student1_proposals = proposal_model.GSoCProposal.all().ancestor(
        self.student1.profile)
    self.student2 = self._createStudent(
        'student2@example.com', n_proposals=3)
    self.student2_proposals = proposal_model.GSoCProposal.all().ancestor(
        self.student2.profile)

    self.assertEqual(self.student1_proposals.count(), 2)
    self.assertEqual(self.student2_proposals.count(), 3)

    # accept 1 of 2 proposal of student1
    proposal1 = self.student1_proposals[0]
    proposal1.accept_as_project = True
    proposal1.put()

    proposal2 = self.student1_proposals[1]
    proposal2.accept_as_project = False
    proposal2.put()

    # reject all proposals of student2
    for proposal in self.student2_proposals:
      proposal.accept_as_project = False
      proposal.put()

    self.org.slots = 5
    self.org.put()

    self.timeline_helper.studentsAnnounced()

  def testConvertProposals(self):
    """Tests convert proposal task runs successfully."""
    post_data = {'program_key': self.gsoc.key().name()}
    response = self.post(MAIN_URL, post_data)
    self.assertEqual(response.status_code, httplib.OK)

    # assert accept task started for first org
    self.assertTasksInQueue(n=1, url=ACCEPT_URL)

    # assert main task started for next org
    self.assertTasksInQueue(n=1, url=MAIN_URL)

    # assert parameters to tasks
    for task in self.get_tasks():
      if task['url'] == ACCEPT_URL:
        expected_params = {
            'org_key': urllib.quote_plus(self.org.key().id_or_name())
            }
        self.assertEqual(expected_params, task['params'])

      elif task['url'] == MAIN_URL:
        q = org_model.GSoCOrganization.all()
        q.filter('scope', self.gsoc)
        q.filter('status', 'active')
        q.get()
        expected_params = {
            'org_cursor': q.cursor(),
            'program_key': urllib.quote_plus(self.gsoc.key().name())
            }

        # as we can't know XSRF token, ignore it
        self.assertNotEqual(task['params'].get('xsrf_token'), None)
        task_params = task['params'].copy()
        del task_params['xsrf_token']

        self.assertEqual(expected_params, task_params)

  def testAcceptProposals(self):
    """Tests accept task for an organization."""
    program_utils.seedGSoCProgramMessages(program_key=self.gsoc.key())

    # assert current status of proposal to be accepted
    self.assertEqual(self.student1_proposals[0].status, 'pending')

    post_data = {'org_key': self.org.key().name(),
                 'program_key': self.gsoc.key().name()}
    response = self.post(ACCEPT_URL, post_data)

    # assert accepted student got proper emails
    self.assertEqual(response.status_code, httplib.OK)
    self.assertEmailSent(
        to=self.student1.profile.email, subject='Congratulations!')
    self.assertEmailSent(
        to=self.student1.profile.email,
        subject='Welcome to %s' % self.gsoc.name)
    # TODO(daniel): add assertEmailNotSent to DjangoTestCase
    # self.assertEmailNotSent(to=self.student2.profile.email)

    # assert post status of proposal to be accepted
    self.assertEqual(
        self.student1_proposals[0].status, proposal_model.STATUS_ACCEPTED)

    # assert a project created and associated with accepted student
    projects = project_model.GSoCProject.all().ancestor(self.student1.profile)
    self.assertEqual(projects.count(), 1)
    project = projects.get()
    self.assertEqual(project.status, project_model.STATUS_ACCEPTED)

    # assert reject task is queued
    self.assertTasksInQueue(n=1, url=REJECT_URL)

    # assert parameters to task
    for task in self.get_tasks():
      if task['url'] == REJECT_URL:
        expected_params = {
            'org_key': urllib.quote_plus(self.org.key().name()),
            'program_key': urllib.quote_plus(self.gsoc.key().name())
            }

        # ignore xsrf token
        self.assertNotEqual(task['params'].get('xsrf_token'), None)
        task_params = task['params'].copy()
        del task_params['xsrf_token']

        self.assertEqual(expected_params, task_params)

    # test reject proposals
    post_data = {'org_key': self.org.key().name(),
                 'program_key': self.gsoc.key().name()}
    response = self.post(REJECT_URL, post_data)
    self.assertEqual(response.status_code, httplib.OK)

    # assert post status of proposals
    self.assertEqual(
        self.student1_proposals[0].status, proposal_model.STATUS_ACCEPTED)
    self.assertEqual(
        self.student1_proposals[1].status, proposal_model.STATUS_REJECTED)
    self.assertEqual(
        self.student2_proposals[0].status, proposal_model.STATUS_REJECTED)
    self.assertEqual(
        self.student2_proposals[1].status, proposal_model.STATUS_REJECTED)
    self.assertEqual(
        self.student2_proposals[2].status, proposal_model.STATUS_REJECTED)

    # assert student2 got a reject email
    self.assertEmailSent(
        to=self.student2.profile.email,
        subject='Thank you for applying to %s' % self.gsoc.name)
    # assert student2 got no accept email
    # TODO(daniel): add assertEmailNotSent to DjangoTestCase
    #self.assertEmailNotSent(to=self.student2.profile.email,
    #    subject='Congratulations!')
    # assert student1 got a reject email (already got an accept mail)
    self.assertEmailSent(
        to=self.student1.profile.email,
        subject='Thank you for applying to %s' % self.gsoc.name)

    # assert no projects are created for rejected student
    projects = project_model.GSoCProject.all().ancestor(self.student2.profile)
    self.assertEqual(projects.count(), 0)

class ConvertProposalsTest(
    test_utils.GSoCDjangoTestCase, test_utils.TaskQueueTestCase):
  """Unit tests for convertProposals function."""

  def setUp(self):
    super(ConvertProposalsTest, self).setUp()
    self.init()

    # seed a new program
    self.program = program_utils.seedGSoCProgram()

    # seed a few organizations
    org_properties = {
        'program': self.program,
        'status': 'active',
        }
    self.org_keys = []
    for _ in range(5):
      self.org_keys.append(seeder_logic.seed(
          org_model.GSoCOrganization, org_properties).key())

    # create post data that will be sent to tasks
    self.post_data = {
        'program_key': self.program.key().name()
        }

  def testNoProgramKey(self):
    # program_key is missing in POST params
    response = self.post(MAIN_URL, {})

    # assert no task started
    self.assertTasksInQueue(n=0, url=ACCEPT_URL)

  def testTaskExecutedForAllOrgs(self):
    # this set will store key names of organizations for which accept
    # proposals task has been enqueued
    org_key_names = set()

    post_data = self.post_data
    while post_data is not None:
      response = self.post(MAIN_URL, post_data)

      # assert task completed with OK status
      self.assertEqual(response.status_code, httplib.OK)

      # try getting a convert task for the next organization
      convert_tasks = self.get_tasks(url=MAIN_URL)

      if convert_tasks:
        # assert that exactly one task was enqueued
        self.assertEqual(len(convert_tasks), 1)

        # this task should be executed next so take its POST data
        post_data = convert_tasks[0]['params'].copy()

        # this is necessary, as get_tasks returns quoted strings
        # the program_key has to be "fixed" here
        post_data.update({
            'program_key': urllib.unquote(post_data['program_key'])
            })

        # assert there is an accept proposals task another organization
        self.assertTasksInQueue(n=1, url=ACCEPT_URL)

        # get an accept proposals task for the organization
        accept_tasks = self.get_tasks(url=ACCEPT_URL)
        params = accept_tasks[0]['params']

        # org_key must be in its params
        self.assertIn('org_key', params)

        # add key to set of enqueued key names
        org_key_names.add(urllib.unquote(params['org_key']))

      else:
        post_data = None

      # remove all the enqueued tasks
      self.clear_task_queue()

    # there should be key name for every organization in the set
    self.assertEqual(len(org_key_names), 5)

    # there should be an entry for every org
    expected_org_key_names = set([org_key.name() for org_key in self.org_keys])
    self.assertEqual(expected_org_key_names, org_key_names)
<commit_msg>All functions in test_accept_proposals module are based on NDB organizations.<commit_after># Copyright 2011 the Melange authors.
#
# Licensed 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.

"""Tests for accept_proposals task."""

import httplib
import urllib

from google.appengine.ext import ndb

from melange.models import organization as org_model

from soc.modules.gsoc.models import project as project_model
from soc.modules.gsoc.models import proposal as proposal_model

from soc.modules.seeder.logic.seeder import logic as seeder_logic

from summerofcode.models import organization as soc_org_model

from tests import org_utils
from tests import profile_utils
from tests import program_utils
from tests import test_utils

ACCEPT_URL = '/tasks/gsoc/accept_proposals/accept'
REJECT_URL = '/tasks/gsoc/accept_proposals/reject'
MAIN_URL = '/tasks/gsoc/accept_proposals/main'

TEST_SLOT_ALLOCATION = 5

class AcceptProposalsTest(
    test_utils.GSoCDjangoTestCase, test_utils.TaskQueueTestCase):
  """Tests for accept proposals task."""

  def setUp(self):
    super(AcceptProposalsTest, self).setUp()
    self.init()

    # the organization should already be accepted and have slots allocated
    self.org.status = org_model.Status.ACCEPTED
    self.org.slot_allocation = TEST_SLOT_ALLOCATION
    self.org.put()

    self._createHost()
    self._createMentor()
    self._acceptProposals()

  def _createHost(self):
    """Sets program host."""
    self.host = self.profile_helper
    self.host.createHost()
    self.host.createProfile()

  def _createMentor(self):
    """Creates a mentor for default organization."""
    self.mentor = profile_utils.seedGSoCProfile(
        self.program, mentor_for=[self.org.key.to_old_key()])

  def _createStudent(self, email, n_proposals):
    """Creates a student with proposals."""
    student = profile_utils.GSoCProfileHelper(self.gsoc, self.dev_test)
    student.createOtherUser(email)
    student.createStudentWithProposals(
        self.org, self.mentor, n=n_proposals)
    student.notificationSettings()
    return student

  def _acceptProposals(self):
    """Set student proposals' acceptance state and make sure the organization
    has slots available.
    """
    self.student1 = self._createStudent(
        'student1@example.com', n_proposals=2)
    self.student1_proposals = proposal_model.GSoCProposal.all().ancestor(
        self.student1.profile)
    self.student2 = self._createStudent(
        'student2@example.com', n_proposals=3)
    self.student2_proposals = proposal_model.GSoCProposal.all().ancestor(
        self.student2.profile)

    self.assertEqual(self.student1_proposals.count(), 2)
    self.assertEqual(self.student2_proposals.count(), 3)

    # accept 1 of 2 proposal of student1
    proposal1 = self.student1_proposals[0]
    proposal1.accept_as_project = True
    proposal1.put()

    proposal2 = self.student1_proposals[1]
    proposal2.accept_as_project = False
    proposal2.put()

    # reject all proposals of student2
    for proposal in self.student2_proposals:
      proposal.accept_as_project = False
      proposal.put()

    self.timeline_helper.studentsAnnounced()

  def testConvertProposals(self):
    """Tests convert proposal task runs successfully."""
    post_data = {'program_key': self.gsoc.key().name()}
    response = self.post(MAIN_URL, post_data)
    self.assertEqual(response.status_code, httplib.OK)

    # assert accept task started for first org
    self.assertTasksInQueue(n=1, url=ACCEPT_URL)

    # assert main task started for next org
    self.assertTasksInQueue(n=1, url=MAIN_URL)

    # assert parameters to tasks
    for task in self.get_tasks():
      if task['url'] == ACCEPT_URL:
        expected_params = {
            'org_key': urllib.quote_plus(self.org.key.id())
            }
        self.assertEqual(expected_params, task['params'])

      elif task['url'] == MAIN_URL:
        query = soc_org_model.SOCOrganization.query(
            soc_org_model.SOCOrganization.program ==
                ndb.Key.from_old_key(self.program.key()),
            soc_org_model.SOCOrganization.status == org_model.Status.ACCEPTED)
        _, next_cursor, _ = query.fetch_page(1)

        expected_params = {
            'org_cursor': urllib.quote_plus(next_cursor.urlsafe()),
            'program_key': urllib.quote_plus(self.gsoc.key().name())
            }

        # as we can't know XSRF token, ignore it
        self.assertNotEqual(task['params'].get('xsrf_token'), None)
        task_params = task['params'].copy()
        del task_params['xsrf_token']

        self.assertEqual(expected_params, task_params)

  def testAcceptProposals(self):
    """Tests accept task for an organization."""
    program_utils.seedGSoCProgramMessages(program_key=self.gsoc.key())

    # assert current status of proposal to be accepted
    self.assertEqual(self.student1_proposals[0].status, 'pending')

    post_data = {'org_key': self.org.key.id(),
                 'program_key': self.gsoc.key().name()}
    response = self.post(ACCEPT_URL, post_data)

    # assert accepted student got proper emails
    self.assertEqual(response.status_code, httplib.OK)
    self.assertEmailSent(
        to=self.student1.profile.email, subject='Congratulations!')
    self.assertEmailSent(
        to=self.student1.profile.email,
        subject='Welcome to %s' % self.gsoc.name)
    # TODO(daniel): add assertEmailNotSent to DjangoTestCase
    # self.assertEmailNotSent(to=self.student2.profile.email)

    # assert post status of proposal to be accepted
    self.assertEqual(
        self.student1_proposals[0].status, proposal_model.STATUS_ACCEPTED)

    # assert a project created and associated with accepted student
    projects = project_model.GSoCProject.all().ancestor(self.student1.profile)
    self.assertEqual(projects.count(), 1)
    project = projects.get()
    self.assertEqual(project.status, project_model.STATUS_ACCEPTED)

    # assert reject task is queued
    self.assertTasksInQueue(n=1, url=REJECT_URL)

    # assert parameters to task
    for task in self.get_tasks():
      if task['url'] == REJECT_URL:
        expected_params = {
            'org_key': urllib.quote_plus(self.org.key.id()),
            'program_key': urllib.quote_plus(self.gsoc.key().name())
            }

        # ignore xsrf token
        self.assertNotEqual(task['params'].get('xsrf_token'), None)
        task_params = task['params'].copy()
        del task_params['xsrf_token']

        self.assertEqual(expected_params, task_params)

    # test reject proposals
    post_data = {'org_key': self.org.key.id(),
                 'program_key': self.gsoc.key().name()}
    response = self.post(REJECT_URL, post_data)
    self.assertEqual(response.status_code, httplib.OK)

    # assert post status of proposals
    self.assertEqual(
        self.student1_proposals[0].status, proposal_model.STATUS_ACCEPTED)
    self.assertEqual(
        self.student1_proposals[1].status, proposal_model.STATUS_REJECTED)
    self.assertEqual(
        self.student2_proposals[0].status, proposal_model.STATUS_REJECTED)
    self.assertEqual(
        self.student2_proposals[1].status, proposal_model.STATUS_REJECTED)
    self.assertEqual(
        self.student2_proposals[2].status, proposal_model.STATUS_REJECTED)

    # assert student2 got a reject email
    self.assertEmailSent(
        to=self.student2.profile.email,
        subject='Thank you for applying to %s' % self.gsoc.name)
    # assert student2 got no accept email
    # TODO(daniel): add assertEmailNotSent to DjangoTestCase
    #self.assertEmailNotSent(to=self.student2.profile.email,
    #    subject='Congratulations!')
    # assert student1 got a reject email (already got an accept mail)
    self.assertEmailSent(
        to=self.student1.profile.email,
        subject='Thank you for applying to %s' % self.gsoc.name)

    # assert no projects are created for rejected student
    projects = project_model.GSoCProject.all().ancestor(self.student2.profile)
    self.assertEqual(projects.count(), 0)

TEST_NUMBER_OF_ORGS = 5

class ConvertProposalsTest(
    test_utils.GSoCDjangoTestCase, test_utils.TaskQueueTestCase):
  """Unit tests for convertProposals function."""

  def setUp(self):
    super(ConvertProposalsTest, self).setUp()
    self.init()

    # seed a new program
    self.program = program_utils.seedGSoCProgram()

    # seed a few organizations
    self.org_keys = [
        org_utils.seedSOCOrganization(
            self.program.key(), status=org_model.Status.ACCEPTED).key
        for _ in range(TEST_NUMBER_OF_ORGS)]

    # create post data that will be sent to tasks
    self.post_data = {
        'program_key': self.program.key().name()
        }

  def testNoProgramKey(self):
    # program_key is missing in POST params
    response = self.post(MAIN_URL, {})

    # assert no task started
    self.assertTasksInQueue(n=0, url=ACCEPT_URL)

  def testTaskExecutedForAllOrgs(self):
    # this set will store key names of organizations for which accept
    # proposals task has been enqueued
    org_key_names = set()

    post_data = self.post_data
    while post_data is not None:
      response = self.post(MAIN_URL, post_data)

      # assert task completed with OK status
      self.assertEqual(response.status_code, httplib.OK)

      # try getting a convert task for the next organization
      convert_tasks = self.get_tasks(url=MAIN_URL)

      if convert_tasks:
        # assert that exactly one task was enqueued
        self.assertEqual(len(convert_tasks), 1)

        # this task should be executed next so take its POST data
        post_data = convert_tasks[0]['params'].copy()

        # this is necessary, as get_tasks returns quoted strings
        # the program_key has to be "fixed" here
        post_data.update({
            'program_key': urllib.unquote(post_data['program_key'])
            })

        # assert there is an accept proposals task another organization
        self.assertTasksInQueue(n=1, url=ACCEPT_URL)

        # get an accept proposals task for the organization
        accept_tasks = self.get_tasks(url=ACCEPT_URL)
        params = accept_tasks[0]['params']

        # org_key must be in its params
        self.assertIn('org_key', params)

        # add key to set of enqueued key names
        org_key_names.add(urllib.unquote(params['org_key']))

      else:
        post_data = None

      # remove all the enqueued tasks
      self.clear_task_queue()

    # there should be key name for every organization in the set
    self.assertEqual(len(org_key_names), TEST_NUMBER_OF_ORGS)

    # there should be an entry for every org
    expected_org_key_names = set(org_key.id() for org_key in self.org_keys)
    self.assertEqual(expected_org_key_names, org_key_names)
<|endoftext|>


<commit_before>import unittest

from cupy import testing
import cupyx.scipy.special  # NOQA
import numpy

@testing.gpu
@testing.with_requires('scipy')
class TestGamma(unittest.TestCase):

    @testing.for_all_dtypes(no_complex=True)
    @testing.numpy_cupy_allclose(atol=1e-5, scipy_name='scp')
    def test_arange(self, xp, scp, dtype):
        import scipy.special  # NOQA

        a = testing.shaped_arange((2, 3), xp, dtype)
        return scp.special.gamma(a)

    @testing.for_all_dtypes(no_complex=True, no_bool=True)
    @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
    def test_linspace(self, xp, scp, dtype):
        import scipy.special  # NOQA

        a = numpy.linspace(-30, 30, 1000, dtype=dtype)
        a = xp.asarray(a)
        return scp.special.gamma(a)

    @testing.for_all_dtypes(no_complex=True)
    @testing.numpy_cupy_allclose(atol=1e-2, rtol=1e-3, scipy_name='scp')
    def test_scalar(self, xp, scp, dtype):
        import scipy.special  # NOQA

        return scp.special.gamma(dtype(1.5))

    @testing.for_all_dtypes(no_complex=True)
    @testing.numpy_cupy_allclose(atol=1e-2, rtol=1e-3, scipy_name='scp')
    def test_inf_and_nan(self, xp, scp, dtype):
        import scipy.special  # NOQA

        a = numpy.array([-numpy.inf, numpy.nan, numpy.inf]).astype(dtype)
        a = xp.asarray(a)
        return scp.special.gamma(a)
<commit_msg>TST: test gamma with complex-valued inputs<commit_after>import unittest

from cupy import testing
import cupyx.scipy.special  # NOQA
import numpy

@testing.gpu
@testing.with_requires('scipy')
class TestGamma(unittest.TestCase):

    @testing.for_all_dtypes(no_complex=True)
    @testing.numpy_cupy_allclose(atol=1e-5, scipy_name='scp')
    def test_arange(self, xp, scp, dtype):
        import scipy.special  # NOQA

        a = testing.shaped_arange((2, 3), xp, dtype)
        return scp.special.gamma(a)

    @testing.for_all_dtypes(no_bool=True)
    @testing.numpy_cupy_allclose(atol=1e-5, rtol=1e-5, scipy_name='scp')
    def test_linspace(self, xp, scp, dtype):
        import scipy.special  # NOQA

        a = numpy.linspace(-30, 30, 1000, dtype=dtype)
        if a.dtype.kind == 'c':
            a -= 1j * a
        a = xp.asarray(a)
        return scp.special.gamma(a)

    @testing.for_all_dtypes(no_complex=True)
    @testing.numpy_cupy_allclose(atol=1e-2, rtol=1e-3, scipy_name='scp')
    def test_scalar(self, xp, scp, dtype):
        import scipy.special  # NOQA

        return scp.special.gamma(dtype(1.5))

    @testing.for_all_dtypes(no_complex=True)
    @testing.numpy_cupy_allclose(atol=1e-2, rtol=1e-3, scipy_name='scp')
    def test_inf_and_nan(self, xp, scp, dtype):
        import scipy.special  # NOQA

        a = numpy.array([-numpy.inf, numpy.nan, numpy.inf]).astype(dtype)
        a = xp.asarray(a)
        return scp.special.gamma(a)
<|endoftext|>


<commit_before>from cattle import ApiError

from common_fixtures import *  # NOQA

def _create_virtual_machine(client, context, **kw):
    args = {
        'accountId': context.project.id,
        'imageUuid': context.image_uuid,
    }
    args.update(kw)

    return client.create_virtual_machine(**args)

@pytest.fixture(scope='module')
def network(context):
    return context.nsp.network()

@pytest.fixture(scope='module')
def subnet(network):
    return network.subnets()[0]

def test_virtual_machine_case_sensitivity(super_client, client, context):
    name = random_str()
    volume = client.create_volume(name='R' + name, driver='local')

    assert volume.name == 'R' + name
    assert volume.state == 'requested'

    disks = [
        {
            'name': 'r' + name,
            'size': '2g',
        },
    ]

    vm = _create_virtual_machine(client, context, name=random_str(),
                                 volumeDriver='foo-bar',
                                 userdata='hi', vcpu=2, memoryMb=42,
                                 disks=disks)
    vm = client.wait_success(vm)
    assert vm.state == 'running'

    c = super_client.reload(vm)
    assert c.dataVolumes == ['/var/lib/rancher/vm:/vm',
                             'R{}:/volumes/disk00'.format(name)]

def test_virtual_machine_root_disk(super_client, client, context):
    disks = [
        {
            'size': '2g',
        },
        {
            'name': 'foo',
            'size': '2g',
            'root': True,
        },
        {
            'name': 'nope',
            'size': '2g',
            'root': True,
        },
        {
            'size': '2g',
        },
    ]

    vm = _create_virtual_machine(client, context, name=random_str(),
                                 volumeDriver='foo-bar',
                                 userdata='hi', vcpu=2, memoryMb=42,
                                 disks=disks)
    vm = client.wait_success(vm)
    assert vm.state == 'running'

    c = super_client.reload(vm)
    prefix = c.name + '-' + c.uuid[0:7]
    assert c.dataVolumes == ['/var/lib/rancher/vm:/vm',
                             '{}-00:/volumes/disk00'.format(prefix),
                             '{}-foo:/image'.format(prefix),
                             '{}-01:/volumes/disk01'.format(prefix)]

def test_virtual_machine_default_fields(super_client, client, context):
    disk_name = 'disk' + random_str()
    disks = [
        {
            'size': '2g',
            'opts': {
                'foo': 'bar'
            }
        },
        {
            'name': disk_name,
            'driver': 'foo',
        }
    ]

    vm = _create_virtual_machine(client, context,
                                 volumeDriver='foo-bar',
                                 userdata='hi', vcpu=2, memoryMb=42,
                                 disks=disks)
    vm = client.wait_success(vm)
    assert vm.state == 'running'
    assert vm.vcpu == 2
    assert vm.memoryMb == 42

    c = super_client.reload(vm)

    assert c.labels['io.rancher.vm'] == 'true'
    assert c.labels['io.rancher.vm.memory'] == '42'
    assert c.labels['io.rancher.vm.vcpu'] == '2'
    assert c.labels['io.rancher.vm.userdata'] == 'hi'
    assert c.dataVolumes == ['/var/lib/rancher/vm:/vm',
                             '{}-00:/volumes/disk00'.format(c.uuid[0:7]),
                             '{}-{}:/volumes/disk01'.format(c.uuid[0:7],
                                                            disk_name)]
    assert c.devices == ['/dev/kvm:/dev/kvm', '/dev/net/tun:/dev/net/tun']
    assert c.capAdd == ['NET_ADMIN']
    assert c.capabilities == ['console']

    volume1 = find_one(client.list_volume, name=c.uuid[0:7] + '-00')
    assert volume1.driver == 'foo-bar'
    assert volume1.driverOpts == {'vm': 'true', 'size': '2g', 'foo': 'bar'}

    x = c.uuid[0:7] + '-' + disk_name
    volume2 = find_one(client.list_volume, name=x)
    assert volume2.name == x
    assert volume2.driver == 'foo'
    assert volume2.driverOpts == {'vm': 'true', 'size': '40g'}

    assert c.dataVolumeMounts == {
        '/volumes/disk00': volume1.id,
        '/volumes/disk01': volume2.id,
    }

def test_virtual_machine_stats(client, context):
    vm = _create_virtual_machine(client, context, vcpu=2, memoryMb=42)
    vm = client.wait_success(vm)
    assert vm.state == 'running'

    assert 'stats' in vm
    assert 'containerStats' in vm

def test_virtual_machine_create_cpu_memory(client, context):
    vm = _create_virtual_machine(client, context,
                                 vcpu=2, memoryMb=42)

    vm = client.wait_success(vm)
    assert vm.state == 'running'

    assert vm.vcpu == 2
    assert vm.memoryMb == 42

def test_virtual_machine_create(super_client, context):
    vm = _create_virtual_machine(super_client, context)

    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    assert vm.vcpu == 1
    assert vm.memoryMb == 512

def test_virtual_machine_create_null_network_id(super_client, context):
    image_uuid = context.image_uuid
    try:
        super_client.create_virtual_machine(imageUuid=image_uuid,
                                            networkIds=[None])
        assert False
    except ApiError as e:
        assert e.error.code == 'NotNullable'

def test_virtual_machine_n_ids_s_ids(super_client, context,
                                     network, subnet):
    image_uuid = context.image_uuid
    try:
        super_client.create_virtual_machine(imageUuid=image_uuid,
                                            networkIds=[network.id],
                                            subnetIds=[subnet.id])
    except ApiError as e:
        assert e.error.code == 'NetworkIdsSubnetIdsMutuallyExclusive'

def test_virtual_machine_network(super_client, context, network, subnet):
    subnet_plain_id = get_plain_id(super_client, subnet)
    vm = _create_virtual_machine(super_client, context,
                                 networkIds=[network.id])

    vm = super_client.wait_success(vm)
    assert vm.state == 'running'
    assert 'networkIds' not in vm

    nics = vm.nics()
    assert len(nics) == 1

    nic = nics[0]

    assert nic.network().id == network.id
    assert nic.state == 'active'
    assert nic.macAddress is not None
    assert nic.macAddress.startswith(network.macPrefix)

    nic_admin = super_client.reload(nic)
    vm_admin = super_client.reload(vm)

    assert nic_admin.account().id == vm_admin.accountId

    ips = nic.ipAddresses()

    assert len(ips) == 1
    assert super_client.reload(nic).ipAddressNicMaps()[0].state == 'active'

    ip = ips[0]
    ip_admin = super_client.reload(ip)

    assert ip_admin.account().id == vm_admin.accountId
    assert ip_admin.subnet().id == nic_admin.subnet().id
    assert ip_admin.role == 'primary'

    assert ip.address is not None
    assert ip.address.startswith('10.42')

    assert vm.primaryIpAddress is not None
    assert vm.primaryIpAddress == ip.address

    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)
    assert ip.address in [x.item for x in addresses]

def test_virtual_machine_subnet(super_client, context, subnet):
    network = subnet.network()
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])

    vm = super_client.wait_success(vm)
    assert vm.state == 'running'
    assert 'subnetIds' not in vm

    nics = vm.nics()
    assert len(nics) == 1

    nic = nics[0]

    assert nic.subnetId == subnet.id
    assert nic.network().id == network.id
    assert nic.state == 'active'

    ips = nic.ipAddresses()

    assert len(ips) == 1

    ip = ips[0]

    assert ip.address is not None
    assert ip.address.startswith('10.42')

    assert vm.primaryIpAddress is not None
    assert vm.primaryIpAddress == ip.address

def test_virtual_machine_no_ip(super_client, context):
    account_id = context.project.id
    network = super_client.create_network(accountId=account_id)
    subnet = super_client.create_subnet(networkAddress='192.168.0.0',
                                        accountId=account_id,
                                        cidrSize='16',
                                        networkId=network.id,
                                        startAddress='192.168.0.3',
                                        endAddress='192.168.0.3')
    subnet = super_client.wait_success(subnet)
    assert subnet.state == 'active'
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])

    vm = super_client.wait_success(vm)

    assert vm.state == 'running'
    assert vm.primaryIpAddress == '192.168.0.3'

    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_transitioning(vm)

    assert vm.state == 'removed'
    assert vm.transitioning == 'error'
    assert vm.transitioningMessage == \
        'Failed to allocate IP from subnet : IP allocation error'

def test_virtual_machine_stop_subnet(super_client, context, subnet):
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1
    assert vm.nics()[0].ipAddresses()[0].address.startswith('10.42')

    vm = super_client.wait_success(vm.stop())

    assert vm.state == 'stopped'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1

    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]

    assert ip_address.state == 'active'
    assert ip_address.address.startswith('10.42')
    assert nic.state == 'inactive'

def test_virtual_machine_remove_subnet(super_client, context, subnet):
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1
    assert vm.nics()[0].ipAddresses()[0].address.startswith('10.42')

    vm = super_client.wait_success(vm.stop(remove=True))

    assert vm.state == 'removed'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1

    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]

    assert ip_address.state == 'active'
    assert ip_address.address.startswith('10.42')
    assert nic.state == 'removed'

def test_virtual_machine_purge_subnet(super_client, context, subnet):
    subnet_plain_id = get_plain_id(super_client, subnet)
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)
    assert vm.primaryIpAddress in [x.item for x in addresses]
    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1
    assert vm.nics()[0].ipAddresses()[0].address.startswith('10.42')

    vm = super_client.wait_success(vm.stop(remove=True))

    assert vm.state == 'removed'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1

    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]

    assert ip_address.state == 'active'
    assert ip_address.address.startswith('10.42')
    assert nic.state == 'removed'

    vm = super_client.wait_success(vm.purge())
    assert vm.state == 'purged'

    nics = vm.nics()
    assert len(nics) == 1

    nic = nics[0]
    assert nic.state == 'removed'
    assert nic.macAddress is not None

    nic = super_client.wait_success(nic.purge())
    assert nic.state == 'purged'
    assert nic.macAddress is None

    assert len(nic.ipAddressNicMaps()) == 1
    assert nic.ipAddressNicMaps()[0].state == 'removed'
    assert len(nic.ipAddresses()) == 0

    ip_address = super_client.reload(ip_address)
    assert ip_address.state == 'removed'
    assert ip_address.address is not None
    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)
    assert vm.primaryIpAddress not in [x.item for x in addresses]

def test_virtual_machine_restore_subnet(super_client, context, subnet):
    subnet_plain_id = get_plain_id(super_client, subnet)
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)

    assert vm.primaryIpAddress in [x.item for x in addresses]
    vm = super_client.wait_success(vm.stop())
    assert vm.state == 'stopped'

    vm = super_client.wait_success(super_client.delete(vm))
    assert vm.state == 'removed'

    assert vm.state == 'removed'
    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]
    address = ip_address.address
    assert ip_address.address.startswith('10.42')

    vm = vm.restore()
    assert vm.state == 'restoring'

    vm = super_client.wait_success(vm)
    assert vm.state == 'stopped'

    assert len(vm.nics()) == 1
    nic = vm.nics()[0]
    assert nic.state == 'inactive'

    assert len(nic.ipAddresses()) == 1
    ip_address = nic.ipAddresses()[0]
    assert ip_address.state == 'active'

    vm = super_client.wait_success(vm.start())

    assert vm.state == 'running'
    assert vm.nics()[0].ipAddresses()[0].address == address

def test_virtual_machine_console(super_client, context):
    vm = _create_virtual_machine(super_client, context)
    vm = super_client.wait_success(vm)

    assert 'console' in vm
    assert 'console' in vm and callable(vm.console)

    console = vm.console()

    assert console is not None
    assert console.url.endswith('/v1/console/')

def test_virtual_machine_console_visibility(super_client, context):
    vm = _create_virtual_machine(super_client, context)
    vm = super_client.wait_success(vm)

    assert 'console' in vm
    assert 'console' in vm and callable(vm.console)

    vm = super_client.wait_success(vm.stop())

    assert vm.state == 'stopped'
    assert 'console' not in vm
<commit_msg>Fix broken test on master<commit_after>from cattle import ApiError

from common_fixtures import *  # NOQA

def _create_virtual_machine(client, context, **kw):
    args = {
        'accountId': context.project.id,
        'imageUuid': context.image_uuid,
    }
    args.update(kw)

    return client.create_virtual_machine(**args)

@pytest.fixture(scope='module')
def network(context):
    return context.nsp.network()

@pytest.fixture(scope='module')
def subnet(network):
    return network.subnets()[0]

def test_virtual_machine_case_sensitivity(super_client, client, context):
    name = random_str()
    volume = client.create_volume(name='R' + name, driver='local')

    assert volume.name == 'R' + name
    assert volume.state == 'requested'

    disks = [
        {
            'name': 'r' + name,
            'size': '2g',
        },
    ]

    vm = _create_virtual_machine(client, context, name=random_str(),
                                 volumeDriver='foo-bar',
                                 userdata='hi', vcpu=2, memoryMb=42,
                                 disks=disks)
    vm = client.wait_success(vm)
    assert vm.state == 'running'

    c = super_client.reload(vm)
    assert len(c.dataVolumes) == 2
    assert c.dataVolumes[0] == '/var/lib/rancher/vm:/vm'
    assert c.dataVolumes[1] in {'R{}:/volumes/disk00'.format(name),
                                'r{}:/volumes/disk00'.format(name)}

def test_virtual_machine_root_disk(super_client, client, context):
    disks = [
        {
            'size': '2g',
        },
        {
            'name': 'foo',
            'size': '2g',
            'root': True,
        },
        {
            'name': 'nope',
            'size': '2g',
            'root': True,
        },
        {
            'size': '2g',
        },
    ]

    vm = _create_virtual_machine(client, context, name=random_str(),
                                 volumeDriver='foo-bar',
                                 userdata='hi', vcpu=2, memoryMb=42,
                                 disks=disks)
    vm = client.wait_success(vm)
    assert vm.state == 'running'

    c = super_client.reload(vm)
    prefix = c.name + '-' + c.uuid[0:7]
    assert c.dataVolumes == ['/var/lib/rancher/vm:/vm',
                             '{}-00:/volumes/disk00'.format(prefix),
                             '{}-foo:/image'.format(prefix),
                             '{}-01:/volumes/disk01'.format(prefix)]

def test_virtual_machine_default_fields(super_client, client, context):
    disk_name = 'disk' + random_str()
    disks = [
        {
            'size': '2g',
            'opts': {
                'foo': 'bar'
            }
        },
        {
            'name': disk_name,
            'driver': 'foo',
        }
    ]

    vm = _create_virtual_machine(client, context,
                                 volumeDriver='foo-bar',
                                 userdata='hi', vcpu=2, memoryMb=42,
                                 disks=disks)
    vm = client.wait_success(vm)
    assert vm.state == 'running'
    assert vm.vcpu == 2
    assert vm.memoryMb == 42

    c = super_client.reload(vm)

    assert c.labels['io.rancher.vm'] == 'true'
    assert c.labels['io.rancher.vm.memory'] == '42'
    assert c.labels['io.rancher.vm.vcpu'] == '2'
    assert c.labels['io.rancher.vm.userdata'] == 'hi'
    assert c.dataVolumes == ['/var/lib/rancher/vm:/vm',
                             '{}-00:/volumes/disk00'.format(c.uuid[0:7]),
                             '{}-{}:/volumes/disk01'.format(c.uuid[0:7],
                                                            disk_name)]
    assert c.devices == ['/dev/kvm:/dev/kvm', '/dev/net/tun:/dev/net/tun']
    assert c.capAdd == ['NET_ADMIN']
    assert c.capabilities == ['console']

    volume1 = find_one(client.list_volume, name=c.uuid[0:7] + '-00')
    assert volume1.driver == 'foo-bar'
    assert volume1.driverOpts == {'vm': 'true', 'size': '2g', 'foo': 'bar'}

    x = c.uuid[0:7] + '-' + disk_name
    volume2 = find_one(client.list_volume, name=x)
    assert volume2.name == x
    assert volume2.driver == 'foo'
    assert volume2.driverOpts == {'vm': 'true', 'size': '40g'}

    assert c.dataVolumeMounts == {
        '/volumes/disk00': volume1.id,
        '/volumes/disk01': volume2.id,
    }

def test_virtual_machine_stats(client, context):
    vm = _create_virtual_machine(client, context, vcpu=2, memoryMb=42)
    vm = client.wait_success(vm)
    assert vm.state == 'running'

    assert 'stats' in vm
    assert 'containerStats' in vm

def test_virtual_machine_create_cpu_memory(client, context):
    vm = _create_virtual_machine(client, context,
                                 vcpu=2, memoryMb=42)

    vm = client.wait_success(vm)
    assert vm.state == 'running'

    assert vm.vcpu == 2
    assert vm.memoryMb == 42

def test_virtual_machine_create(super_client, context):
    vm = _create_virtual_machine(super_client, context)

    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    assert vm.vcpu == 1
    assert vm.memoryMb == 512

def test_virtual_machine_create_null_network_id(super_client, context):
    image_uuid = context.image_uuid
    try:
        super_client.create_virtual_machine(imageUuid=image_uuid,
                                            networkIds=[None])
        assert False
    except ApiError as e:
        assert e.error.code == 'NotNullable'

def test_virtual_machine_n_ids_s_ids(super_client, context,
                                     network, subnet):
    image_uuid = context.image_uuid
    try:
        super_client.create_virtual_machine(imageUuid=image_uuid,
                                            networkIds=[network.id],
                                            subnetIds=[subnet.id])
    except ApiError as e:
        assert e.error.code == 'NetworkIdsSubnetIdsMutuallyExclusive'

def test_virtual_machine_network(super_client, context, network, subnet):
    subnet_plain_id = get_plain_id(super_client, subnet)
    vm = _create_virtual_machine(super_client, context,
                                 networkIds=[network.id])

    vm = super_client.wait_success(vm)
    assert vm.state == 'running'
    assert 'networkIds' not in vm

    nics = vm.nics()
    assert len(nics) == 1

    nic = nics[0]

    assert nic.network().id == network.id
    assert nic.state == 'active'
    assert nic.macAddress is not None
    assert nic.macAddress.startswith(network.macPrefix)

    nic_admin = super_client.reload(nic)
    vm_admin = super_client.reload(vm)

    assert nic_admin.account().id == vm_admin.accountId

    ips = nic.ipAddresses()

    assert len(ips) == 1
    assert super_client.reload(nic).ipAddressNicMaps()[0].state == 'active'

    ip = ips[0]
    ip_admin = super_client.reload(ip)

    assert ip_admin.account().id == vm_admin.accountId
    assert ip_admin.subnet().id == nic_admin.subnet().id
    assert ip_admin.role == 'primary'

    assert ip.address is not None
    assert ip.address.startswith('10.42')

    assert vm.primaryIpAddress is not None
    assert vm.primaryIpAddress == ip.address

    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)
    assert ip.address in [x.item for x in addresses]

def test_virtual_machine_subnet(super_client, context, subnet):
    network = subnet.network()
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])

    vm = super_client.wait_success(vm)
    assert vm.state == 'running'
    assert 'subnetIds' not in vm

    nics = vm.nics()
    assert len(nics) == 1

    nic = nics[0]

    assert nic.subnetId == subnet.id
    assert nic.network().id == network.id
    assert nic.state == 'active'

    ips = nic.ipAddresses()

    assert len(ips) == 1

    ip = ips[0]

    assert ip.address is not None
    assert ip.address.startswith('10.42')

    assert vm.primaryIpAddress is not None
    assert vm.primaryIpAddress == ip.address

def test_virtual_machine_no_ip(super_client, context):
    account_id = context.project.id
    network = super_client.create_network(accountId=account_id)
    subnet = super_client.create_subnet(networkAddress='192.168.0.0',
                                        accountId=account_id,
                                        cidrSize='16',
                                        networkId=network.id,
                                        startAddress='192.168.0.3',
                                        endAddress='192.168.0.3')
    subnet = super_client.wait_success(subnet)
    assert subnet.state == 'active'
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])

    vm = super_client.wait_success(vm)

    assert vm.state == 'running'
    assert vm.primaryIpAddress == '192.168.0.3'

    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_transitioning(vm)

    assert vm.state == 'removed'
    assert vm.transitioning == 'error'
    assert vm.transitioningMessage == \
        'Failed to allocate IP from subnet : IP allocation error'

def test_virtual_machine_stop_subnet(super_client, context, subnet):
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1
    assert vm.nics()[0].ipAddresses()[0].address.startswith('10.42')

    vm = super_client.wait_success(vm.stop())

    assert vm.state == 'stopped'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1

    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]

    assert ip_address.state == 'active'
    assert ip_address.address.startswith('10.42')
    assert nic.state == 'inactive'

def test_virtual_machine_remove_subnet(super_client, context, subnet):
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1
    assert vm.nics()[0].ipAddresses()[0].address.startswith('10.42')

    vm = super_client.wait_success(vm.stop(remove=True))

    assert vm.state == 'removed'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1

    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]

    assert ip_address.state == 'active'
    assert ip_address.address.startswith('10.42')
    assert nic.state == 'removed'

def test_virtual_machine_purge_subnet(super_client, context, subnet):
    subnet_plain_id = get_plain_id(super_client, subnet)
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)
    assert vm.primaryIpAddress in [x.item for x in addresses]
    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1
    assert vm.nics()[0].ipAddresses()[0].address.startswith('10.42')

    vm = super_client.wait_success(vm.stop(remove=True))

    assert vm.state == 'removed'

    assert len(vm.nics()) == 1
    assert len(vm.nics()[0].ipAddresses()) == 1

    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]

    assert ip_address.state == 'active'
    assert ip_address.address.startswith('10.42')
    assert nic.state == 'removed'

    vm = super_client.wait_success(vm.purge())
    assert vm.state == 'purged'

    nics = vm.nics()
    assert len(nics) == 1

    nic = nics[0]
    assert nic.state == 'removed'
    assert nic.macAddress is not None

    nic = super_client.wait_success(nic.purge())
    assert nic.state == 'purged'
    assert nic.macAddress is None

    assert len(nic.ipAddressNicMaps()) == 1
    assert nic.ipAddressNicMaps()[0].state == 'removed'
    assert len(nic.ipAddresses()) == 0

    ip_address = super_client.reload(ip_address)
    assert ip_address.state == 'removed'
    assert ip_address.address is not None
    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)
    assert vm.primaryIpAddress not in [x.item for x in addresses]

def test_virtual_machine_restore_subnet(super_client, context, subnet):
    subnet_plain_id = get_plain_id(super_client, subnet)
    vm = _create_virtual_machine(super_client, context,
                                 subnetIds=[subnet.id])
    vm = super_client.wait_success(vm)
    assert vm.state == 'running'

    addresses = super_client.list_resource_pool(poolType='subnet',
                                                poolId=subnet_plain_id)

    assert vm.primaryIpAddress in [x.item for x in addresses]
    vm = super_client.wait_success(vm.stop())
    assert vm.state == 'stopped'

    vm = super_client.wait_success(super_client.delete(vm))
    assert vm.state == 'removed'

    assert vm.state == 'removed'
    nic = vm.nics()[0]
    ip_address = nic.ipAddresses()[0]
    address = ip_address.address
    assert ip_address.address.startswith('10.42')

    vm = vm.restore()
    assert vm.state == 'restoring'

    vm = super_client.wait_success(vm)
    assert vm.state == 'stopped'

    assert len(vm.nics()) == 1
    nic = vm.nics()[0]
    assert nic.state == 'inactive'

    assert len(nic.ipAddresses()) == 1
    ip_address = nic.ipAddresses()[0]
    assert ip_address.state == 'active'

    vm = super_client.wait_success(vm.start())

    assert vm.state == 'running'
    assert vm.nics()[0].ipAddresses()[0].address == address

def test_virtual_machine_console(super_client, context):
    vm = _create_virtual_machine(super_client, context)
    vm = super_client.wait_success(vm)

    assert 'console' in vm
    assert 'console' in vm and callable(vm.console)

    console = vm.console()

    assert console is not None
    assert console.url.endswith('/v1/console/')

def test_virtual_machine_console_visibility(super_client, context):
    vm = _create_virtual_machine(super_client, context)
    vm = super_client.wait_success(vm)

    assert 'console' in vm
    assert 'console' in vm and callable(vm.console)

    vm = super_client.wait_success(vm.stop())

    assert vm.state == 'stopped'
    assert 'console' not in vm
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from __future__ import unicode_literals

import csv, logging, pprint

log = logging.getLogger(__name__)

class SSBuilder( object ):
    """ Builds file for serials-solutions.
        Main worker function: build_row() """

    def __init__(self ):
        self.row_dct = {}

    def build_row( self, data_dct ):
        """ Takes some dct vals, and creates others.
            Called by views.create_ss_file() """
        dct = {
            'issn': data_dct['issn'],
            'title': data_dct['title'],
            'type': 'Journal',
            'url': data_dct['url'],
            'location': '{building} - {callnumber}'.format( building=data_dct['building'], callnumber=data_dct['callnumber'] ),
            'display_location_note': 'Yes',
            'year_start': '{}'.format( data_dct['year_start'] ),
            'year_end': '{}'.format( data_dct['year_end'] ),
            }
        self.row_dct = dct
        lst = [
            dct['title'],
            dct['type'],
            dct['url'],
            dct['location'],
            dct['display_location_note'],
            dct['issn'],
            dct['year_start'],
            dct['year_end'],
            ]
        return lst

    def save_file( self, lines_lst, path ):
        """ Saves csv file.
            Called by views.create_ss_file() """
        try:
            with open(path, 'w') as outcsv:
                #configure writer to write standard csv file
                writer = csv.writer( outcsv, delimiter=','.encode('utf-8'), quotechar='"'.encode('utf-8'), quoting=csv.QUOTE_ALL, lineterminator='\n'.encode('utf-8') )
                for line_lst in sorted(lines_lst):
                    utf8_line_list = []
                    for element in line_lst:
                        utf8_line_list.append( element.encode('utf-8') )
                    #Write item to outcsv
                    writer.writerow(
                        [ utf8_line_list[0], utf8_line_list[1], utf8_line_list[2], utf8_line_list[3], utf8_line_list[4], utf8_line_list[5], utf8_line_list[6], utf8_line_list[7] ]
                        )
        except Exception as e:
            log.error( 'exception saving file, ```{}```'.format(unicode(repr(e))) )
        return

    # end class SSBuilder
<commit_msg>handles empty element.<commit_after># -*- coding: utf-8 -*-

from __future__ import unicode_literals

import csv, logging, pprint

log = logging.getLogger(__name__)

class SSBuilder( object ):
    """ Builds file for serials-solutions.
        Main worker function: build_row() """

    def __init__(self ):
        self.row_dct = {}

    def build_row( self, data_dct ):
        """ Takes some dct vals, and creates others.
            Called by views.create_ss_file() """
        dct = {
            'issn': data_dct['issn'],
            'title': data_dct['title'],
            'type': 'Journal',
            'url': data_dct['url'],
            'location': '{building} - {callnumber}'.format( building=data_dct['building'], callnumber=data_dct['callnumber'] ),
            'display_location_note': 'Yes',
            'year_start': '{}'.format( data_dct['year_start'] ),
            'year_end': '{}'.format( data_dct['year_end'] ),
            }
        self.row_dct = dct
        lst = [
            dct['title'],
            dct['type'],
            dct['url'],
            dct['location'],
            dct['display_location_note'],
            dct['issn'],
            dct['year_start'],
            dct['year_end'],
            ]
        return lst

    def save_file( self, lines_lst, path ):
        """ Saves csv file.
            Called by views.create_ss_file() """
        try:
            with open(path, 'w') as outcsv:
                #configure writer to write standard csv file
                writer = csv.writer( outcsv, delimiter=','.encode('utf-8'), quotechar='"'.encode('utf-8'), quoting=csv.QUOTE_ALL, lineterminator='\n'.encode('utf-8') )
                for line_lst in sorted(lines_lst):
                    utf8_line_list = []
                    for element in line_lst:
                        if element:
                            element.encode('utf-8')
                        else:
                            element = ''.encode('utf-8')
                        utf8_line_list.append( element )
                    #Write item to outcsv
                    writer.writerow(
                        [ utf8_line_list[0], utf8_line_list[1], utf8_line_list[2], utf8_line_list[3], utf8_line_list[4], utf8_line_list[5], utf8_line_list[6], utf8_line_list[7] ]
                        )
        except Exception as e:
            log.error( 'exception saving file, ```{}```'.format(unicode(repr(e))) )
        return

    # end class SSBuilder
<|endoftext|>


<commit_before>import json
import logging
import re
from typing import Any, Awaitable, Callable, Dict, List, Optional, Text

from rasa.core.channels.channel import InputChannel, OutputChannel, UserMessage
from rasa.utils.common import raise_warning
from sanic import Blueprint, response
from sanic.request import Request
from sanic.response import HTTPResponse
from slack import WebClient

logger = logging.getLogger(__name__)

class SlackBot(OutputChannel):
    """A Slack communication channel"""

    @classmethod
    def name(cls) -> Text:
        return "slack"

    def __init__(
        self,
        token: Text,
        slack_channel: Optional[Text] = None,
        ts: Optional[Text] = None,
    ) -> None:

        self.slack_channel = slack_channel
        self.ts = ts
        self.client = WebClient(token, run_async=True)
        super().__init__()

    @staticmethod
    def _get_text_from_slack_buttons(buttons: List[Dict]) -> Text:
        return "".join([b.get("title", "") for b in buttons])

    async def send_text_message(
        self, recipient_id: Text, text: Text, **kwargs: Any
    ) -> None:
        recipient = self.slack_channel or recipient_id
        for message_part in text.strip().split("\n\n"):
            await self.client.chat_postMessage(
                channel=recipient,
                as_user=True,
                text=message_part,
                type="mrkdwn",
                thread_ts=self.ts if self.ts else None,
            )

    async def send_image_url(
        self, recipient_id: Text, image: Text, **kwargs: Any
    ) -> None:
        recipient = self.slack_channel or recipient_id
        image_block = {"type": "image", "image_url": image, "alt_text": image}
        await self.client.chat_postMessage(
            channel=recipient,
            as_user=True,
            text=image,
            blocks=[image_block],
            thread_ts=self.ts if self.ts else None,
        )

    async def send_attachment(
        self, recipient_id: Text, attachment: Dict[Text, Any], **kwargs: Any
    ) -> None:
        recipient = self.slack_channel or recipient_id

        await self.client.chat_postMessage(
            channel=recipient,
            as_user=True,
            attachments=[attachment],
            thread_ts=self.ts if self.ts else None,
            **kwargs,
        )

    async def send_text_with_buttons(
        self,
        recipient_id: Text,
        text: Text,
        buttons: List[Dict[Text, Any]],
        **kwargs: Any,
    ) -> None:
        recipient = self.slack_channel or recipient_id

        text_block = {"type": "section", "text": {"type": "plain_text", "text": text}}

        if len(buttons) > 5:
            raise_warning(
                "Slack API currently allows only up to 5 buttons. "
                "Since you added more than 5, slack will ignore all of them."
            )
            return await self.send_text_message(recipient, text, **kwargs)

        button_block = {"type": "actions", "elements": []}
        for button in buttons:
            button_block["elements"].append(
                {
                    "type": "button",
                    "text": {"type": "plain_text", "text": button["title"]},
                    "value": button["payload"],
                }
            )

        await self.client.chat_postMessage(
            channel=recipient,
            as_user=True,
            text=text,
            blocks=[text_block, button_block],
            thread_ts=self.ts if self.ts else None,
        )

    async def send_custom_json(
        self, recipient_id: Text, json_message: Dict[Text, Any], **kwargs: Any
    ) -> None:
        logger.debug(f"send_custom_json, json_message: {json_message}")
        json_message.setdefault("channel", self.slack_channel or recipient_id)
        json_message.setdefault("as_user", True)
        json_message.setdefault("thread_ts", self.ts if self.ts else None)
        await self.client.chat_postMessage(**json_message)

class SlackInput(InputChannel):
    """Slack input channel implementation. Based on the HTTPInputChannel."""

    @classmethod
    def name(cls) -> Text:
        return "slack"

    @classmethod
    def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> InputChannel:
        if not credentials:
            cls.raise_missing_credentials_exception()

        # pytype: disable=attribute-error
        return cls(
            credentials.get("slack_token"),
            credentials.get("slack_channel"),
            credentials.get("slack_retry_reason_header", "x-slack-retry-reason"),
            credentials.get("slack_retry_number_header", "x-slack-retry-num"),
            credentials.get("errors_ignore_retry", None),
            credentials.get("use_threads", False),
        )
        # pytype: enable=attribute-error

    def __init__(
        self,
        slack_token: Text,
        slack_channel: Optional[Text] = None,
        slack_retry_reason_header: Optional[Text] = None,
        slack_retry_number_header: Optional[Text] = None,
        errors_ignore_retry: Optional[List[Text]] = None,
        use_threads: Optional[Text] = None,
    ) -> None:
        """Create a Slack input channel.

        Needs a couple of settings to properly authenticate and validate
        messages. Details to setup:

        https://github.com/slackapi/python-slackclient

        Args:
            slack_token: Your Slack Authentication token. You can create a
                Slack app and get your Bot User OAuth Access Token
                `here <https://api.slack.com/slack-apps>`_.
            slack_channel: the string identifier for a channel to which
                the bot posts, or channel name (e.g. '#bot-test')
                If not set, messages will be sent back
                to the "App" DM channel of your bot's name.
            slack_retry_reason_header: Slack HTTP header name indicating reason that slack send retry request.
            slack_retry_number_header: Slack HTTP header name indicating the attempt number
            errors_ignore_retry: Any error codes given by Slack
                included in this list will be ignored.
                Error codes are listed
                `here <https://api.slack.com/events-api#errors>`_.

        """
        self.slack_token = slack_token
        self.slack_channel = slack_channel
        self.errors_ignore_retry = errors_ignore_retry or ("http_timeout",)
        self.retry_reason_header = slack_retry_reason_header
        self.retry_num_header = slack_retry_number_header
        self.use_threads = use_threads

    @staticmethod
    def _is_app_mention(slack_event: Dict) -> bool:
        try:
            return slack_event["event"]["type"] == "app_mention"
        except KeyError:
            return False

    @staticmethod
    def _is_direct_message(slack_event: Dict) -> bool:
        try:
            return slack_event["event"]["channel_type"] == "im"
        except KeyError:
            return False

    @staticmethod
    def _is_user_message(slack_event: Dict) -> bool:
        return (
            slack_event.get("event")
            and (
                slack_event.get("event", {}).get("type") == "message"
                or slack_event.get("event", {}).get("type") == "app_mention"
            )
            and slack_event.get("event", {}).get("text")
            and not slack_event.get("event", {}).get("bot_id")
        )

    @staticmethod
    def _sanitize_user_message(text, uids_to_remove) -> Text:
        """Remove superfluous/wrong/problematic tokens from a message.

        Probably a good starting point for pre-formatting of user-provided text
        to make NLU's life easier in case they go funky to the power of extreme

        In the current state will just drop self-mentions of bot itself

        Args:
            text: raw message as sent from slack
            uids_to_remove: a list of user ids to remove from the content

        Returns:
            str: parsed and cleaned version of the input text
        """

        for uid_to_remove in uids_to_remove:
            # heuristic to format majority cases OK
            # can be adjusted to taste later if needed,
            # but is a good first approximation
            for regex, replacement in [
                (fr"<@{uid_to_remove}>\s", ""),
                (fr"\s<@{uid_to_remove}>", ""),  # a bit arbitrary but probably OK
                (fr"<@{uid_to_remove}>", " "),
            ]:
                text = re.sub(regex, replacement, text)

        """Find multiple mailto or http links like <mailto:xyz@rasa.com|xyz@rasa.com> or '<http://url.com|url.com>in text and substitute it with original content
        """

        pattern = r"(\<(?:mailto|http|https):\/\/.*?\|.*?\>)"
        match = re.findall(pattern, text)

        if match:
            for remove in match:
                replacement = remove.split("|")[1]
                replacement = replacement.replace(">", "")
                text = text.replace(remove, replacement)
        return text.strip()

    @staticmethod
    def _is_interactive_message(payload: Dict) -> bool:
        """Check wheter the input is a supported interactive input type."""

        supported = [
            "button",
            "select",
            "static_select",
            "external_select",
            "conversations_select",
            "users_select",
            "channels_select",
            "overflow",
            "datepicker",
        ]
        if payload.get("actions"):
            action_type = payload["actions"][0].get("type")
            if action_type in supported:
                return True
            elif action_type:
                logger.warning(
                    "Received input from a Slack interactive component of type "
                    f"'{payload['actions'][0]['type']}', for which payload parsing is not yet supported."
                )
        return False

    @staticmethod
    def _get_interactive_response(action: Dict) -> Optional[Text]:
        """Parse the payload for the response value."""

        if action["type"] == "button":
            return action.get("value")
        elif action["type"] == "select":
            return action.get("selected_options", [{}])[0].get("value")
        elif action["type"] == "static_select":
            return action.get("selected_option", {}).get("value")
        elif action["type"] == "external_select":
            return action.get("selected_option", {}).get("value")
        elif action["type"] == "conversations_select":
            return action.get("selected_conversation")
        elif action["type"] == "users_select":
            return action.get("selected_user")
        elif action["type"] == "channels_select":
            return action.get("selected_channel")
        elif action["type"] == "overflow":
            return action.get("selected_option", {}).get("value")
        elif action["type"] == "datepicker":
            return action.get("selected_date")

    async def process_message(
        self,
        request: Request,
        on_new_message: Callable[[UserMessage], Awaitable[Any]],
        text,
        sender_id: Optional[Text],
        metadata: Optional[Dict],
    ) -> Any:
        """Slack retries to post messages up to 3 times based on
        failure conditions defined here:
        https://api.slack.com/events-api#failure_conditions
        """
        retry_reason = request.headers.get(self.retry_reason_header)
        retry_count = request.headers.get(self.retry_num_header)
        if retry_count and retry_reason in self.errors_ignore_retry:
            logger.warning(
                f"Received retry #{retry_count} request from slack"
                f" due to {retry_reason}."
            )

            return response.text(None, status=201, headers={"X-Slack-No-Retry": 1})

        if metadata is not None:
            output_channel = metadata.get("out_channel")
            if self.use_threads:
                ts = metadata.get("ts")
            else:
                ts = None
        else:
            output_channel = None

        try:
            user_msg = UserMessage(
                text,
                self.get_output_channel(output_channel, ts),
                sender_id,
                input_channel=self.name(),
                metadata=metadata,
            )

            await on_new_message(user_msg)
        except Exception as e:
            logger.error(f"Exception when trying to handle message.{e}")
            logger.error(str(e), exc_info=True)

        return response.text("")

    def get_metadata(self, request: Request) -> Dict[Text, Any]:
        """Extracts the metadata from a slack API event (https://api.slack.com/types/event).

        Args:
            request: A `Request` object that contains a slack API event in the body.

        Returns:
            Metadata extracted from the sent event payload. This includes the output channel for the response,
            and users that have installed the bot.
        """

        if request.form:
            output = request.form
            payload = json.loads(output["payload"][0])
            logger.debug(f"get_metadata, payload: {payload}")
            message = payload.get("message", {})
            ts = message.get("thread_ts", message.get("ts"))
            return {
                "out_channel": payload.get("channel", {}).get("id"),
                "ts": ts,
                "users": payload.get("user", {}).get("id"),
            }
        elif request.json:
            slack_event = request.json
            event = slack_event.get("event", {})
            logger.debug(f"get_metadata, event: {event}")
            ts = event.get("thread_ts", event.get("ts"))

            return {
                "out_channel": event.get("channel"),
                "ts": ts,
                "users": slack_event.get("authed_users"),
            }

    def blueprint(
        self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
    ) -> Blueprint:
        slack_webhook = Blueprint("slack_webhook", __name__)

        @slack_webhook.route("/", methods=["GET"])
        async def health(_: Request) -> HTTPResponse:
            return response.json({"status": "ok"})

        @slack_webhook.route("/webhook", methods=["GET", "POST"])
        async def webhook(request: Request) -> HTTPResponse:
            if request.form:
                output = request.form
                payload = json.loads(output["payload"][0])
                logger.debug(f"slack form payload: {payload}")

                if self._is_interactive_message(payload):
                    sender_id = payload["user"]["id"]
                    text = self._get_interactive_response(payload["actions"][0])
                    if text is not None:
                        metadata = self.get_metadata(request)
                        return await self.process_message(
                            request, on_new_message, text, sender_id, metadata
                        )
                    elif payload["actions"][0]["type"] == "button":
                        # link buttons don't have "value", don't send their clicks to bot
                        return response.text("User clicked link button")
                return response.text(
                    "The input message could not be processed.", status=500
                )

            elif request.json:
                output = request.json
                event = output.get("event", {})
                user_message = event.get("text", "")
                sender_id = event.get("user", "")

                logger.debug(f'slack request event.ts: {event.get("ts", "")}, event.thread_ts: {event.get("thread_ts", "")}')
                metadata = self.get_metadata(request)

                if "challenge" in output:
                    return response.json(output.get("challenge"))

                elif self._is_user_message(output) and self._is_supported_channel(
                    output, metadata
                ):

                    return await self.process_message(
                        request,
                        on_new_message,
                        text=self._sanitize_user_message(
                            user_message, metadata["users"]
                        ),
                        sender_id=sender_id,
                        metadata=metadata,
                    )
                else:
                    logger.warning(
                        f"Received message on unsupported channel: {metadata['out_channel']}"
                    )

            return response.text("Bot message delivered.")

        return slack_webhook

    def _is_supported_channel(self, slack_event: Dict, metadata: Dict) -> bool:
        return (
            self._is_direct_message(slack_event)
            or self._is_app_mention(slack_event)
            or metadata["out_channel"] == self.slack_channel
        )

    def get_output_channel(self, channel: Optional[Text] = None, ts: Optional[Text] = None) -> OutputChannel:
        channel = channel or self.slack_channel
        return SlackBot(self.slack_token, channel, ts)

    def set_output_channel(self, channel: Text) -> None:
        self.slack_channel = channel

<commit_msg>Removed testing debug statements<commit_after>import json
import logging
import re
from typing import Any, Awaitable, Callable, Dict, List, Optional, Text

from rasa.core.channels.channel import InputChannel, OutputChannel, UserMessage
from rasa.utils.common import raise_warning
from sanic import Blueprint, response
from sanic.request import Request
from sanic.response import HTTPResponse
from slack import WebClient

logger = logging.getLogger(__name__)

class SlackBot(OutputChannel):
    """A Slack communication channel"""

    @classmethod
    def name(cls) -> Text:
        return "slack"

    def __init__(
        self,
        token: Text,
        slack_channel: Optional[Text] = None,
        ts: Optional[Text] = None,
    ) -> None:

        self.slack_channel = slack_channel
        self.ts = ts
        self.client = WebClient(token, run_async=True)
        super().__init__()

    @staticmethod
    def _get_text_from_slack_buttons(buttons: List[Dict]) -> Text:
        return "".join([b.get("title", "") for b in buttons])

    async def send_text_message(
        self, recipient_id: Text, text: Text, **kwargs: Any
    ) -> None:
        recipient = self.slack_channel or recipient_id
        for message_part in text.strip().split("\n\n"):
            await self.client.chat_postMessage(
                channel=recipient,
                as_user=True,
                text=message_part,
                type="mrkdwn",
                thread_ts=self.ts if self.ts else None,
            )

    async def send_image_url(
        self, recipient_id: Text, image: Text, **kwargs: Any
    ) -> None:
        recipient = self.slack_channel or recipient_id
        image_block = {"type": "image", "image_url": image, "alt_text": image}
        await self.client.chat_postMessage(
            channel=recipient,
            as_user=True,
            text=image,
            blocks=[image_block],
            thread_ts=self.ts if self.ts else None,
        )

    async def send_attachment(
        self, recipient_id: Text, attachment: Dict[Text, Any], **kwargs: Any
    ) -> None:
        recipient = self.slack_channel or recipient_id

        await self.client.chat_postMessage(
            channel=recipient,
            as_user=True,
            attachments=[attachment],
            thread_ts=self.ts if self.ts else None,
            **kwargs,
        )

    async def send_text_with_buttons(
        self,
        recipient_id: Text,
        text: Text,
        buttons: List[Dict[Text, Any]],
        **kwargs: Any,
    ) -> None:
        recipient = self.slack_channel or recipient_id

        text_block = {"type": "section", "text": {"type": "plain_text", "text": text}}

        if len(buttons) > 5:
            raise_warning(
                "Slack API currently allows only up to 5 buttons. "
                "Since you added more than 5, slack will ignore all of them."
            )
            return await self.send_text_message(recipient, text, **kwargs)

        button_block = {"type": "actions", "elements": []}
        for button in buttons:
            button_block["elements"].append(
                {
                    "type": "button",
                    "text": {"type": "plain_text", "text": button["title"]},
                    "value": button["payload"],
                }
            )

        await self.client.chat_postMessage(
            channel=recipient,
            as_user=True,
            text=text,
            blocks=[text_block, button_block],
            thread_ts=self.ts if self.ts else None,
        )

    async def send_custom_json(
        self, recipient_id: Text, json_message: Dict[Text, Any], **kwargs: Any
    ) -> None:
        json_message.setdefault("channel", self.slack_channel or recipient_id)
        json_message.setdefault("as_user", True)
        json_message.setdefault("thread_ts", self.ts if self.ts else None)
        await self.client.chat_postMessage(**json_message)

class SlackInput(InputChannel):
    """Slack input channel implementation. Based on the HTTPInputChannel."""

    @classmethod
    def name(cls) -> Text:
        return "slack"

    @classmethod
    def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> InputChannel:
        if not credentials:
            cls.raise_missing_credentials_exception()

        # pytype: disable=attribute-error
        return cls(
            credentials.get("slack_token"),
            credentials.get("slack_channel"),
            credentials.get("slack_retry_reason_header", "x-slack-retry-reason"),
            credentials.get("slack_retry_number_header", "x-slack-retry-num"),
            credentials.get("errors_ignore_retry", None),
            credentials.get("use_threads", False),
        )
        # pytype: enable=attribute-error

    def __init__(
        self,
        slack_token: Text,
        slack_channel: Optional[Text] = None,
        slack_retry_reason_header: Optional[Text] = None,
        slack_retry_number_header: Optional[Text] = None,
        errors_ignore_retry: Optional[List[Text]] = None,
        use_threads: Optional[Text] = None,
    ) -> None:
        """Create a Slack input channel.

        Needs a couple of settings to properly authenticate and validate
        messages. Details to setup:

        https://github.com/slackapi/python-slackclient

        Args:
            slack_token: Your Slack Authentication token. You can create a
                Slack app and get your Bot User OAuth Access Token
                `here <https://api.slack.com/slack-apps>`_.
            slack_channel: the string identifier for a channel to which
                the bot posts, or channel name (e.g. '#bot-test')
                If not set, messages will be sent back
                to the "App" DM channel of your bot's name.
            slack_retry_reason_header: Slack HTTP header name indicating reason that slack send retry request.
            slack_retry_number_header: Slack HTTP header name indicating the attempt number
            errors_ignore_retry: Any error codes given by Slack
                included in this list will be ignored.
                Error codes are listed
                `here <https://api.slack.com/events-api#errors>`_.

        """
        self.slack_token = slack_token
        self.slack_channel = slack_channel
        self.errors_ignore_retry = errors_ignore_retry or ("http_timeout",)
        self.retry_reason_header = slack_retry_reason_header
        self.retry_num_header = slack_retry_number_header
        self.use_threads = use_threads

    @staticmethod
    def _is_app_mention(slack_event: Dict) -> bool:
        try:
            return slack_event["event"]["type"] == "app_mention"
        except KeyError:
            return False

    @staticmethod
    def _is_direct_message(slack_event: Dict) -> bool:
        try:
            return slack_event["event"]["channel_type"] == "im"
        except KeyError:
            return False

    @staticmethod
    def _is_user_message(slack_event: Dict) -> bool:
        return (
            slack_event.get("event")
            and (
                slack_event.get("event", {}).get("type") == "message"
                or slack_event.get("event", {}).get("type") == "app_mention"
            )
            and slack_event.get("event", {}).get("text")
            and not slack_event.get("event", {}).get("bot_id")
        )

    @staticmethod
    def _sanitize_user_message(text, uids_to_remove) -> Text:
        """Remove superfluous/wrong/problematic tokens from a message.

        Probably a good starting point for pre-formatting of user-provided text
        to make NLU's life easier in case they go funky to the power of extreme

        In the current state will just drop self-mentions of bot itself

        Args:
            text: raw message as sent from slack
            uids_to_remove: a list of user ids to remove from the content

        Returns:
            str: parsed and cleaned version of the input text
        """

        for uid_to_remove in uids_to_remove:
            # heuristic to format majority cases OK
            # can be adjusted to taste later if needed,
            # but is a good first approximation
            for regex, replacement in [
                (fr"<@{uid_to_remove}>\s", ""),
                (fr"\s<@{uid_to_remove}>", ""),  # a bit arbitrary but probably OK
                (fr"<@{uid_to_remove}>", " "),
            ]:
                text = re.sub(regex, replacement, text)

        """Find multiple mailto or http links like <mailto:xyz@rasa.com|xyz@rasa.com> or '<http://url.com|url.com>in text and substitute it with original content
        """

        pattern = r"(\<(?:mailto|http|https):\/\/.*?\|.*?\>)"
        match = re.findall(pattern, text)

        if match:
            for remove in match:
                replacement = remove.split("|")[1]
                replacement = replacement.replace(">", "")
                text = text.replace(remove, replacement)
        return text.strip()

    @staticmethod
    def _is_interactive_message(payload: Dict) -> bool:
        """Check wheter the input is a supported interactive input type."""

        supported = [
            "button",
            "select",
            "static_select",
            "external_select",
            "conversations_select",
            "users_select",
            "channels_select",
            "overflow",
            "datepicker",
        ]
        if payload.get("actions"):
            action_type = payload["actions"][0].get("type")
            if action_type in supported:
                return True
            elif action_type:
                logger.warning(
                    "Received input from a Slack interactive component of type "
                    f"'{payload['actions'][0]['type']}', for which payload parsing is not yet supported."
                )
        return False

    @staticmethod
    def _get_interactive_response(action: Dict) -> Optional[Text]:
        """Parse the payload for the response value."""

        if action["type"] == "button":
            return action.get("value")
        elif action["type"] == "select":
            return action.get("selected_options", [{}])[0].get("value")
        elif action["type"] == "static_select":
            return action.get("selected_option", {}).get("value")
        elif action["type"] == "external_select":
            return action.get("selected_option", {}).get("value")
        elif action["type"] == "conversations_select":
            return action.get("selected_conversation")
        elif action["type"] == "users_select":
            return action.get("selected_user")
        elif action["type"] == "channels_select":
            return action.get("selected_channel")
        elif action["type"] == "overflow":
            return action.get("selected_option", {}).get("value")
        elif action["type"] == "datepicker":
            return action.get("selected_date")

    async def process_message(
        self,
        request: Request,
        on_new_message: Callable[[UserMessage], Awaitable[Any]],
        text,
        sender_id: Optional[Text],
        metadata: Optional[Dict],
    ) -> Any:
        """Slack retries to post messages up to 3 times based on
        failure conditions defined here:
        https://api.slack.com/events-api#failure_conditions
        """
        retry_reason = request.headers.get(self.retry_reason_header)
        retry_count = request.headers.get(self.retry_num_header)
        if retry_count and retry_reason in self.errors_ignore_retry:
            logger.warning(
                f"Received retry #{retry_count} request from slack"
                f" due to {retry_reason}."
            )

            return response.text(None, status=201, headers={"X-Slack-No-Retry": 1})

        if metadata is not None:
            output_channel = metadata.get("out_channel")
            if self.use_threads:
                ts = metadata.get("ts")
            else:
                ts = None
        else:
            output_channel = None

        try:
            user_msg = UserMessage(
                text,
                self.get_output_channel(output_channel, ts),
                sender_id,
                input_channel=self.name(),
                metadata=metadata,
            )

            await on_new_message(user_msg)
        except Exception as e:
            logger.error(f"Exception when trying to handle message.{e}")
            logger.error(str(e), exc_info=True)

        return response.text("")

    def get_metadata(self, request: Request) -> Dict[Text, Any]:
        """Extracts the metadata from a slack API event (https://api.slack.com/types/event).

        Args:
            request: A `Request` object that contains a slack API event in the body.

        Returns:
            Metadata extracted from the sent event payload. This includes the output channel for the response,
            and users that have installed the bot.
        """

        if request.form:
            output = request.form
            payload = json.loads(output["payload"][0])
            logger.debug(f"get_metadata, payload: {payload}")
            message = payload.get("message", {})
            ts = message.get("thread_ts", message.get("ts"))
            return {
                "out_channel": payload.get("channel", {}).get("id"),
                "ts": ts,
                "users": payload.get("user", {}).get("id"),
            }
        elif request.json:
            slack_event = request.json
            event = slack_event.get("event", {})
            logger.debug(f"get_metadata, event: {event}")
            ts = event.get("thread_ts", event.get("ts"))

            return {
                "out_channel": event.get("channel"),
                "ts": ts,
                "users": slack_event.get("authed_users"),
            }

    def blueprint(
        self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
    ) -> Blueprint:
        slack_webhook = Blueprint("slack_webhook", __name__)

        @slack_webhook.route("/", methods=["GET"])
        async def health(_: Request) -> HTTPResponse:
            return response.json({"status": "ok"})

        @slack_webhook.route("/webhook", methods=["GET", "POST"])
        async def webhook(request: Request) -> HTTPResponse:
            if request.form:
                output = request.form
                payload = json.loads(output["payload"][0])

                if self._is_interactive_message(payload):
                    sender_id = payload["user"]["id"]
                    text = self._get_interactive_response(payload["actions"][0])
                    if text is not None:
                        metadata = self.get_metadata(request)
                        return await self.process_message(
                            request, on_new_message, text, sender_id, metadata
                        )
                    elif payload["actions"][0]["type"] == "button":
                        # link buttons don't have "value", don't send their clicks to bot
                        return response.text("User clicked link button")
                return response.text(
                    "The input message could not be processed.", status=500
                )

            elif request.json:
                output = request.json
                event = output.get("event", {})
                user_message = event.get("text", "")
                sender_id = event.get("user", "")

                metadata = self.get_metadata(request)

                if "challenge" in output:
                    return response.json(output.get("challenge"))

                elif self._is_user_message(output) and self._is_supported_channel(
                    output, metadata
                ):
                    return await self.process_message(
                        request,
                        on_new_message,
                        text=self._sanitize_user_message(
                            user_message, metadata["users"]
                        ),
                        sender_id=sender_id,
                        metadata=metadata,
                    )
                else:
                    logger.warning(
                        f"Received message on unsupported channel: {metadata['out_channel']}"
                    )

            return response.text("Bot message delivered.")

        return slack_webhook

    def _is_supported_channel(self, slack_event: Dict, metadata: Dict) -> bool:
        return (
            self._is_direct_message(slack_event)
            or self._is_app_mention(slack_event)
            or metadata["out_channel"] == self.slack_channel
        )

    def get_output_channel(self, channel: Optional[Text] = None, ts: Optional[Text] = None) -> OutputChannel:
        channel = channel or self.slack_channel
        return SlackBot(self.slack_token, channel, ts)

    def set_output_channel(self, channel: Text) -> None:
        self.slack_channel = channel

<|endoftext|>