def test_load_component_failed_cause_package_not_found(self): """Test package not found in import.""" self.add_item("skill", "fetchai/echo:latest", local=True) with cd(self._get_cwd()): echo_dir = "./vendor/fetchai/skills/echo" handlers_file = Path(echo_dir) / "handlers.py" assert handlers_file.exists() file_data = handlers_file.read_text() file_data = file_data.replace( "from packages.fetchai.protocols.default", "from packages.fetchai.protocols.not_exist_protocol", ) handlers_file.write_text(file_data) with cd("./vendor/fetchai"): self.run_cli_command("fingerprint", "skill", "fetchai/echo:0.14.0") agent_context = Mock() agent_context.agent_name = self.agent_name configuration = cast( SkillConfig, load_component_configuration(ComponentType.SKILL, Path(echo_dir)), ) configuration.directory = Path(echo_dir) with pytest.raises( ClickException, match= r"Package loading error: An error occurred while loading skill fetchai/echo:.*\nTraceback", ) as e: self.run_cli_command("run", cwd=self._get_cwd()) assert "No AEA package found with author name" in str(e) assert "not_exist_protocol" in str(e)
def test_upgrade(self): """Test upgrade project old version to latest one and compare with latest project fetched.""" with cd(self.latest_agent_name): latest_agent_items = set( ItemRemoveHelper(self.load_mock_context()) .get_agent_dependencies_with_reverse_dependencies() .keys() ) with cd(self.agent_name): self.runner.invoke( # pylint: disable=no-member cli, ["--skip-consistency-check", "upgrade", "--local"], standalone_mode=False, catch_exceptions=False, ) agent_items = set( ItemRemoveHelper(self.load_mock_context()) .get_agent_dependencies_with_reverse_dependencies() .keys() ) assert latest_agent_items == agent_items # upgrade again to check it workd with upgraded version with cd(self.agent_name): self.runner.invoke( # pylint: disable=no-member cli, ["--skip-consistency-check", "upgrade", "--local"], standalone_mode=False, catch_exceptions=False, ) agent_items = set( ItemRemoveHelper(self.load_mock_context()) .get_agent_dependencies_with_reverse_dependencies() .keys() ) assert latest_agent_items == agent_items # compare both configuration files, except the agent name and the author upgraded_agent_dir = Path(self.agent_name) latest_agent_dir = Path(self.latest_agent_name) lines_upgraded_agent_config = ( (upgraded_agent_dir / DEFAULT_AEA_CONFIG_FILE).read_text().splitlines() ) lines_latest_agent_config = ( (latest_agent_dir / DEFAULT_AEA_CONFIG_FILE).read_text().splitlines() ) # the slice is because we don't compare the agent name and the author name assert lines_upgraded_agent_config[2:] == lines_latest_agent_config[2:] # compare vendor folders. assert are_dirs_equal( upgraded_agent_dir / "vendor", latest_agent_dir / "vendor" )
def invoke(cls, *args: str) -> Result: """Call the cli command.""" with cd(cls._get_cwd()): result = cls.runner.invoke( cli, args, standalone_mode=False, catch_exceptions=False ) return result
def _launch_threads(agents: List[Path]) -> int: """ Launch many agents, multithreaded. :param click_context: the click context. :param agents: list of paths to agent projects. :return: exit status """ aeas = [] # type: List[AEA] for agent_directory in agents: with cd(agent_directory): aeas.append(AEABuilder.from_aea_project(".").build()) runner = AEARunner(agents=aeas, mode="threaded", fail_policy=ExecutorExceptionPolicies.log_only) try: runner.start(threaded=True) runner.join_thread( ) # for some reason on windows and python 3.7/3.7 keyboard interuption exception gets lost so run in threaded mode to catch keyboard interruped except KeyboardInterrupt: logger.info("Keyboard interrupt detected.") finally: runner.stop() return runner.num_failed
def test_fetch_twice_remote(): """Test fails on fetch if dir exists.""" with TemporaryDirectory() as tmp_dir: with cd(tmp_dir): name = "my_first_aea" runner = CliRunner() result = runner.invoke( cli, [ "--registry-path", PACKAGES_DIR, "fetch", "--local", "fetchai/my_first_aea", ], catch_exceptions=False, ) assert result.exit_code == 0, result.stdout assert os.path.exists(name) with pytest.raises( ClickException, match='Item "my_first_aea" already exists in target folder.', ): result = runner.invoke( cli, ["fetch", "--remote", "fetchai/my_first_aea"], standalone_mode=False, catch_exceptions=False, )
def set_runtime_mode_to_async(cls, agent_name: str) -> None: """Set runtime mode of the agent to async.""" with cd(agent_name): config_path = Path(cls.t, agent_name, DEFAULT_AEA_CONFIG_FILE) config = yaml.safe_load(open(config_path)) config.setdefault("runtime_mode", "async") yaml.safe_dump(config, open(config_path, "w"))
def _launch_threads(click_context: click.Context, agents: List[Path]) -> int: """ Launch many agents, multithreaded. :param agents: the click context. :param agents: list of paths to agent projects. :return: exit status """ aeas = [] # type: List[AEA] for agent_directory in agents: with cd(agent_directory): aeas.append(AEABuilder.from_aea_project(".").build()) threads = [Thread(target=agent.start) for agent in aeas] for t in threads: t.start() try: while sum([t.is_alive() for t in threads]) != 0: # exit when all threads are not alive. # done to avoid block on joins for t in threads: t.join(0.1) except KeyboardInterrupt: logger.info("Keyboard interrupt detected.") finally: for idx, agent in enumerate(aeas): if not agent.liveness.is_stopped: agent.stop() threads[idx].join() logger.info("Agent {} has been stopped.".format(agent.name)) return 0
def run_cli_command(cls, *args: str, cwd: str = ".", **kwargs: str) -> Result: """ Run AEA CLI command. :param args: CLI args :param cwd: the working directory from where to run the command. :param kwargs: other keyword arguments to click.CLIRunner.invoke. :raises AEATestingException: if command fails. :return: Result """ with cd(cwd): result = cls.runner.invoke( cli, [*CLI_LOG_OPTION, *args], standalone_mode=False, catch_exceptions=False, **kwargs, ) cls.last_cli_runner_result = result if result.exit_code != 0: # pragma: nocover raise AEATestingException( "Failed to execute AEA CLI command with args {}.\n" "Exit code: {}\nException: {}".format( args, result.exit_code, result.exception ) ) return result
def difference_to_fetched_agent(cls, public_id: str, agent_name: str) -> List[str]: """ Compare agent against the one fetched from public id. :param public_id: str public id :param agents_name: str agent name. :return: list of files differing in the projects """ def is_allowed_diff_in_agent_config( path_to_fetched_aea, path_to_manually_created_aea ) -> Tuple[bool, Dict[str, str], Dict[str, str]]: with open(os.path.join(path_to_fetched_aea, "aea-config.yaml"), "r") as file: content1 = yaml.full_load(file) with open( os.path.join(path_to_manually_created_aea, "aea-config.yaml"), "r") as file: content2 = yaml.full_load(file) content1c = copy.deepcopy(content1) for key, value in content1c.items(): if content2[key] == value: content1.pop(key) content2.pop(key) allowed_diff_keys = [ "aea_version", "author", "description", "version" ] result = all([key in allowed_diff_keys for key in content1.keys()]) result = result and all( [key in allowed_diff_keys for key in content2.keys()]) if result: return result, {}, {} else: return result, content1, content2 path_to_manually_created_aea = os.path.join(cls.t, agent_name) new_cwd = os.path.join(cls.t, "fetch_dir") os.mkdir(new_cwd) path_to_fetched_aea = os.path.join(new_cwd, agent_name) registry_tmp_dir = os.path.join(new_cwd, cls.packages_dir_path) shutil.copytree(str(cls.package_registry_src), str(registry_tmp_dir)) with cd(new_cwd): cls.run_cli_command("fetch", "--local", public_id, "--alias", agent_name) comp = dircmp(path_to_manually_created_aea, path_to_fetched_aea) file_diff = comp.diff_files result, diff1, diff2 = is_allowed_diff_in_agent_config( path_to_fetched_aea, path_to_manually_created_aea) if result: file_diff.remove("aea-config.yaml") # won't match! else: file_diff.append("Difference in aea-config.yaml: " + str(diff1) + " vs. " + str(diff2)) try: shutil.rmtree(new_cwd) except (OSError, IOError): pass return file_diff
def test_local_registry_update(): """Test local-registry-sync cli command.""" PACKAGES = [ PackageId(PackageType.CONNECTION, PublicId("fetchai", "local", "0.11.0")), PackageId(PackageType.AGENT, PublicId("fetchai", "my_first_aea", "0.10.0")), ] with TemporaryDirectory() as tmp_dir: for package_id in PACKAGES: package_dir = os.path.join( tmp_dir, package_id.public_id.author, str(package_id.package_type.to_plural()), package_id.public_id.name, ) os.makedirs(package_dir) fetch_package( str(package_id.package_type), public_id=package_id.public_id, cwd=tmp_dir, dest=package_dir, ) assert set(PACKAGES) == set([i[0] for i in enlist_packages(tmp_dir)]) runner = CliRunner() with cd(tmp_dir): # check intention to upgrade with patch( "aea.cli.local_registry_sync.replace_package" ) as replace_package_mock: result = runner.invoke( cli, ["-s", "local-registry-sync"], catch_exceptions=False ) assert result.exit_code == 0, result.stdout assert replace_package_mock.call_count == len(PACKAGES) # do actual upgrade result = runner.invoke( cli, ["-s", "local-registry-sync"], catch_exceptions=False ) assert result.exit_code == 0, result.stdout # check next update will do nothing with patch( "aea.cli.local_registry_sync.replace_package" ) as replace_package_mock: result = runner.invoke( cli, ["-s", "local-registry-sync"], catch_exceptions=False ) assert result.exit_code == 0, result.stdout assert replace_package_mock.call_count == 0 def sort_(packages): return sorted(packages, key=lambda x: str(x)) new_packages = [i[0] for i in enlist_packages(tmp_dir)] for new_package, old_package in zip(sort_(new_packages), sort_(PACKAGES)): assert new_package.public_id > old_package.public_id
def invoke(self, *args): """Call the cli command.""" with cd(self._get_cwd()): result = self.runner.invoke(cli, args, standalone_mode=False, catch_exceptions=False) return result
def load_config(self) -> AgentConfig: """Load AgentConfig from current directory.""" with cd(self._get_cwd()): agent_loader = self.loader() path = Path(DEFAULT_AEA_CONFIG_FILE) with path.open(mode="r", encoding="utf-8") as fp: agent_config = agent_loader.load(fp) return agent_config
def test_build_positive_aea(self): """Test build project-wide entrypoint, positive.""" with cd(self._get_cwd()): self.script_path.write_text("") with patch.object(self.builder.logger, "info") as info_mock: self.builder.call_all_build_entrypoints() info_mock.assert_any_call("Building AEA package...") info_mock.assert_any_call(RegexComparator("Running command '.*script.py .*'"))
def test_from_project(self): """Test builder set from project dir.""" self._add_dummy_skill_config() builder = AEABuilder.from_aea_project(Path(self._get_cwd())) with cd(self._get_cwd()): aea = builder.build() dummy_skill = aea.resources.get_skill(DUMMY_SKILL_PUBLIC_ID) assert dummy_skill is None, "Shouldn't have found the skill in Resources."
def load_agent(agent_dir: Union[PathLike, str]) -> AEA: """ Load AEA from directory. :param agent_dir: agent configuration directory :return: AEA instance """ with cd(agent_dir): return AEABuilder.from_aea_project(".").build()
def get_balance(self) -> int: """Get balance for current agent.""" with cd(self._get_cwd()): agent_config = AgentConfigManager.verify_or_create_private_keys( Path("."), substitude_env_vars=False, private_key_helper=private_key_verify_or_create, ).agent_config wallet = get_wallet_from_agent_config(agent_config) return int(try_get_balance(agent_config, wallet, self.LEDGER_ID))
def test_from_project(self): """Test builder set from project dir.""" self._add_dummy_skill_config() self.run_cli_command("issue-certificates", cwd=self._get_cwd()) builder = AEABuilder.from_aea_project(Path(self._get_cwd())) with cd(self._get_cwd()): builder.call_all_build_entrypoints() aea = builder.build() dummy_skill = aea.resources.get_skill(DUMMY_SKILL_PUBLIC_ID) assert dummy_skill is None, "Shouldn't have found the skill in Resources."
def test_from_project(self): """Test builder set from project dir.""" self.expected_input_file = "custom_input_file" self.expected_output_file = "custom_output_file" self._add_stub_connection_config() with pytest.raises( AEAEnforceError, match=r"Component \(protocol, some_author/non_existing_package:0.1.0\) not declared in the agent configuration.", ): with cd(self._get_cwd()): AEABuilder.from_aea_project(Path(self._get_cwd()))
def test_from_project(self): """Test builder set from project dir.""" self.expected_input_file = "custom_input_file" self.expected_output_file = "custom_output_file" self._add_stub_connection_config() builder = AEABuilder.from_aea_project(Path(self._get_cwd())) with cd(self._get_cwd()): aea = builder.build() assert aea.name == self.agent_name stub_connection_id = StubConnection.connection_id stub_connection = aea.resources.get_connection(stub_connection_id) assert stub_connection.configuration.config == dict( input_file=self.expected_input_file, output_file=self.expected_output_file )
def replace_private_key_in_file( cls, private_key: str, private_key_filepath: str = DEFAULT_PRIVATE_KEY_FILE ) -> None: """ Replace the private key in the provided file with the provided key. :param private_key: the private key :param private_key_filepath: the filepath to the private key file :return: None :raises: exception if file does not exist """ with cd(cls._get_cwd()): # pragma: nocover with open_file(private_key_filepath, "wt") as f: f.write(private_key)
def test_non_vendor_nothing_to_upgrade(self, *mocks): # pylint: disable=unused-argument """Test upgrade project dependencies not removed cause non vendor.""" with cd(self.agent_name): base_agent_items = set( ItemRemoveHelper(self.load_config()). get_agent_dependencies_with_reverse_dependencies().keys()) self.runner.invoke( # pylint: disable=no-member cli, ["--skip-consistency-check", "upgrade"], standalone_mode=False, catch_exceptions=False, ) agent_items = set( ItemRemoveHelper(self.load_config()). get_agent_dependencies_with_reverse_dependencies().keys()) assert base_agent_items == agent_items
def test_from_project(self): """Test builder set from project dir.""" self.new_behaviour_args = {"behaviour_arg_1": 42} self.new_handler_args = {"handler_arg_1": 42} self.new_model_args = {"model_arg_1": 42} self._add_dummy_skill_config() builder = AEABuilder.from_aea_project(Path(self._get_cwd())) with cd(self._get_cwd()): aea = builder.build() dummy_skill = aea.resources.get_skill(DUMMY_SKILL_PUBLIC_ID) dummy_behaviour = dummy_skill.behaviours["dummy"] assert dummy_behaviour.config == {"behaviour_arg_1": 42, "behaviour_arg_2": "2"} dummy_handler = dummy_skill.handlers["dummy"] assert dummy_handler.config == {"handler_arg_1": 42, "handler_arg_2": "2"} dummy_model = dummy_skill.models["dummy"] assert dummy_model.config == {"model_arg_1": 42, "model_arg_2": "2"}
def test_component_configuration_removed_from_agent_config(self): """Test component configuration removed from agent config.""" with cd(self._get_cwd()): self.run_cli_command( "add", "--local", self.ITEM_TYPE, str(self.ITEM_PUBLIC_ID) ) self.run_cli_command("add", "--local", "connection", "fetchai/http_server") self.runner.invoke( cli, [ "config", "set", "vendor.fetchai.connections.soef.config.api_key", "some_api_key", ], standalone_mode=False, catch_exceptions=False, ) self.runner.invoke( cli, [ "config", "set", "vendor.fetchai.connections.http_server.config.port", "9000", ], standalone_mode=False, catch_exceptions=False, ) config = self.load_config() assert config.component_configurations assert ( PackageId(self.ITEM_TYPE, self.ITEM_PUBLIC_ID) in config.component_configurations ) self.run_cli_command("remove", self.ITEM_TYPE, str(self.ITEM_PUBLIC_ID)) config = self.load_config() assert ( PackageId(self.ITEM_TYPE, self.ITEM_PUBLIC_ID) not in config.component_configurations ) assert config.component_configurations
def difference_to_fetched_agent(cls, public_id: str, agent_name: str) -> List[str]: """ Compare agent against the one fetched from public id. :param public_id: str public id :param agents_name: str agent name. :return: list of files differing in the projects """ def is_allowed_diff_in_agent_config( path_to_fetched_aea, path_to_manually_created_aea) -> bool: with open(os.path.join(path_to_fetched_aea, "aea-config.yaml"), "r") as file: content1 = yaml.full_load(file) with open( os.path.join(path_to_manually_created_aea, "aea-config.yaml"), "r") as file: content2 = yaml.full_load(file) diff_count = 0 for key, value in content1.items(): if content2[key] != value: diff_count += 1 # allow diff in aea_version, author, description and version return diff_count <= 4 path_to_manually_created_aea = os.path.join(cls.t, agent_name) new_cwd = os.path.join(cls.t, "fetch_dir") os.mkdir(new_cwd) path_to_fetched_aea = os.path.join(new_cwd, agent_name) registry_tmp_dir = os.path.join(new_cwd, cls.packages_dir_path) shutil.copytree(str(cls.package_registry_src), str(registry_tmp_dir)) with cd(new_cwd): cls.run_cli_command("fetch", "--local", public_id, "--alias", agent_name) comp = dircmp(path_to_manually_created_aea, path_to_fetched_aea) file_diff = comp.diff_files if is_allowed_diff_in_agent_config(path_to_fetched_aea, path_to_manually_created_aea): file_diff.remove("aea-config.yaml") # won't match! try: shutil.rmtree(new_cwd) except (OSError, IOError): pass return file_diff
def run_cli_command(cls, *args: str, cwd: str = ".") -> None: """ Run AEA CLI command. :param args: CLI args :param cwd: the working directory from where to run the command. :raises AEATestingException: if command fails. :return: None """ with cd(cwd): result = cls.runner.invoke(cli, [*CLI_LOG_OPTION, *args], standalone_mode=False) cls.last_cli_runner_result = result if result.exit_code != 0: raise AEATestingException( "Failed to execute AEA CLI command with args {}.\n" "Exit code: {}\nException: {}".format( args, result.exit_code, result.exception))
def test_clean_tarfiles_error(): """Test clean tarfiles wrapper in case of error.""" expected_message = "some exception" def func() -> str: """The function being wrapped.""" raise Exception(expected_message) wrapped = clean_tarfiles(func) with tempfile.TemporaryDirectory() as tempdir: with cd(tempdir): tarfile_path = Path(tempdir, "tarfile.tar.gz") tarfile_path.touch() with pytest.raises(Exception, match=expected_message): wrapped() assert not tarfile_path.exists()
def test_clean_tarfiles(): """Test clean tarfiles wrapper.""" expected_result = "result" def func() -> str: """The function being wrapped.""" return expected_result wrapped = clean_tarfiles(func) with tempfile.TemporaryDirectory() as tempdir: with cd(tempdir): tarfile_path = Path(tempdir, "tarfile.tar.gz") tarfile_path.touch() result = wrapped() assert not tarfile_path.exists() assert result == expected_result
def test_build_positive_package(self): """Test build package entrypoint, positive.""" with cd(self._get_cwd()): self.script_path.write_text("") # add mock configuration build entrypoint with patch.object(self.builder, "_package_dependency_manager") as _mock_mgr: mock_config = MagicMock( component_id=self.component_id, build_entrypoint=str(self.script_path), directory=".", build_directory="test", ) mock_values = MagicMock(return_value=[mock_config]) _mock_mgr._dependencies = MagicMock(values=mock_values) with patch.object(self.builder.logger, "info") as info_mock: self.builder.call_all_build_entrypoints() info_mock.assert_any_call(f"Building package {self.component_id}...") info_mock.assert_any_call(RegexComparator("Running command '.*script.py .*'"))
def _test_project(self, is_local: bool, skip_consistency_check: bool): """Test method to handle both local and remote registry.""" registry_path = os.path.join(ROOT_DIR, "packages") project = Project.load( self.t, self.project_public_id, is_local=is_local, registry_path=registry_path, skip_consistency_check=skip_consistency_check, ) assert os.path.exists(self.project_path) with cd(self.project_path): result = self.runner.invoke( cli, ["--skip-consistency-check", "config", "get", "agent.agent_name"], catch_exceptions=False, standalone_mode=False, ) assert self.project_public_id.name in result.output project.remove() assert not os.path.exists(self.project_path)
def test_get_wallet_from_ctx(self): """Test get_wallet_from_context.""" ctx = mock.Mock() with cd(self._get_cwd()): assert isinstance(get_wallet_from_context(ctx), Wallet)