def test_execute_send_software_error(self, mock_post, mock_get): at = ActionTemplate.query.get('00000000-0000-0000-000a-000000000001') soft = Software(name='test', version=1, filename='test.zip') node1 = Server('nodeA', port=5000) ssa1 = SoftwareServerAssociation(software=soft, server=node1, path='/') db.session.add_all([soft, node1, ssa1]) mock_post.return_value = Response(msg={'error': 'message'}, code=400) mock_get.return_value = Response(code=400) ro = NativeSoftwareSendOperation(code, expected_stdout=at.expected_stdout, expected_stderr=at.expected_stderr, expected_rc=at.expected_rc) cp = ro._execute( dict(input=dict(software=str(soft.id), server=str(node1.id))), timeout=10, context=self.context) mock_post.assert_called_once_with(node1, 'api_1_0.send', json=dict(software_id=str(soft.id), dest_server_id=str( node1.id), background=False, include_transfer_data=True, force=True), timeout=10, identity=ROOT) self.assertFalse(cp.success) self.assertEqual(flask.json.dumps(mock_post.return_value.msg), cp.stdout)
def test_execute_send_software(self, mock_post, mock_get): at = ActionTemplate.query.get('00000000-0000-0000-000a-000000000001') soft = Software(name='test', version=1, filename='test.zip') node1 = Server('nodeA', port=5000) node2 = Server('nodeB', port=5000) ssa1 = SoftwareServerAssociation(software=soft, server=node1, path='/') ssa2 = SoftwareServerAssociation(software=soft, server=node2, path='/') ssa3 = SoftwareServerAssociation(software=soft, server=self.s1, path='/') db.session.add_all([soft, node1, node2, ssa1, ssa2, ssa3]) mock_post.return_value = Response(msg={'transfer_id': 1}, code=at.expected_rc) mock_get.return_value = Response(msg={ "route_list": [{ "cost": 0, "destination_id": f"{node1.id}", }, { "cost": 1, "destination_id": f"{self.s1.id}", }], }, code=200, server=node2) ro = NativeSoftwareSendOperation(code, expected_stdout=at.expected_stdout, expected_stderr=at.expected_stderr, expected_rc=at.expected_rc) cp = ro._execute(dict(input=dict(software=soft.id, server=node2.id, dest_path='dest', chunk_size=20, max_senders=2)), timeout=None, context=self.context) mock_post.assert_called_once_with(node1, 'api_1_0.send', json=dict(software_id=str(soft.id), dest_server_id=str( node2.id), background=False, include_transfer_data=True, force=True, dest_path='dest', chunk_size=20, max_senders=2), timeout=None, identity=ROOT) self.assertTrue(cp.success) self.assertEqual(flask.json.dumps(mock_post.return_value.msg), cp.stdout)
def test_get_headers_url_response(self, m): msg = {'data': 'content'} status = 200 def callback(request, **kwargs): if isinstance(request, PreparedRequest): self.assertEqual(str(self.server.id), request.headers.get('D-Destination')) self.assertEqual(str(self.server.id), request.headers.get('D-Source')) self.assertEqual("True", request.headers.get('D-Securizer')) self.assertEqual(f"Bearer {self.token}", request.headers.get('Authorization')) return status, {}, json.dumps(msg) else: self.assertEqual(str(self.server.id), kwargs['headers'].get('D-Destination')) self.assertEqual(str(self.server.id), kwargs['headers'].get('D-Source')) self.assertEqual("True", kwargs['headers'].get('D-Securizer')) self.assertEqual(f"Bearer {self.token}", kwargs['headers'].get('Authorization')) return CallbackResult(status=status, payload=msg) responses.add_callback(responses.GET, self.url, callback=callback) m.get(self.url, callback=callback) resp = get(self.server, 'home', auth=self.auth, headers={'D-Securizer': "True"}) self.assertEqual( Response(msg=msg, code=status, exception=None, server=self.server, url=self.url), resp) resp = run( async_get(self.server, 'home', auth=self.auth, headers={'D-Securizer': "True"})) self.assertEqual( Response(msg=msg, code=status, exception=None, server=self.server, url=self.url), resp)
def test_lock_unlock_lock_error_on_lock(self, m): m.post(self.n1.url('api_1_0.locker_prevent'), status=200, payload={'message': 'Preventing lock acquired'}) m.post(self.n2.url('api_1_0.locker_prevent'), status=200, payload={'message': 'Preventing lock acquired'}) m.post(self.n1.url('api_1_0.locker_lock'), status=200, payload={'message': 'Locked'}) m.post(self.n2.url('api_1_0.locker_lock'), status=409, payload={'error': 'Unable to lock'}) with self.assertRaises(errors.LockError) as e: ret = lock_unlock('L', Scope.ORCHESTRATION, [self.n1, self.n2], identity=ROOT) self.assertEqual(Scope.ORCHESTRATION, e.exception.scope) self.assertListEqual([ Response(msg={'error': 'Unable to lock'}, code=409, server=self.n2, url=self.n2.url('api_1_0.locker_lock')) ], e.exception.responses)
def test_send_file_background(self, mock_post, mock_executor_submit, mock_exists, mock_getsize, mock_md5): mock_post.return_value = Response(msg={'id': '1'}, code=200) mock_exists.return_value = True mock_getsize.return_value = self.size mock_md5.return_value = self.checksum resp = self.client.post(url_for('api_1_0.send'), json=dict(file=os.path.join( self.source_path, self.filename), dest_server_id=str(self.node2.id), dest_path=self.dest_path), headers=self.auth.header) mock_post.assert_called_once() server, view = mock_post.call_args[0] kwargs = mock_post.call_args[1] mock_executor_submit.assert_called_once() self.assertEqual(self.node2, db.session.merge(server)) self.assertDictEqual( { 'filename': self.filename, 'num_chunks': 1, 'dest_path': self.dest_path, 'checksum': self.checksum, 'size': self.size }, kwargs['json']) self.assertEqual(202, resp.status_code) self.assertDictEqual({'transfer_id': '1'}, resp.get_json())
def test_lock_unreachable_network(self, m): def callback_prevent(url, **kwargs): return CallbackResult("{'message': 'Preventing lock acquired'}", status=200) def callback_unlock(url, **kwargs): return CallbackResult("{'message': 'UnLocked'}", status=200) m.post(self.n1.url('api_1_0.locker_prevent'), callback=callback_prevent) m.post(self.n2.url('api_1_0.locker_prevent'), callback=callback_prevent) m.post(self.n2.url('api_1_0.locker_unlock'), callback=callback_unlock) self.n1.route.cost = None self.n1.route.gate = None self.n1.route.proxy_server = None with self.assertRaises(errors.LockError) as e: lock(Scope.CATALOG, servers=[self.n1, self.n2], identity=ROOT) self.assertEqual(Scope.CATALOG, e.exception.scope) self.assertEqual(errors.LockError.action_map['P'], e.exception.action) self.assertDictEqual( errors.format_error_content( errors.LockError( Scope.CATALOG, action='P', responses=[ Response(exception=errors.UnreachableDestination( self.n1), server=self.n1) ])), errors.format_error_content(e.exception)) self.assertEqual(2, len(m.requests))
def test_send_software_get_transfer_data(self, mock_post, mock_send_file, mock_exists, mock_get): mock_post.return_value = Response(msg={'id': '1'}, code=200) mock_exists.return_value = True mock_get.return_value = Response(msg={ 'transfer_id': '1', 'status': 'COMPLETED' }, code=200) resp = self.client.post(url_for('api_1_0.send'), json=dict(software_id=str(self.soft.id), dest_server_id=str(self.node2.id), dest_path=self.dest_path, include_transfer_data=True), headers=self.auth.header) mock_get.assert_called_once() server, view = mock_get.call_args[0] kwargs = mock_get.call_args[1] self.assertEqual(self.node2, db.session.merge(server)) self.assertEqual('api_1_0.transferresource', view) self.assertDictEqual({'transfer_id': '1'}, kwargs['view_data']) self.assertEqual(202, resp.status_code) self.assertDictEqual({ 'transfer_id': '1', 'status': 'COMPLETED' }, resp.get_json()) mock_get.return_value = Response( msg={'error': { 'message': 'some error content' }}, code=404) resp = self.client.post(url_for('api_1_0.send'), json=dict(software_id=str(self.soft.id), dest_server_id=str(self.node2.id), dest_path=self.dest_path, include_transfer_data=True), headers=self.auth.header) self.validate_error_response(resp, errors.HTTPError(mock_get.return_value))
class TestResourceCompleter(TestCase): default_response = Response(msg=[{ "last_modified_at": "20200616.113822.505932+0200", "id": "00000000-1111-0000-0000-000000000000", "name": "dev1", "granules": [] }, { "last_modified_at": "20200616.113955.580271+0200", "id": "00000000-2222-0000-0000-000000000000", "name": "dev2", "granules": [] }, { "last_modified_at": "20200616.113955.580271+0200", "id": "00000000-3333-0000-0000-000000000000", "name": "node3", "granules": [] }], code=200) @mock.patch('dimensigon.dshell.completer.ntwrk.request') @mock.patch('dimensigon.dshell.completer.ntwrk.generate_url') def test_get_completions(self, mock_generate_url, mock_request): mock_request.return_value = self.default_response completer = ResourceCompleter('api_1_0.serverlist', 'name', match_middle=False) # Static list on empty input. completions = completer.get_completions(Document(""), CompleteEvent()) self.assertListEqual(["dev1", "dev2", "node3"], [c.text for c in completions]) completions = completer.get_completions(Document("d"), CompleteEvent()) self.assertListEqual(["dev1", "dev2"], [c.text for c in completions]) @mock.patch('dimensigon.dshell.completer.ntwrk.request') @mock.patch('dimensigon.dshell.completer.ntwrk.generate_url') def test_filter(self, mock_generate_url, mock_request): mock_request.return_value = self.default_response completer = ResourceCompleter('servers', 'name', match_middle=True) completions = completer.get_completions(Document("3"), CompleteEvent()) self.assertListEqual(["node3"], [c.text for c in completions]) completer = ResourceCompleter('servers', 'name', ignore_case=True, match_middle=False) completions = completer.get_completions(Document("D"), CompleteEvent()) self.assertListEqual(["dev1", "dev2"], [c.text for c in completions])
def test_send_software_FileNotFound(self, mock_post, mock_send_file, mock_exists): mock_post.return_value = Response(msg={'transfer_id': '1'}, code=200) mock_exists.return_value = False resp = self.client.post(url_for('api_1_0.send'), json=dict(software_id=str(self.soft.id), dest_server_id=str(self.node2.id), dest_path=self.dest_path), headers=self.auth.header) self.validate_error_response( resp, errors.FileNotFound(os.path.join(self.source_path, self.filename)))
def test_send_NoSoftwareServer(self, mock_post, mock_send_file, mock_exists): mock_post.return_value = Response(msg={'id': '1'}, code=200) mock_exists.return_value = True db.session.delete(self.ssa) resp = self.client.post(url_for('api_1_0.send'), json=dict(software_id=str(self.soft.id), dest_server_id=str(self.node2.id), dest_path=self.dest_path), headers=self.auth.header) self.validate_error_response( resp, errors.NoSoftwareServer(str(self.soft.id)))
def test_send_file_FileNotFound(self, mock_post, mock_send_file, mock_exists, mock_getsize, mock_md5): mock_post.return_value = Response(msg={'id': '1'}, code=200) mock_exists.return_value = False mock_getsize.return_value = self.size mock_md5.return_value = self.checksum resp = self.client.post(url_for('api_1_0.send'), json=dict(file=os.path.join( self.source_path, self.filename), dest_server_id=str(self.node2.id), dest_path=self.dest_path), headers=self.auth.header) self.validate_error_response( resp, errors.FileNotFound(os.path.join(self.source_path, self.filename)))
def test_execute_send_software_no_ssa(self, mock_get): at = ActionTemplate.query.get('00000000-0000-0000-000a-000000000001') soft = Software(name='test', version=1, filename='test.zip') node1 = Server('nodeA', port=5000) db.session.add_all([soft, node1]) mock_get.return_value = Response(code=400) ro = NativeSoftwareSendOperation(code, expected_stdout=at.expected_stdout, expected_stderr=at.expected_stderr, expected_rc=at.expected_rc) cp = ro._execute(dict(input=dict(software=soft.id, server=self.s1.id)), context=self.context) self.assertFalse(cp.success) self.assertEqual(f'{soft.id} has no server association', cp.stderr)
def test_lock_unlock_lock_with_server_error_on_preventing(self, m): m.post(self.n1.url('api_1_0.locker_prevent'), status=200, payload={'message': 'Preventing lock acquired'}) m.post(self.n2.url('api_1_0.locker_prevent'), status=500, body="Error message") with self.assertRaises(errors.LockError) as e: ret = lock_unlock('L', Scope.ORCHESTRATION, [self.n1, self.n2], identity=ROOT) self.assertEqual(Scope.ORCHESTRATION, e.exception.scope) self.assertListEqual([ Response("Error message", code=500, server=self.n2, url=self.n2.url('api_1_0.locker_prevent')) ], e.exception.responses)
def test_lock_unlock_unlock_with_error(self, m): m.post(self.n2.url('api_1_0.locker_unlock'), status=200, payload={'message': 'UnLocked'}) m.post(self.n1.url('api_1_0.locker_unlock'), status=409, payload={'error': 'Unable to unlock.'}) with self.assertRaises(errors.LockError) as e: lock_unlock('U', Scope.ORCHESTRATION, [self.n1, self.n2], [str(self.n1.id), str(self.n2.id)], identity=ROOT) self.assertEqual(Scope.ORCHESTRATION, e.exception.scope) self.assertListEqual([ Response(msg={'error': 'Unable to unlock.'}, code=409, server=self.n1, url=self.n1.url('api_1_0.locker_unlock')) ], e.exception.responses)
def test_send_software(self, mock_post, mock_send_file, mock_exists): mock_post.return_value = Response(msg={'id': '1'}, code=200) mock_exists.return_value = True resp = self.client.post(url_for('api_1_0.send'), json=dict(software_id=str(self.soft.id), dest_server_id=str(self.node2.id), dest_path=self.dest_path), headers=self.auth.header) mock_post.assert_called_once() server, view = mock_post.call_args[0] kwargs = mock_post.call_args[1] self.assertEqual(self.node2, db.session.merge(server)) self.assertDictEqual( { 'software_id': str(self.soft.id), 'num_chunks': 1, 'dest_path': self.dest_path }, kwargs['json']) self.assertEqual(202, resp.status_code) self.assertDictEqual({'transfer_id': '1'}, resp.get_json())
def request(method, url, session=None, token_refreshed=False, login=True, **kwargs) -> Response: exception = None content = None status = None json_data = None headers = {} if not session: _session = requests.session() else: _session = session raise_on_error = kwargs.pop('raise_on_error', False) func = getattr(_session, method.lower()) if 'auth' not in kwargs: if env._access_token is None: try: refresh_access_token(login_=login) except requests.exceptions.ConnectionError as e: return Response(exception=ConnectionError( f"Unable to contact with {env.get('SCHEME')}://" f"{env.get('SERVER')}:{env.get('PORT')}/refresh"), url=url) except Exception as e: return Response(exception=e, url=url) else: if env._access_token is None: return Response( exception=ValueError("No authentication set"), url=url) kwargs['auth'] = HTTPBearerAuth(env._access_token) if 'headers' not in kwargs: kwargs['headers'] = {} kwargs['headers'].update({'D-Securizer': 'plain'}) kwargs['verify'] = env.get('SSL_VERIFY') logger.debug(f"{method.upper()} {url}\n{kwargs}") resp = None try: resp: requests.Response = func(url, **kwargs) except (requests.Timeout, ) as e: timeout = kwargs.get('timeout', None) if isinstance(timeout, tuple): timeout = timeout[0] + timeout[1] exception = TimeoutError( f"Socket timeout reached while trying to connect to {url} " f"for {timeout} seconds") except requests.ConnectionError as e: exception = ConnectionError(f"Unable to contact to {url}") except Exception as e: exception = e finally: if session is None and not (getattr(resp, 'status_code', None) == 401 and resp.json().get('msg') == 'Token has expired'): _session.close() if exception is None: status = resp.status_code headers = resp.headers if status == 401 and not token_refreshed: json_data = resp.json() if json_data.get('msg') == 'Token has expired': try: refresh_access_token() except requests.exceptions.ConnectionError as e: return Response(exception=ConnectionError( f"Unable to contact with {env.get('SCHEME')}://" f"{env.get('SERVER')}:{env.get('PORT')}/refresh"), url=url) kwargs['auth'] = HTTPBearerAuth(env._access_token) resp = request(method, url, session=_session, token_refreshed=True, **kwargs) if not session: _session.close() return resp try: json_data = resp.json() except (ValueError, ): content = resp.text if json_data is not None: # try: # content = unpack_msg(json_data) # except NotValidMessage: # content = json_data content = json_data else: if raise_on_error: raise exception return Response(msg=content, code=status, exception=exception, url=url, headers=headers)
def to_json(self, add_step_exec=False, human=False, split_lines=False): data = {} if self.id: data.update(id=str(self.id)) if self.start_time: data.update( start_time=self.start_time.strftime(defaults.DATETIME_FORMAT)) if self.end_time: data.update( end_time=self.end_time.strftime(defaults.DATETIME_FORMAT)) if human: # convert target ids to server names d = {} if isinstance(self.target, dict): for k, v in self.target.items(): if is_iterable_not_string(v): d[k] = [str(Server.query.get(s) or s) for s in v] else: d[k] = str(Server.query.get(v) or v) elif isinstance(self.target, list): d = [str(Server.query.get(s) or s) for s in self.target] else: d = str(Server.query.get(self.target) or self.target) data.update(target=d) if self.executor: data.update(executor=str(self.executor)) if self.service: data.update(service=str(self.service)) if self.orchestration: data.update( orchestration=dict(id=str(self.orchestration.id), name=self.orchestration.name, version=self.orchestration.version)) else: data.update(orchestration=None) if self.server: data.update( server=dict(id=str(self.server.id), name=self.server.name)) else: data.update(target=self.target) if self.orchestration_id or getattr(self.orchestration, 'id', None): data.update( orchestration_id=str(self.orchestration_id or getattr( self.orchestration, 'id', None))) if self.executor_id or getattr(self.executor, 'id', None): data.update(executor_id=str( self.executor_id or getattr(self.executor, 'id', None))) if self.service_id or getattr(self.service, 'id', None): data.update(service_id=str( self.server_id or getattr(self.service, 'id', None))) if self.server_id or getattr(self.server, 'id', None): data.update(server_id=str(self.server_id or getattr(self.server, 'id', None))) data.update(params=self.params) data.update(success=self.success) data.update(undo_success=self.undo_success) data.update(message=self.message) if self.parent_step_execution_id and not add_step_exec: data.update( parent_step_execution_id=str(self.parent_step_execution_id)) if add_step_exec: steps = [] for se in self.step_executions: se: StepExecution se_json = se.to_json(human, split_lines=split_lines) if se.child_orch_execution: se_json[ 'orch_execution'] = se.child_orch_execution.to_json( add_step_exec=add_step_exec, split_lines=split_lines, human=human) elif se.child_orch_execution_id: from dimensigon.web.network import get, Response from dimensigon.network.auth import HTTPBearerAuth from flask_jwt_extended import create_access_token params = ['steps'] if human: params.append('human') try: resp = get(se.server, 'api_1_0.orchexecutionresource', view_data=dict( execution_id=se.child_orch_execution_id, params=params)) except Exception as e: current_app.logger.exception( f"Exception while trying to acquire orch execution " f"{se.child_orch_execution_id} from {se.server}") resp = Response(exception=e) if resp.ok: se_json['orch_execution'] = resp.msg se_json.pop('child_orch_execution_id', None) steps.append(se_json) # steps.sort(key=lambda x: x.start_time) data.update(steps=steps) return data
def test_get_completions(self, mock_generate_url, mock_request): mock_request.return_value = Response(msg=[{ "last_modified_at": "20200616.113822.505932+0200", "id": "01", "name": "dev1", "granules": [] }, { "last_modified_at": "20200616.113955.580271+0200", "id": "02", "name": "dev2", "granules": [] }, { "last_modified_at": "20200616.113955.580271+0200", "id": "13", "name": "node3", "granules": [] }], code=200) data = { 'status': [{ 'argument': 'node', 'completer': ResourceCompleter('server', 'name') }], 'server': { 'list': [{ 'argument': '--json', 'action': 'store_true', 'required': False }, [{ 'argument': '--like' }, { 'argument': '--name', 'completer': ResourceCompleter('servers', 'name') }, { 'argument': '--id', 'completer': ResourceCompleter('servers', 'id') }, { 'argument': '--last', 'action': 'store', 'type': int }]], 'show': [{ 'argument': '--json', 'action': 'store_true', 'required': False }, { 'argument': 'node', 'nargs': '+', 'completer': ResourceCompleter('servers', 'name') }] }, 'nargs': [{ 'argument': 'compiler', 'completer': WordCompleter(['python', 'ruby', 'c++']), 'nargs': 2 }, { 'argument': 'so', 'completer': WordCompleter(['windows', 'linux', 'mac']) }, { 'argument': '--foo', 'nargs': 1, 'completer': WordCompleter(['aaa', 'bbb']) }, { 'argument': '--bar', 'nargs': '*', 'completer': WordCompleter(['x1', 'x2', 'x3']) }, { 'argument': '--xyz', 'nargs': '+', 'completer': WordCompleter(['x', 'y', 'z']) }] } completer = DshellCompleter.from_nested_dict(data) # Static list on empty input. completions = completer.get_completions(Document(""), CompleteEvent()) self.assertListEqual(["status", "server", "nargs"], [c.text for c in completions]) # mid word completions = completer.get_completions(Document("st"), CompleteEvent()) self.assertListEqual(["status"], [c.text for c in completions]) # completed word completions = completer.get_completions(Document("status"), CompleteEvent()) self.assertListEqual([], [c.text for c in completions]) completions = completer.get_completions(Document("status "), CompleteEvent()) self.assertListEqual(["dev1", "dev2", "node3"], [c.text for c in completions]) completions = completer.get_completions(Document("server "), CompleteEvent()) self.assertListEqual(["list", "show"], [c.text for c in completions]) completions = completer.get_completions(Document("server l"), CompleteEvent()) self.assertListEqual(["list"], [c.text for c in completions]) completions = completer.get_completions(Document("server list "), CompleteEvent()) self.assertListEqual(["--json", "--like", "--name", "--id", "--last"], [c.text for c in completions]) completions = completer.get_completions(Document("server list --j"), CompleteEvent()) self.assertListEqual(["--json"], [c.text for c in completions]) completions = completer.get_completions( Document("server list --json "), CompleteEvent()) self.assertListEqual(["--json", "--like", "--name", "--id", "--last"], [c.text for c in completions]) completions = completer.get_completions( Document("server list --json --id "), CompleteEvent()) self.assertListEqual(["01", "02", "13"], [c.text for c in completions]) completions = completer.get_completions( Document("server list --json --id 0"), CompleteEvent()) self.assertListEqual(["01", "02"], [c.text for c in completions]) completions = completer.get_completions( Document("server list --json --id 01 "), CompleteEvent()) self.assertListEqual(["--json", "--like", "--name", "--id", "--last"], [c.text for c in completions]) completions = completer.get_completions(Document("server show "), CompleteEvent()) self.assertListEqual(["--json", "dev1", "dev2", "node3"], [c.text for c in completions]) completions = completer.get_completions(Document("nargs "), CompleteEvent()) self.assertListEqual( ['--foo', '--bar', '--xyz', 'python', 'ruby', 'c++'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --foo "), CompleteEvent()) self.assertListEqual(['aaa', 'bbb'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --foo s "), CompleteEvent()) self.assertListEqual( ['--foo', '--bar', '--xyz', 'python', 'ruby', 'c++'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --bar "), CompleteEvent()) self.assertListEqual( ['--foo', '--bar', '--xyz', '--', 'x1', 'x2', 'x3'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --bar x"), CompleteEvent()) self.assertListEqual(['x1', 'x2', 'x3'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --bar x1 x2 "), CompleteEvent()) self.assertListEqual(['--foo', '--bar', '--xyz', '--', 'x3'], [c.text for c in completions]) completions = completer.get_completions( Document("nargs --bar x1 x2 x3 "), CompleteEvent()) self.assertListEqual(['--foo', '--bar', '--xyz', '--'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --xyz "), CompleteEvent()) self.assertListEqual(['x', 'y', 'z'], [c.text for c in completions]) completions = completer.get_completions(Document("nargs --xyz x "), CompleteEvent()) self.assertListEqual(['--foo', '--bar', '--xyz', '--', 'y', 'z'], [c.text for c in completions]) completions = completer.get_completions( Document("nargs --bar x1 x2 x3 -- python ruby "), CompleteEvent()) self.assertListEqual(['windows', 'linux', 'mac'], [c.text for c in completions])