1# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import mock
6import unittest
7
8import common
9from autotest_lib.frontend.afe.json_rpc import proxy as rpc_proxy
10from autotest_lib.server import frontend
11from autotest_lib.server.hosts import afe_store
12from autotest_lib.server.hosts import host_info
13
14class AfeStoreTest(unittest.TestCase):
15    """Test refresh/commit success cases for AfeStore."""
16
17    def setUp(self):
18        self.hostname = 'some-host'
19        self.mock_afe = mock.create_autospec(frontend.AFE, instance=True)
20        self.store = afe_store.AfeStore(self.hostname, afe=self.mock_afe)
21
22
23    def _create_mock_host(self, labels, attributes):
24        """Create a mock frontend.Host with the given labels and attributes.
25
26        @param labels: The labels to set on the host.
27        @param attributes: The attributes to set on the host.
28        @returns: A mock object for frontend.Host.
29        """
30        mock_host = mock.create_autospec(frontend.Host, instance=True)
31        mock_host.labels = labels
32        mock_host.attributes = attributes
33        return mock_host
34
35
36    def test_refresh(self):
37        """Test that refresh correctly translates host information."""
38        self.mock_afe.get_hosts.return_value = [
39                self._create_mock_host(['label1'], {'attrib1': 'val1'})]
40        info = self.store._refresh_impl()
41        self.assertListEqual(info.labels, ['label1'])
42        self.assertDictEqual(info.attributes, {'attrib1': 'val1'})
43
44
45    def test_refresh_no_host_raises(self):
46        """Test that refresh complains if no host is found."""
47        self.mock_afe.get_hosts.return_value = []
48        with self.assertRaises(host_info.StoreError):
49            self.store._refresh_impl()
50
51
52    def test_refresh_multiple_hosts_picks_first(self):
53        """Test that refresh returns the first host if multiple match."""
54        self.mock_afe.get_hosts.return_value = [
55                self._create_mock_host(['label1'], {'attrib1': 'val1'}),
56                self._create_mock_host(['label2'], {'attrib2': 'val2'})]
57        info = self.store._refresh_impl()
58        self.assertListEqual(info.labels, ['label1'])
59        self.assertDictEqual(info.attributes, {'attrib1': 'val1'})
60
61
62    def test_commit_labels(self):
63        """Tests that labels are updated correctly on commit."""
64        self.mock_afe.get_hosts.return_value = [
65                self._create_mock_host(['label1'], {})]
66        info = host_info.HostInfo(['label2'], {})
67        self.store._commit_impl(info)
68        self.assertEqual(self.mock_afe.run.call_count, 2)
69        expected_run_calls = [
70                mock.call('host_remove_labels', id='some-host',
71                          labels={'label1'}),
72                mock.call('host_add_labels', id='some-host',
73                          labels={'label2'}),
74        ]
75        self.mock_afe.run.assert_has_calls(expected_run_calls,
76                                           any_order=True)
77
78
79    def test_commit_labels_raises(self):
80        """Test that exception while committing is translated properly."""
81        self.mock_afe.get_hosts.return_value = [
82                self._create_mock_host(['label1'], {})]
83        self.mock_afe.run.side_effect = rpc_proxy.JSONRPCException('some error')
84        info = host_info.HostInfo(['label2'], {})
85        with self.assertRaises(host_info.StoreError):
86            self.store._commit_impl(info)
87
88
89    def test_committing_attributes_warns(self):
90        """Test that a warning is issued if attribute changes are committed."""
91        self.mock_afe.get_hosts.return_value = [
92                self._create_mock_host([], {})]
93        info = host_info.HostInfo([], {'attrib': 'val'})
94        with mock.patch('logging.warning', autospec=True) as mock_warning:
95            self.store._commit_impl(info)
96        self.assertEqual(mock_warning.call_count, 1)
97        self.assertRegexpMatches(
98                mock_warning.call_args[0][0],
99                '.*Updating attributes is currently not supported.*')
100
101
102    @unittest.expectedFailure
103    def test_commit_adds_attributes(self):
104        """Tests that new attributes are added correctly on commit."""
105        self.mock_afe.get_hosts.return_value = [
106                self._create_mock_host([], {})]
107        info = host_info.HostInfo([], {'attrib1': 'val1'})
108        self.store._commit_impl(info)
109        self.assertEqual(self.mock_afe.set_host_attribute.call_count, 1)
110        self.mock_afe.assert_called_once_with('attrib1', 'val1',
111                                              hostname=self.hostname)
112
113
114    @unittest.expectedFailure
115    def test_commit_updates_attributes(self):
116        """Tests that existing attributes are updated correctly on commit."""
117        self.mock_afe.get_hosts.return_value = [
118                self._create_mock_host([], {'attrib1': 'val1'})]
119        info = host_info.HostInfo([], {'attrib1': 'val1_updated'})
120        self.store._commit_impl(info)
121        self.assertEqual(self.mock_afe.set_host_attribute.call_count, 1)
122        self.mock_afe.assert_called_once_with('attrib1', 'val1_updated',
123                                              hostname=self.hostname)
124
125
126    @unittest.expectedFailure
127    def test_commit_deletes_attributes(self):
128        """Tests that deleted attributes are updated correctly on commit."""
129        self.mock_afe.get_hosts.return_value = [
130                self._create_mock_host([], {'attrib1': 'val1'})]
131        info = host_info.HostInfo([], {})
132        self.store._commit_impl(info)
133        self.assertEqual(self.mock_afe.set_host_attribute.call_count, 1)
134        self.mock_afe.assert_called_once_with('attrib1', None,
135                                              hostname=self.hostname)
136
137
138if __name__ == '__main__':
139    unittest.main()
140