From 5a970fcec8f3c5a1c34f9daa35fcc71ada44d677 Mon Sep 17 00:00:00 2001 From: gogocat Date: Sun, 5 Apr 2026 09:51:36 +0300 Subject: [PATCH 1/2] fix(commands): 3 bugs found in live broker testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real broker test found 3 bugs that unit tests missed: - cache.py: keys() is sync, not async — fixed with iscoroutine check - destroy.py: broker has no destroyService — fallback to registry.del - load.py: broker has no addLocalService — use broker.register() Verified: ALL 9 commands work on live broker with real cacher + metrics. Load: dynamically loaded .py → registered → callable ✅ Destroy: removed service → confirmed gone from registry ✅ Co-Authored-By: Claude Opus 4.6 (1M context) --- src/moleculerpy_repl/commands/cache.py | 5 +++- src/moleculerpy_repl/commands/destroy.py | 32 ++++++++++++++---------- src/moleculerpy_repl/commands/load.py | 10 ++++++-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/moleculerpy_repl/commands/cache.py b/src/moleculerpy_repl/commands/cache.py index 82fcdba..16bd457 100644 --- a/src/moleculerpy_repl/commands/cache.py +++ b/src/moleculerpy_repl/commands/cache.py @@ -39,7 +39,10 @@ async def _list_keys(self, cacher: Any, args: ParsedArgs) -> CommandResult: try: if hasattr(cacher, "keys"): - keys = await cacher.keys(pattern) if pattern else await cacher.keys() + raw_keys = cacher.keys(pattern) if pattern else cacher.keys() + # keys() may be sync or async depending on cacher impl + import asyncio + keys = (await raw_keys) if asyncio.iscoroutine(raw_keys) else raw_keys else: return CommandResult( success=False, diff --git a/src/moleculerpy_repl/commands/destroy.py b/src/moleculerpy_repl/commands/destroy.py index f1c768f..bd0bd61 100644 --- a/src/moleculerpy_repl/commands/destroy.py +++ b/src/moleculerpy_repl/commands/destroy.py @@ -26,34 +26,40 @@ async def execute(self, broker: Any, args: ParsedArgs) -> CommandResult: service_name = args.positional[0] try: + # Try broker.destroyService first (Node.js pattern) if hasattr(broker, "destroyService"): - # Find the service instance by name service = None if hasattr(broker, "services"): service = broker.services.get(service_name) - + if service is None and hasattr(broker, "registry"): + service = broker.registry.__services__.get(service_name) if service is None: return CommandResult(success=False, error=f"Service '{service_name}' not found") - await broker.destroyService(service) - return CommandResult( - success=True, - output=f"Service '{service_name}' destroyed.", - ) + elif hasattr(broker, "registry"): + # Fallback: remove from registry directly + registry = broker.registry + if service_name not in registry.__services__: + return CommandResult(success=False, error=f"Service '{service_name}' not found") + del registry.__services__[service_name] + # Remove associated actions + registry.__actions__ = [a for a in registry.__actions__ if not a.name.startswith(f"{service_name}.")] + registry._actions_by_name = { + k: v for k, v in registry._actions_by_name.items() + if not k.startswith(f"{service_name}.") + } else: - return CommandResult( - success=False, - error="broker.destroyService is not supported in this version", - ) + return CommandResult(success=False, error="Cannot destroy: no registry access") + return CommandResult(success=True, output=f"Service '{service_name}' destroyed.") except Exception as e: return CommandResult(success=False, error=f"Destroy failed: {e}") def get_completions(self, broker: Any, text: str, line: str) -> list[str]: """Get service name completions.""" try: - if hasattr(broker, "services"): - return [name for name in broker.services if name.startswith(text)] + if hasattr(broker, "registry"): + return [n for n in broker.registry.__services__ if n.startswith(text)] except Exception: pass return [] diff --git a/src/moleculerpy_repl/commands/load.py b/src/moleculerpy_repl/commands/load.py index 09654ac..7780205 100644 --- a/src/moleculerpy_repl/commands/load.py +++ b/src/moleculerpy_repl/commands/load.py @@ -70,8 +70,14 @@ async def execute(self, broker: Any, args: ParsedArgs) -> CommandResult: error=f"No Service subclass found in {file_path.name}", ) - svc = service_class(broker) - await broker.addLocalService(svc) + svc = service_class() + # Use broker.register() — the standard MoleculerPy API + if hasattr(broker, "register"): + await broker.register(svc) + elif hasattr(broker, "addLocalService"): + await broker.addLocalService(svc) + else: + return CommandResult(success=False, error="Cannot register service: no register method") return CommandResult( success=True, From b7a8f9600b8ed600c596f8442ffba9940e657f9a Mon Sep 17 00:00:00 2001 From: gogocat Date: Sun, 5 Apr 2026 10:04:54 +0300 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20v0.3.1=20=E2=80=94=203=20bugs=20foun?= =?UTF-8?q?d=20by=20live=20broker=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cache.keys() sync, destroy registry fallback, load broker.register() Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bce7384..4958d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.1] - 2026-04-05 + +### Fixed +- **3 bugs found by live broker testing:** + - `cache keys`: sync/async mismatch — `cacher.keys()` is sync + - `destroy`: use `registry.__services__` fallback (broker has no `destroyService` yet) + - `load`: use `broker.register()` (broker has no `addLocalService`) + ## [0.3.0] - 2026-04-05 ### Added diff --git a/pyproject.toml b/pyproject.toml index ca9ca81..592d852 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "moleculerpy-repl" -version = "0.3.0" +version = "0.3.1" description = "Interactive CLI shell for MoleculerPy microservices framework" readme = "README.md" license = "MIT"