Example #1
0
async def async_server(request, virtual_smoothie_env, loop):
    if _should_skip_api1(request):
        pytest.skip('requires api1 only')
    elif _should_skip_api2(request):
        pytest.skip('requires api2 only')
    with request.param(loop) as hw:
        if request.param == using_api1:
            app = init(hw)
            app['api_version'] = 1
        else:
            app = init(hw)
            app['api_version'] = 2
        yield app
        await app.shutdown()
async def test_wifi_list(virtual_smoothie_env, loop, aiohttp_client,
                         monkeypatch):
    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    expected_res = [{
        'ssid': 'Opentrons',
        'signal': 81,
        'active': True
    }, {
        'ssid': 'Big Duck',
        'signal': 47,
        'active': False
    }, {
        'ssid': 'HP-Print-1-LaserJet Pro',
        'signal': 35,
        'active': False
    }, {
        'ssid': 'Guest Wireless',
        'signal': 24,
        'active': False
    }]

    async def mock_available():
        return expected_res

    monkeypatch.setattr(nmcli, 'available_ssids', mock_available)

    expected = json.dumps({'list': expected_res})

    resp = await cli.get('/wifi/list')
    text = await resp.text()
    assert resp.status == 200
    assert text == expected
async def test_wifi_configure(
        virtual_smoothie_env, loop, test_client, monkeypatch):
    app = init(loop)
    cli = await loop.create_task(test_client(app))

    msg = "Device 'wlan0' successfully activated with '076aa998-0275-4aa0-bf85-e9629021e267'."  # noqa

    async def mock_configure(ssid, securityType=None, psk=None, hidden=False):
        # Command: nmcli device wifi connect "{ssid}" password "{psk}"
        return True, msg

    monkeypatch.setattr(nmcli, 'configure', mock_configure)

    expected = {'ssid': 'Opentrons', 'message': msg}

    resp = await cli.post(
        '/wifi/configure', json={'ssid': 'Opentrons', 'psk': 'scrt sqrl'})
    body = await resp.json()
    assert resp.status == 201
    assert body == expected

    resp = await cli.post(
        '/wifi/configure', json={'ssid': 'asasd', 'foo': 'bar'})
    assert resp.status == 400
    body = await resp.json()
    assert 'message' in body
async def test_list_keys(loop, test_client, wifi_keys_tempdir):
    dummy_names = ['ad12d1df199bc912', 'cbdda8124128cf', '812410990c5412']
    app = init(loop)
    cli = await loop.create_task(test_client(app))
    empty_resp = await cli.get('/wifi/keys')
    assert empty_resp.status == 200
    empty_body = await empty_resp.json()
    assert empty_body == {'keys': []}

    for dn in dummy_names:
        os.mkdir(os.path.join(wifi_keys_tempdir, dn))
        open(os.path.join(wifi_keys_tempdir, dn, 'test.pem'), 'w').write('hi')

    resp = await cli.get('/wifi/keys')
    assert resp.status == 200
    body = await resp.json()
    keys = body['keys']
    assert len(keys) == 3
    for dn in dummy_names:
        for keyfile in keys:
            if keyfile['id'] == dn:
                assert keyfile['name'] == 'test.pem'
                assert keyfile['uri'] == '/wifi/keys/{}'.format(dn)
                break
        else:
            raise KeyError(dn)
async def test_ignore_updates(virtual_smoothie_env, loop, aiohttp_client):
    tmpdir = tempfile.mkdtemp("files")
    ignore_name = "testy_ignore.json"
    serverlib_fallback.filepath = os.path.join(tmpdir, ignore_name)
    app = init()
    cli = await loop.create_task(aiohttp_client(app))
    # Test no ignore file found
    r0 = await cli.get('/update/ignore')
    r0body = await r0.text()
    assert json.loads(r0body) == {'version': None}

    # Test that values are set correctly

    ignore = {'version': '3.1.3'}
    r1 = await cli.post('/update/ignore', json=ignore)
    assert r1.status == 200

    # Test that you cannot pass an empty version
    ignore2 = {'version': ''}
    r2 = await cli.post('/update/ignore', json=ignore2)
    assert r2.status == 400

    # Test that version in the temporary directory is still '3.1.3'
    r3 = await cli.get('/update/ignore')
    r3body = await r3.text()
    assert json.loads(r3body) == {'version': '3.1.3'}
async def test_get(virtual_smoothie_env, loop, test_client):
    app = init(loop)
    cli = await loop.create_task(test_client(app))

    resp = await cli.get('/settings')
    body = await resp.json()
    assert resp.status == 200
    validate_response_body(body)
Example #7
0
async def async_server(request, virtual_smoothie_env, loop):
    if request.node.get_marker('api1_only') and request.param != using_api1:
        pytest.skip('requires api1 only')
    elif request.node.get_marker('api2_only') and request.param != using_api2:
        pytest.skip('requires api2 only')
    with request.param(loop):
        app = init(loop)
        app['api_version'] = 1 if request.param == using_api1 else 2
        yield app
        await app.shutdown()
async def test_add_key_no_key(loop, aiohttp_client):
    """Test response when no key supplied"""
    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    with patch("opentrons.system.wifi.add_key") as p:
        r = await cli.post('/wifi/keys', data={})

        p.assert_not_called()
        assert r.status == 400
async def test_key_lifecycle(loop, test_client, wifi_keys_tempdir):
    with tempfile.TemporaryDirectory() as source_td:
        app = init(loop)
        cli = await loop.create_task(test_client(app))
        empty_resp = await cli.get('/wifi/keys')
        assert empty_resp.status == 200
        empty_body = await empty_resp.json()
        assert empty_body == {'keys': []}

        results = {}
        # We should be able to add multiple keys
        for fn in ['test1.pem', 'test2.pem', 'test3.pem']:
            path = os.path.join(source_td, fn)
            with open(path, 'w') as f:
                f.write(str(random.getrandbits(2048)))
            upload_resp = await cli.post('/wifi/keys',
                                         data={'key': open(path, 'rb')})
            assert upload_resp.status == 201
            upload_body = await upload_resp.json()
            assert 'uri' in upload_body
            assert 'id' in upload_body
            assert 'name' in upload_body
            assert upload_body['name'] == os.path.basename(fn)
            assert upload_body['uri'] == '/wifi/keys/'\
                + upload_body['id']
            results[fn] = upload_body

        # We should not be able to upload a duplicate
        dup_resp = await cli.post(
            '/wifi/keys',
            data={'key': open(os.path.join(source_td, 'test1.pem'))})
        assert dup_resp.status == 200
        dup_body = await dup_resp.json()
        assert 'message' in dup_body

        # We should be able to see them all
        list_resp = await cli.get('/wifi/keys')
        assert list_resp.status == 200
        list_body = await list_resp.json()
        keys = list_body['keys']
        assert len(keys) == 3
        for elem in keys:
            assert elem['id'] in [r['id'] for r in results.values()]

        for fn, data in results.items():
            del_resp = await cli.delete(data['uri'])
            assert del_resp.status == 200
            del_body = await del_resp.json()
            assert 'message' in del_body
            del_list_resp = await cli.get('/wifi/keys')
            del_list_body = await del_list_resp.json()
            assert data['id'] not in [k['id'] for k in del_list_body['keys']]

        dup_del_resp = await cli.delete(results['test1.pem']['uri'])
        assert dup_del_resp.status == 404
async def test_remove_key(arg, remove_key_return, expected_status,
                          expected_body, loop, aiohttp_client):
    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    with patch("opentrons.system.wifi.remove_key") as p:
        p.return_value = remove_key_return
        r = await cli.delete("/wifi/keys/" + arg)
        p.assert_called_once_with(arg)
        assert r.status == expected_status
        assert await r.json() == expected_body
async def test_set(virtual_smoothie_env, loop, test_client):
    app = init(loop)
    cli = await loop.create_task(test_client(app))
    test_id = 'disableHomeOnBoot'

    resp = await cli.post('/settings', json={"id": test_id, "value": True})
    body = await resp.json()
    assert resp.status == 200
    validate_response_body(body)
    test_setting = list(
        filter(lambda x: x.get('id') == test_id, body.get('settings')))[0]
    assert test_setting.get('value')
Example #12
0
async def test_health(virtual_smoothie_env, loop, aiohttp_client):
    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    expected = json.dumps({
        'name': 'opentrons-dev',
        'api_version': __version__,
        'fw_version': 'Virtual Smoothie',
        'logs': ['/logs/serial.log', '/logs/api.log'],
        'system_version': '0.0.0'
    })
    resp = await cli.get('/health')
    text = await resp.text()
    assert resp.status == 200
    assert text == expected
async def test_add_key_response(add_key_return, expected_status, expected_body,
                                loop, aiohttp_client, wifi_keys_tempdir):
    with tempfile.TemporaryDirectory() as source_td:
        app = init()
        cli = await loop.create_task(aiohttp_client(app))

        path = os.path.join(source_td, "t.pem")
        with open(path, 'w') as f:
            f.write(str(random.getrandbits(20)))

        with patch("opentrons.system.wifi.add_key") as p:
            p.return_value = add_key_return
            r = await cli.post('/wifi/keys', data={'key': open(path, 'rb')})
            assert r.status == expected_status
            assert await r.json() == expected_body
async def test_restart(virtual_smoothie_env, monkeypatch, loop, test_client):
    test_data = {"test": "pass"}

    async def mock_restart(request):
        return web.json_response(test_data)

    monkeypatch.setattr(serverlib_fallback, 'restart', mock_restart)

    app = init(loop)
    cli = await loop.create_task(test_client(app))

    expected = json.dumps(test_data)
    resp = await cli.post('/server/restart')
    text = await resp.text()
    assert resp.status == 200
    assert text == expected
Example #15
0
async def test_available_resets(virtual_smoothie_env, loop, test_client):
    app = init(loop)
    cli = await loop.create_task(test_client(app))

    resp = await cli.get('/settings/reset/options')
    body = await resp.json()
    options_list = body.get('options')
    assert resp.status == 200
    for key in ['tipProbe', 'labwareCalibration', 'bootScripts']:
        for opt in options_list:
            if opt['id'] == key:
                assert 'name' in opt
                assert 'description' in opt
                break
        else:
            raise KeyError(key)
async def test_networking_status(virtual_smoothie_env, loop, aiohttp_client,
                                 monkeypatch):
    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    async def mock_is_connected():
        return 'full'

    connection_statuses = {
        'wlan0': {
            # test "--" gets mapped to None
            'ipAddress': None,
            'macAddress': 'B8:27:EB:5F:A6:89',
            # test "--" gets mapped to None
            'gatewayAddress': None,
            'state': 'disconnected',
            'type': 'wifi'
        },
        'eth0': {
            'ipAddress': '169.254.229.173/16',
            'macAddress': 'B8:27:EB:39:C0:9A',
            # test missing output gets mapped to None
            'gatewayAddress': None,
            'state': 'connected',
            'type': 'ethernet'
        }
    }

    async def mock_iface_info(k: nmcli.NETWORK_IFACES):
        return connection_statuses[k.value]

    monkeypatch.setattr(nmcli, 'is_connected', mock_is_connected)
    monkeypatch.setattr(nmcli, 'iface_info', mock_iface_info)

    expected = {'status': 'full', 'interfaces': connection_statuses}

    resp = await cli.get('/networking/status')
    body_json = await resp.json()
    assert resp.status == 200
    assert body_json == expected

    async def mock_is_connected():
        raise FileNotFoundError("No")

    monkeypatch.setattr(nmcli, 'is_connected', mock_is_connected)
    resp = await cli.get('/networking/status')
    assert resp.status == 500
async def test_add_key_call(loop, aiohttp_client, wifi_keys_tempdir):
    """Test that uploaded file is processed properly"""
    with tempfile.TemporaryDirectory() as source_td:
        app = init()
        cli = await loop.create_task(aiohttp_client(app))

        # We should be able to add multiple keys
        for fn in ['test1.pem', 'test2.pem', 'test3.pem']:
            path = os.path.join(source_td, fn)
            with open(path, 'w') as f:
                f.write(str(random.getrandbits(20)))

            with patch("opentrons.system.wifi.add_key") as p:
                await cli.post('/wifi/keys', data={'key': open(path, 'rb')})

                with open(path, 'rb') as f:
                    p.assert_called_once_with(fn, f.read())
async def test_restart(virtual_smoothie_env, monkeypatch, async_server,
                       aiohttp_client):
    test_data = {"test": "pass"}
    loop = asyncio.get_event_loop()

    async def mock_restart(request):
        return web.json_response(test_data)

    monkeypatch.setattr(serverlib_fallback, 'restart', mock_restart)
    hw = async_server['com.opentrons.hardware']
    app = init(hw)
    cli = await loop.create_task(aiohttp_client(app))

    expected = json.dumps(test_data)
    resp = await cli.post('/server/restart')
    text = await resp.text()
    assert resp.status == 200
    assert text == expected
async def test_update_module_firmware(dummy_attached_modules,
                                      virtual_smoothie_env, loop,
                                      aiohttp_client, monkeypatch):

    app = init()
    client = await loop.create_task(aiohttp_client(app))
    serial_num = 'mdYYYYMMDD123'
    fw_filename = 'dummyFirmware.hex'
    tmpdir = tempfile.mkdtemp("files")

    with open(os.path.join(tmpdir, fw_filename), 'wb') as fd:
        fd.write(bytes(0x1234))

    def dummy_discover_modules():
        return

    async def mock_enter_bootloader(module):
        return '/dev/modules/tty0_bootloader'

    monkeypatch.setattr(robot, 'discover_modules', dummy_discover_modules)
    monkeypatch.setattr(robot, '_attached_modules', dummy_attached_modules)
    monkeypatch.setattr(modules, 'enter_bootloader', mock_enter_bootloader)

    # ========= Happy path ==========
    res_msg = {
        'message': 'Firmware update successful',
        'avrdudeResponse': '1234 bytes of flash verified',
        'filename': fw_filename
    }

    async def mock_successful_upload_to_module(module, fw_file, loop):
        return res_msg

    expected_res = res_msg

    monkeypatch.setattr(modules, 'update_firmware',
                        mock_successful_upload_to_module)
    resp = await client.post(
        '/modules/{}/update'.format(serial_num),
        data={'module_firmware': open(os.path.join(tmpdir, fw_filename))})

    assert resp.status == 200
    res = await resp.json()
    assert res == expected_res
async def test_wifi_disconnect(loop, aiohttp_client, monkeypatch):
    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    msg1 = 'Connection \'ot_wifi\' successfully deactivated. ' \
           'Connection \'ot_wifi\' (fa7ed807-23ef-41f0-ab3e-34' \
           '99cc5a960e) successfully deleted'

    async def mock_disconnect(ssid):
        # Command: nmcli connection down ssid
        return True, msg1

    monkeypatch.setattr(nmcli, 'wifi_disconnect', mock_disconnect)

    expected = {'message': 'SSID must be specified as a string'}
    resp = await cli.post('/wifi/disconnect', json={})
    body = await resp.json()
    assert resp.status == 400
    assert body == expected

    resp = await cli.post('wifi/disconnect', json={'ssid': 'ot_wifi'})
    body = await resp.json()
    assert resp.status == 200
    assert 'message' in body

    msg2 = 'Connection \'ot_wifi\' successfully deactivated. \n' \
           'Error: Could not remove ssid. No connection for ssid ot_wif123'

    async def mock_bad_disconnect(ssid):
        # Command: nmcli connection down ssid
        return True, msg2

    monkeypatch.setattr(nmcli, 'wifi_disconnect', mock_bad_disconnect)

    resp = await cli.post('wifi/disconnect', json={'ssid': 'ot_wifi'})
    body = await resp.json()
    assert resp.status == 207
    assert 'message' in body
Example #21
0
async def test_log_endpoints(virtual_smoothie_env, loop, test_client):
    app = init(loop)
    cli = await loop.create_task(test_client(app))

    # # Test that values are set correctly
    serial_name = "serial.log"
    serial_file = config.CONFIG['log_dir'] / serial_name
    data1 = {'serial': 'No, CEREAL!'}
    with open(serial_file, 'w') as data_file:
        json.dump(data1, data_file)

    s1 = await cli.get('/logs/serial.log')
    s1body = await s1.text()
    assert json.loads(s1body) == data1

    api_name = "api.log"
    api_file = config.CONFIG['log_dir'] / api_name
    data2 = {'api': 'application program interface'}
    with open(api_file, 'w') as data_file:
        json.dump(data2, data_file)

    a1 = await cli.get('/logs/api.log')
    a1body = await a1.text()
    assert json.loads(a1body) == data2
async def test_update(virtual_smoothie_env, monkeypatch, loop, aiohttp_client):
    msg = "success"
    whl_name = "testy.whl"
    serverlib_name = "testylib.whl"
    fw_name = "testy.fw"
    tmpdir = tempfile.mkdtemp("files")
    for filename in [whl_name, serverlib_name, fw_name]:
        with open(os.path.join(tmpdir, filename), 'w') as fd:
            fd.write("test")

    async def mock_install(filename, loop=None, modeset=True):
        return msg

    monkeypatch.setattr(serverlib_fallback, '_install', mock_install)
    monkeypatch.setattr(robot, 'update_firmware', mock_install)

    app = init()
    cli = await loop.create_task(aiohttp_client(app))

    data = {
        'whl': open(os.path.join(tmpdir, whl_name)),
        'serverlib': open(os.path.join(tmpdir, serverlib_name)),
        'fw': open(os.path.join(tmpdir, fw_name))
    }

    # Note: hits API server update endpoint--this test covers backward
    # compatibility until the update server is universally available
    resp = await cli.post('/server/update', data=data)

    expected = json.dumps({
        'message': [msg, msg, msg],
        'filename': [whl_name, serverlib_name, fw_name]
    })
    text = await resp.text()
    assert resp.status == 200
    assert text == expected
async def test_eap_config_options(virtual_smoothie_env, loop, test_client):
    app = init(loop)
    cli = await loop.create_task(test_client(app))
    resp = await cli.get('/wifi/eap-options')

    assert resp.status == 200

    body = await resp.json()
    # Check that the body is shaped correctly but ignore the actual content
    assert 'options' in body
    option_keys = ('name', 'displayName', 'required', 'type')
    option_types = ('string', 'password', 'file')

    def check_option(opt_dict):
        for key in option_keys:
            assert key in opt_dict
        assert opt_dict['type'] in option_types

    for opt in body['options']:
        assert 'name' in opt
        assert 'displayName' in opt
        assert 'options' in opt
        for method_opt in opt['options']:
            check_option(method_opt)
async def test_fail_update_module_firmware(dummy_attached_modules,
                                           virtual_smoothie_env, loop,
                                           aiohttp_client, monkeypatch):
    app = init()
    client = await loop.create_task(aiohttp_client(app))
    serial_num = 'mdYYYYMMDD123'
    fw_filename = 'dummyFirmware.hex'
    tmpdir = tempfile.mkdtemp("files")

    with open(os.path.join(tmpdir, fw_filename), 'wb') as fd:
        fd.write(bytes(0x1234))

    def dummy_discover_modules():
        return

    async def mock_enter_bootloader(module):
        return '/dev/modules/tty0_bootloader'

    monkeypatch.setattr(robot, 'discover_modules', dummy_discover_modules)
    monkeypatch.setattr(robot, '_attached_modules', dummy_attached_modules)
    monkeypatch.setattr(modules, 'enter_bootloader', mock_enter_bootloader)

    # ========= Case 1: Port not accessible =========
    res_msg1 = {
        'message': 'Firmware update failed',
        'avrdudeResponse': 'ser_open(): can\'t open device',
        'filename': fw_filename
    }

    async def mock_failed_upload_to_module1(serialnum, fw_file, loop):
        return res_msg1

    expected_res1 = res_msg1

    monkeypatch.setattr(modules, 'update_firmware',
                        mock_failed_upload_to_module1)
    resp1 = await client.post(
        '/modules/{}/update'.format(serial_num),
        data={'module_firmware': open(os.path.join(tmpdir, fw_filename))})

    assert resp1.status == 500
    j1 = await resp1.json()
    assert j1 == expected_res1

    # ========= Case 2: Corrupted file =========
    res_msg2 = {
        'message': 'Firmware update failed',
        'avrdudeResponse': 'checksum mismatch in line 1234',
        'filename': fw_filename
    }

    async def mock_failed_upload_to_module2(serialnum, fw_file, loop):
        return res_msg2

    expected_res2 = res_msg2

    monkeypatch.setattr(modules, 'update_firmware',
                        mock_failed_upload_to_module2)
    resp2 = await client.post(
        '/modules/{}/update'.format(serial_num),
        data={'module_firmware': open(os.path.join(tmpdir, fw_filename))})

    assert resp2.status == 400
    j2 = await resp2.json()
    assert j2 == expected_res2

    # ========= Case 3: AVRDUDE not responding =========
    expected_res3 = {
        'message': 'AVRDUDE not responding',
        'filename': fw_filename
    }

    async def mock_failed_upload_to_module3(serialnum, fw_file, loop):
        await asyncio.sleep(2)

    monkeypatch.setattr(modules, 'update_firmware',
                        mock_failed_upload_to_module3)
    update.UPDATE_TIMEOUT = 0.1

    resp3 = await client.post(
        '/modules/{}/update'.format(serial_num),
        data={'module_firmware': open(os.path.join(tmpdir, fw_filename))})

    assert resp3.status == 500
    j3 = await resp3.json()
    assert j3 == expected_res3

    # ========= Case 4: No module/ incorrect serial =========
    wrong_serial = 'abcdef'
    expected_res4 = {
        'message': 'Module {} not found'.format(wrong_serial),
        'filename': fw_filename
    }

    resp4 = await client.post(
        '/modules/{}/update'.format(wrong_serial),
        data={'module_firmware': open(os.path.join(tmpdir, fw_filename))})

    assert resp4.status == 404
    j4 = await resp4.json()
    assert j4 == expected_res4
async def test_networking_status(
        virtual_smoothie_env, loop, test_client, monkeypatch):
    app = init(loop)
    cli = await loop.create_task(test_client(app))

    async def mock_call(cmd):
        # Command: `nmcli networking connectivity`
        if 'connectivity' in cmd:
            res = 'full'
        elif 'wlan0' in cmd:
            res = '''GENERAL.HWADDR:B8:27:EB:5F:A6:89
IP4.ADDRESS[1]:--
IP4.GATEWAY:--
GENERAL.TYPE:wifi
GENERAL.STATE:30 (disconnected)'''
        elif 'eth0' in cmd:
            res = '''GENERAL.HWADDR:B8:27:EB:39:C0:9A
IP4.ADDRESS[1]:169.254.229.173/16
GENERAL.TYPE:ethernet
GENERAL.STATE:100 (connected)'''
        else:
            res = 'incorrect nmcli call'

        return res, ''

    monkeypatch.setattr(nmcli, '_call', mock_call)

    expected = json.dumps({
        'status': 'full',
        'interfaces': {
            'wlan0': {
                # test "--" gets mapped to None
                'ipAddress': None,
                'macAddress': 'B8:27:EB:5F:A6:89',
                # test "--" gets mapped to None
                'gatewayAddress': None,
                'state': 'disconnected',
                'type': 'wifi'
            },
            'eth0': {
                'ipAddress': '169.254.229.173/16',
                'macAddress': 'B8:27:EB:39:C0:9A',
                # test missing output gets mapped to None
                'gatewayAddress': None,
                'state': 'connected',
                'type': 'ethernet'
            }
        }
    })

    resp = await cli.get('/networking/status')
    text = await resp.text()
    assert resp.status == 200
    assert text == expected

    async def mock_call(cmd):
        if 'connectivity' in cmd:
            return 'full', ''
        else:
            return '', 'this is a dummy error'

    monkeypatch.setattr(nmcli, '_call', mock_call)
    resp = await cli.get('/networking/status')
    assert resp.status == 500
Example #26
0
async def async_server(hardware, virtual_smoothie_env, loop):
    app = init(hardware, loop=loop)
    yield app
    await app.shutdown()
Example #27
0
async def async_server(hardware, virtual_smoothie_env, loop, aiohttp_server):
    testserver = await aiohttp_server(init(hardware, loop=loop))
    yield testserver.app