aevum-cli 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/.gitignore +31 -31
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/PKG-INFO +1 -1
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/README.md +12 -12
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/pyproject.toml +66 -66
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/__init__.py +14 -14
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/__main__.py +9 -9
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/app.py +22 -22
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/commands/__init__.py +1 -1
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/commands/complication.py +60 -60
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/commands/conformance.py +46 -46
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/commands/server.py +92 -92
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/commands/store.py +51 -51
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/commands/version.py +32 -32
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/tests/test_cli.py +99 -99
- {aevum_cli-0.2.0 → aevum_cli-0.3.0}/src/aevum/cli/py.typed +0 -0
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
# Python
|
|
2
|
-
__pycache__/
|
|
3
|
-
*.pyc
|
|
4
|
-
*.pyo
|
|
5
|
-
*.pyd
|
|
6
|
-
.venv/
|
|
7
|
-
*.egg-info/
|
|
8
|
-
|
|
9
|
-
# Build
|
|
10
|
-
dist/
|
|
11
|
-
build/
|
|
12
|
-
|
|
13
|
-
# Tools
|
|
14
|
-
.mypy_cache/
|
|
15
|
-
.ruff_cache/
|
|
16
|
-
.pytest_cache/
|
|
17
|
-
.hypothesis/
|
|
18
|
-
|
|
19
|
-
# IDE
|
|
20
|
-
.vscode/
|
|
21
|
-
.idea/
|
|
22
|
-
*.swp
|
|
23
|
-
*.swo
|
|
24
|
-
|
|
25
|
-
# OS
|
|
26
|
-
.DS_Store
|
|
27
|
-
Thumbs.db
|
|
28
|
-
|
|
29
|
-
# Verify scripts (run locally, never commit)
|
|
30
|
-
verify_phase*.py
|
|
31
|
-
scripts/verify_phase*.py
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.venv/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
|
|
9
|
+
# Build
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
|
|
13
|
+
# Tools
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.hypothesis/
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.vscode/
|
|
21
|
+
.idea/
|
|
22
|
+
*.swp
|
|
23
|
+
*.swo
|
|
24
|
+
|
|
25
|
+
# OS
|
|
26
|
+
.DS_Store
|
|
27
|
+
Thumbs.db
|
|
28
|
+
|
|
29
|
+
# Verify scripts (run locally, never commit)
|
|
30
|
+
verify_phase*.py
|
|
31
|
+
scripts/verify_phase*.py
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# aevum-cli
|
|
2
|
-
|
|
3
|
-
Command-line interface for Aevum — start the server, manage store migrations, and inspect complication state.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
pip install aevum-cli
|
|
7
|
-
aevum server start --graph memory
|
|
8
|
-
aevum store migrate postgres:<dsn>
|
|
9
|
-
aevum version
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
See the [main repository README](https://github.com/aevum-labs/aevum) for full usage.
|
|
1
|
+
# aevum-cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for Aevum — start the server, manage store migrations, and inspect complication state.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install aevum-cli
|
|
7
|
+
aevum server start --graph memory
|
|
8
|
+
aevum store migrate postgres:<dsn>
|
|
9
|
+
aevum version
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
See the [main repository README](https://github.com/aevum-labs/aevum) for full usage.
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "aevum-cli"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "Aevum -- command-line interface for operating Aevum nodes."
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.11"
|
|
7
|
-
license = { text = "Apache-2.0" }
|
|
8
|
-
classifiers = [
|
|
9
|
-
"Development Status :: 3 - Alpha",
|
|
10
|
-
"Intended Audience :: Developers",
|
|
11
|
-
"License :: OSI Approved :: Apache Software License",
|
|
12
|
-
"Programming Language :: Python :: 3.11",
|
|
13
|
-
"Typing :: Typed",
|
|
14
|
-
]
|
|
15
|
-
dependencies = [
|
|
16
|
-
"aevum-core",
|
|
17
|
-
"aevum-server",
|
|
18
|
-
"typer[all]>=0.12",
|
|
19
|
-
"uvicorn[standard]>=0.30",
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
[project.scripts]
|
|
23
|
-
aevum = "aevum.cli.__main__:app"
|
|
24
|
-
|
|
25
|
-
[project.urls]
|
|
26
|
-
Homepage = "https://aevum.build"
|
|
27
|
-
Repository = "https://github.com/aevum-labs/aevum"
|
|
28
|
-
|
|
29
|
-
[build-system]
|
|
30
|
-
requires = ["hatchling"]
|
|
31
|
-
build-backend = "hatchling.build"
|
|
32
|
-
|
|
33
|
-
[tool.hatch.build.targets.wheel]
|
|
34
|
-
packages = ["src/aevum"]
|
|
35
|
-
|
|
36
|
-
[tool.uv.sources]
|
|
37
|
-
aevum-core = { workspace = true }
|
|
38
|
-
aevum-server = { workspace = true }
|
|
39
|
-
|
|
40
|
-
[tool.pytest.ini_options]
|
|
41
|
-
testpaths = ["tests"]
|
|
42
|
-
asyncio_mode = "auto"
|
|
43
|
-
addopts = "--tb=short"
|
|
44
|
-
pythonpath = ["src", "tests"]
|
|
45
|
-
|
|
46
|
-
[tool.mypy]
|
|
47
|
-
strict = true
|
|
48
|
-
python_version = "3.11"
|
|
49
|
-
mypy_path = "src"
|
|
50
|
-
explicit_package_bases = true
|
|
51
|
-
ignore_missing_imports = true
|
|
52
|
-
|
|
53
|
-
[tool.ruff]
|
|
54
|
-
line-length = 130
|
|
55
|
-
|
|
56
|
-
[tool.ruff.lint]
|
|
57
|
-
select = ["E", "F", "UP", "B", "SIM", "I", "ANN"]
|
|
58
|
-
ignore = ["ANN401"]
|
|
59
|
-
|
|
60
|
-
[project.optional-dependencies]
|
|
61
|
-
dev = [
|
|
62
|
-
"pytest>=8.0",
|
|
63
|
-
"pytest-asyncio>=0.23",
|
|
64
|
-
"mypy>=1.10",
|
|
65
|
-
"ruff>=0.9",
|
|
66
|
-
]
|
|
1
|
+
[project]
|
|
2
|
+
name = "aevum-cli"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Aevum -- command-line interface for operating Aevum nodes."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = { text = "Apache-2.0" }
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Development Status :: 3 - Alpha",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"License :: OSI Approved :: Apache Software License",
|
|
12
|
+
"Programming Language :: Python :: 3.11",
|
|
13
|
+
"Typing :: Typed",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"aevum-core",
|
|
17
|
+
"aevum-server",
|
|
18
|
+
"typer[all]>=0.12",
|
|
19
|
+
"uvicorn[standard]>=0.30",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
aevum = "aevum.cli.__main__:app"
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://aevum.build"
|
|
27
|
+
Repository = "https://github.com/aevum-labs/aevum"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel]
|
|
34
|
+
packages = ["src/aevum"]
|
|
35
|
+
|
|
36
|
+
[tool.uv.sources]
|
|
37
|
+
aevum-core = { workspace = true }
|
|
38
|
+
aevum-server = { workspace = true }
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
testpaths = ["tests"]
|
|
42
|
+
asyncio_mode = "auto"
|
|
43
|
+
addopts = "--tb=short"
|
|
44
|
+
pythonpath = ["src", "tests"]
|
|
45
|
+
|
|
46
|
+
[tool.mypy]
|
|
47
|
+
strict = true
|
|
48
|
+
python_version = "3.11"
|
|
49
|
+
mypy_path = "src"
|
|
50
|
+
explicit_package_bases = true
|
|
51
|
+
ignore_missing_imports = true
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 130
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["E", "F", "UP", "B", "SIM", "I", "ANN"]
|
|
58
|
+
ignore = ["ANN401"]
|
|
59
|
+
|
|
60
|
+
[project.optional-dependencies]
|
|
61
|
+
dev = [
|
|
62
|
+
"pytest>=8.0",
|
|
63
|
+
"pytest-asyncio>=0.23",
|
|
64
|
+
"mypy>=1.10",
|
|
65
|
+
"ruff>=0.9",
|
|
66
|
+
]
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
"""
|
|
2
|
-
aevum.cli -- Command-line interface for operating Aevum nodes.
|
|
3
|
-
|
|
4
|
-
Usage: aevum <command> [options]
|
|
5
|
-
|
|
6
|
-
Commands:
|
|
7
|
-
version Print installed package versions
|
|
8
|
-
server start Start the HTTP API server
|
|
9
|
-
store migrate Migrate between graph backends
|
|
10
|
-
complication Manage installed complications
|
|
11
|
-
conformance run Run the conformance suite
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
__version__ = "0.1.0"
|
|
1
|
+
"""
|
|
2
|
+
aevum.cli -- Command-line interface for operating Aevum nodes.
|
|
3
|
+
|
|
4
|
+
Usage: aevum <command> [options]
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
version Print installed package versions
|
|
8
|
+
server start Start the HTTP API server
|
|
9
|
+
store migrate Migrate between graph backends
|
|
10
|
+
complication Manage installed complications
|
|
11
|
+
conformance run Run the conformance suite
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Entry point: aevum <command>
|
|
3
|
-
Invoked via [project.scripts] aevum = "aevum.cli.__main__:app"
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from aevum.cli.app import app
|
|
7
|
-
|
|
8
|
-
if __name__ == "__main__":
|
|
9
|
-
app()
|
|
1
|
+
"""
|
|
2
|
+
Entry point: aevum <command>
|
|
3
|
+
Invoked via [project.scripts] aevum = "aevum.cli.__main__:app"
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from aevum.cli.app import app
|
|
7
|
+
|
|
8
|
+
if __name__ == "__main__":
|
|
9
|
+
app()
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Top-level typer app. Sub-commands registered here.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
import typer
|
|
8
|
-
|
|
9
|
-
from aevum.cli.commands import complication, conformance, server, store, version
|
|
10
|
-
|
|
11
|
-
app = typer.Typer(
|
|
12
|
-
name="aevum",
|
|
13
|
-
help="Aevum context kernel -- command-line interface.",
|
|
14
|
-
no_args_is_help=True,
|
|
15
|
-
pretty_exceptions_enable=False,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
app.add_typer(server.app, name="server")
|
|
19
|
-
app.add_typer(store.app, name="store")
|
|
20
|
-
app.add_typer(complication.app, name="complication")
|
|
21
|
-
app.add_typer(conformance.app, name="conformance")
|
|
22
|
-
app.command(name="version")(version.version_command)
|
|
1
|
+
"""
|
|
2
|
+
Top-level typer app. Sub-commands registered here.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from aevum.cli.commands import complication, conformance, server, store, version
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(
|
|
12
|
+
name="aevum",
|
|
13
|
+
help="Aevum context kernel -- command-line interface.",
|
|
14
|
+
no_args_is_help=True,
|
|
15
|
+
pretty_exceptions_enable=False,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
app.add_typer(server.app, name="server")
|
|
19
|
+
app.add_typer(store.app, name="store")
|
|
20
|
+
app.add_typer(complication.app, name="complication")
|
|
21
|
+
app.add_typer(conformance.app, name="conformance")
|
|
22
|
+
app.command(name="version")(version.version_command)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"""aevum.cli.commands -- sub-command modules."""
|
|
1
|
+
"""aevum.cli.commands -- sub-command modules."""
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
"""
|
|
2
|
-
aevum complication list/suspend/resume -- manage complications.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from typing import Annotated
|
|
8
|
-
|
|
9
|
-
import typer
|
|
10
|
-
|
|
11
|
-
app = typer.Typer(help="Manage installed complications.")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@app.command("list")
|
|
15
|
-
def list_complications(
|
|
16
|
-
graph: Annotated[str, typer.Option(help="Graph backend")] = "memory",
|
|
17
|
-
) -> None:
|
|
18
|
-
"""List all installed complications with state and health."""
|
|
19
|
-
from aevum.core.engine import Engine
|
|
20
|
-
engine = Engine()
|
|
21
|
-
complications = engine.list_complications()
|
|
22
|
-
if not complications:
|
|
23
|
-
typer.echo("No complications installed.")
|
|
24
|
-
return
|
|
25
|
-
typer.echo(f"Installed complications ({len(complications)}):")
|
|
26
|
-
for name, entry in complications.items():
|
|
27
|
-
state = entry["state"]
|
|
28
|
-
typer.echo(f" {name}: {state}")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@app.command("suspend")
|
|
32
|
-
def suspend(
|
|
33
|
-
name: Annotated[str, typer.Argument(help="Complication name to suspend")],
|
|
34
|
-
) -> None:
|
|
35
|
-
"""Suspend an ACTIVE complication."""
|
|
36
|
-
from aevum.core.engine import Engine
|
|
37
|
-
from aevum.core.exceptions import ComplicationError
|
|
38
|
-
engine = Engine()
|
|
39
|
-
try:
|
|
40
|
-
engine.suspend_complication(name)
|
|
41
|
-
typer.echo(f"Suspended: {name}")
|
|
42
|
-
except ComplicationError as e:
|
|
43
|
-
typer.echo(f"Error: {e}", err=True)
|
|
44
|
-
raise typer.Exit(code=1) from e
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@app.command("resume")
|
|
48
|
-
def resume(
|
|
49
|
-
name: Annotated[str, typer.Argument(help="Complication name to resume")],
|
|
50
|
-
) -> None:
|
|
51
|
-
"""Resume a SUSPENDED complication."""
|
|
52
|
-
from aevum.core.engine import Engine
|
|
53
|
-
from aevum.core.exceptions import ComplicationError
|
|
54
|
-
engine = Engine()
|
|
55
|
-
try:
|
|
56
|
-
engine.resume_complication(name)
|
|
57
|
-
typer.echo(f"Resumed: {name}")
|
|
58
|
-
except ComplicationError as e:
|
|
59
|
-
typer.echo(f"Error: {e}", err=True)
|
|
60
|
-
raise typer.Exit(code=1) from e
|
|
1
|
+
"""
|
|
2
|
+
aevum complication list/suspend/resume -- manage complications.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="Manage installed complications.")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("list")
|
|
15
|
+
def list_complications(
|
|
16
|
+
graph: Annotated[str, typer.Option(help="Graph backend")] = "memory",
|
|
17
|
+
) -> None:
|
|
18
|
+
"""List all installed complications with state and health."""
|
|
19
|
+
from aevum.core.engine import Engine
|
|
20
|
+
engine = Engine()
|
|
21
|
+
complications = engine.list_complications()
|
|
22
|
+
if not complications:
|
|
23
|
+
typer.echo("No complications installed.")
|
|
24
|
+
return
|
|
25
|
+
typer.echo(f"Installed complications ({len(complications)}):")
|
|
26
|
+
for name, entry in complications.items():
|
|
27
|
+
state = entry["state"]
|
|
28
|
+
typer.echo(f" {name}: {state}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@app.command("suspend")
|
|
32
|
+
def suspend(
|
|
33
|
+
name: Annotated[str, typer.Argument(help="Complication name to suspend")],
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Suspend an ACTIVE complication."""
|
|
36
|
+
from aevum.core.engine import Engine
|
|
37
|
+
from aevum.core.exceptions import ComplicationError
|
|
38
|
+
engine = Engine()
|
|
39
|
+
try:
|
|
40
|
+
engine.suspend_complication(name)
|
|
41
|
+
typer.echo(f"Suspended: {name}")
|
|
42
|
+
except ComplicationError as e:
|
|
43
|
+
typer.echo(f"Error: {e}", err=True)
|
|
44
|
+
raise typer.Exit(code=1) from e
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command("resume")
|
|
48
|
+
def resume(
|
|
49
|
+
name: Annotated[str, typer.Argument(help="Complication name to resume")],
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Resume a SUSPENDED complication."""
|
|
52
|
+
from aevum.core.engine import Engine
|
|
53
|
+
from aevum.core.exceptions import ComplicationError
|
|
54
|
+
engine = Engine()
|
|
55
|
+
try:
|
|
56
|
+
engine.resume_complication(name)
|
|
57
|
+
typer.echo(f"Resumed: {name}")
|
|
58
|
+
except ComplicationError as e:
|
|
59
|
+
typer.echo(f"Error: {e}", err=True)
|
|
60
|
+
raise typer.Exit(code=1) from e
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
"""
|
|
2
|
-
aevum conformance run -- run the conformance suite.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
import subprocess
|
|
8
|
-
from typing import Annotated
|
|
9
|
-
|
|
10
|
-
import typer
|
|
11
|
-
|
|
12
|
-
app = typer.Typer(help="Run the Aevum conformance suite.")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@app.command("run")
|
|
16
|
-
def run(
|
|
17
|
-
impl: Annotated[
|
|
18
|
-
str,
|
|
19
|
-
typer.Option(help="Python import path to AevumProtocol implementation"),
|
|
20
|
-
] = "aevum.core.engine:Engine",
|
|
21
|
-
verbose: Annotated[bool, typer.Option("--verbose", "-v")] = False,
|
|
22
|
-
) -> None:
|
|
23
|
-
"""
|
|
24
|
-
Run the aevum-conformance suite against an implementation.
|
|
25
|
-
|
|
26
|
-
Requires aevum-conformance to be installed separately:
|
|
27
|
-
pip install aevum-conformance
|
|
28
|
-
"""
|
|
29
|
-
try:
|
|
30
|
-
import importlib
|
|
31
|
-
importlib.import_module("conformance")
|
|
32
|
-
except ImportError:
|
|
33
|
-
typer.echo(
|
|
34
|
-
"aevum-conformance is not installed. "
|
|
35
|
-
"Install from: github.com/aevum-labs/aevum-conformance",
|
|
36
|
-
err=True,
|
|
37
|
-
)
|
|
38
|
-
raise typer.Exit(code=1) from None
|
|
39
|
-
|
|
40
|
-
args = ["python", "-m", "pytest", "conformance/"]
|
|
41
|
-
if verbose:
|
|
42
|
-
args.append("-v")
|
|
43
|
-
|
|
44
|
-
typer.echo(f"Running conformance suite against: {impl}")
|
|
45
|
-
result = subprocess.run(args)
|
|
46
|
-
raise typer.Exit(code=result.returncode)
|
|
1
|
+
"""
|
|
2
|
+
aevum conformance run -- run the conformance suite.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="Run the Aevum conformance suite.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command("run")
|
|
16
|
+
def run(
|
|
17
|
+
impl: Annotated[
|
|
18
|
+
str,
|
|
19
|
+
typer.Option(help="Python import path to AevumProtocol implementation"),
|
|
20
|
+
] = "aevum.core.engine:Engine",
|
|
21
|
+
verbose: Annotated[bool, typer.Option("--verbose", "-v")] = False,
|
|
22
|
+
) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Run the aevum-conformance suite against an implementation.
|
|
25
|
+
|
|
26
|
+
Requires aevum-conformance to be installed separately:
|
|
27
|
+
pip install aevum-conformance
|
|
28
|
+
"""
|
|
29
|
+
try:
|
|
30
|
+
import importlib
|
|
31
|
+
importlib.import_module("conformance")
|
|
32
|
+
except ImportError:
|
|
33
|
+
typer.echo(
|
|
34
|
+
"aevum-conformance is not installed. "
|
|
35
|
+
"Install from: github.com/aevum-labs/aevum-conformance",
|
|
36
|
+
err=True,
|
|
37
|
+
)
|
|
38
|
+
raise typer.Exit(code=1) from None
|
|
39
|
+
|
|
40
|
+
args = ["python", "-m", "pytest", "conformance/"]
|
|
41
|
+
if verbose:
|
|
42
|
+
args.append("-v")
|
|
43
|
+
|
|
44
|
+
typer.echo(f"Running conformance suite against: {impl}")
|
|
45
|
+
result = subprocess.run(args)
|
|
46
|
+
raise typer.Exit(code=result.returncode)
|
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
"""
|
|
2
|
-
aevum server start -- start the Aevum HTTP API server.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from typing import TYPE_CHECKING, Annotated
|
|
8
|
-
|
|
9
|
-
import typer
|
|
10
|
-
import uvicorn
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from aevum.core.engine import Engine
|
|
14
|
-
|
|
15
|
-
app = typer.Typer(help="Manage the Aevum HTTP API server.")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@app.command("start")
|
|
19
|
-
def start(
|
|
20
|
-
host: Annotated[str, typer.Option(help="Bind host")] = "0.0.0.0",
|
|
21
|
-
port: Annotated[int, typer.Option(help="Bind port")] = 8000,
|
|
22
|
-
workers: Annotated[int, typer.Option(help="Number of uvicorn workers")] = 1,
|
|
23
|
-
graph: Annotated[
|
|
24
|
-
str,
|
|
25
|
-
typer.Option(
|
|
26
|
-
help="Graph backend. Options: memory | oxigraph:<path> | postgres:<dsn>"
|
|
27
|
-
),
|
|
28
|
-
] = "memory",
|
|
29
|
-
api_key: Annotated[
|
|
30
|
-
str | None,
|
|
31
|
-
typer.Option(envvar="AEVUM_API_KEY", help="API key (overrides AEVUM_API_KEY env var)"),
|
|
32
|
-
] = None,
|
|
33
|
-
reload: Annotated[bool, typer.Option(help="Enable auto-reload (dev only)")] = False,
|
|
34
|
-
) -> None:
|
|
35
|
-
"""Start the Aevum HTTP API server."""
|
|
36
|
-
import os
|
|
37
|
-
|
|
38
|
-
if api_key:
|
|
39
|
-
os.environ["AEVUM_API_KEY"] = api_key
|
|
40
|
-
|
|
41
|
-
engine = _build_engine(graph)
|
|
42
|
-
|
|
43
|
-
from aevum.server.app import create_app
|
|
44
|
-
from aevum.server.core.config import Settings
|
|
45
|
-
|
|
46
|
-
settings_with_overrides = Settings(
|
|
47
|
-
host=host,
|
|
48
|
-
port=port,
|
|
49
|
-
)
|
|
50
|
-
app_instance = create_app(engine=engine, settings=settings_with_overrides)
|
|
51
|
-
|
|
52
|
-
typer.echo(f"Starting Aevum server on {host}:{port} (graph={graph})")
|
|
53
|
-
uvicorn.run(
|
|
54
|
-
app_instance,
|
|
55
|
-
host=host,
|
|
56
|
-
port=port,
|
|
57
|
-
workers=workers if not reload else 1,
|
|
58
|
-
reload=reload,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def _build_engine(graph: str) -> Engine:
|
|
63
|
-
"""Build an Engine from the --graph flag."""
|
|
64
|
-
from aevum.core.engine import Engine
|
|
65
|
-
|
|
66
|
-
if graph == "memory":
|
|
67
|
-
typer.echo("Graph backend: in-memory (dev only -- data lost on restart)")
|
|
68
|
-
return Engine()
|
|
69
|
-
|
|
70
|
-
if graph.startswith("oxigraph:"):
|
|
71
|
-
path = graph[len("oxigraph:"):]
|
|
72
|
-
from aevum.store.oxigraph import OxigraphStore
|
|
73
|
-
typer.echo(f"Graph backend: Oxigraph at {path}")
|
|
74
|
-
return Engine(graph_store=OxigraphStore(path=path))
|
|
75
|
-
|
|
76
|
-
if graph.startswith("postgres:"):
|
|
77
|
-
dsn = graph[len("postgres:"):]
|
|
78
|
-
try:
|
|
79
|
-
import psycopg
|
|
80
|
-
from aevum.store.postgres import PostgresStore
|
|
81
|
-
from aevum.store.postgres.store import initialize_schema
|
|
82
|
-
conn = psycopg.connect(dsn)
|
|
83
|
-
initialize_schema(conn)
|
|
84
|
-
typer.echo("Graph backend: PostgreSQL")
|
|
85
|
-
return Engine(graph_store=PostgresStore(conn))
|
|
86
|
-
except ImportError:
|
|
87
|
-
typer.echo("Error: aevum-store-postgres is not installed.", err=True)
|
|
88
|
-
raise typer.Exit(code=1) from None
|
|
89
|
-
|
|
90
|
-
typer.echo(f"Unknown graph backend: {graph!r}", err=True)
|
|
91
|
-
typer.echo("Options: memory | oxigraph:<path> | postgres:<dsn>", err=True)
|
|
92
|
-
raise typer.Exit(code=1)
|
|
1
|
+
"""
|
|
2
|
+
aevum server start -- start the Aevum HTTP API server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
import uvicorn
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from aevum.core.engine import Engine
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(help="Manage the Aevum HTTP API server.")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command("start")
|
|
19
|
+
def start(
|
|
20
|
+
host: Annotated[str, typer.Option(help="Bind host")] = "0.0.0.0",
|
|
21
|
+
port: Annotated[int, typer.Option(help="Bind port")] = 8000,
|
|
22
|
+
workers: Annotated[int, typer.Option(help="Number of uvicorn workers")] = 1,
|
|
23
|
+
graph: Annotated[
|
|
24
|
+
str,
|
|
25
|
+
typer.Option(
|
|
26
|
+
help="Graph backend. Options: memory | oxigraph:<path> | postgres:<dsn>"
|
|
27
|
+
),
|
|
28
|
+
] = "memory",
|
|
29
|
+
api_key: Annotated[
|
|
30
|
+
str | None,
|
|
31
|
+
typer.Option(envvar="AEVUM_API_KEY", help="API key (overrides AEVUM_API_KEY env var)"),
|
|
32
|
+
] = None,
|
|
33
|
+
reload: Annotated[bool, typer.Option(help="Enable auto-reload (dev only)")] = False,
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Start the Aevum HTTP API server."""
|
|
36
|
+
import os
|
|
37
|
+
|
|
38
|
+
if api_key:
|
|
39
|
+
os.environ["AEVUM_API_KEY"] = api_key
|
|
40
|
+
|
|
41
|
+
engine = _build_engine(graph)
|
|
42
|
+
|
|
43
|
+
from aevum.server.app import create_app
|
|
44
|
+
from aevum.server.core.config import Settings
|
|
45
|
+
|
|
46
|
+
settings_with_overrides = Settings(
|
|
47
|
+
host=host,
|
|
48
|
+
port=port,
|
|
49
|
+
)
|
|
50
|
+
app_instance = create_app(engine=engine, settings=settings_with_overrides)
|
|
51
|
+
|
|
52
|
+
typer.echo(f"Starting Aevum server on {host}:{port} (graph={graph})")
|
|
53
|
+
uvicorn.run(
|
|
54
|
+
app_instance,
|
|
55
|
+
host=host,
|
|
56
|
+
port=port,
|
|
57
|
+
workers=workers if not reload else 1,
|
|
58
|
+
reload=reload,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _build_engine(graph: str) -> Engine:
|
|
63
|
+
"""Build an Engine from the --graph flag."""
|
|
64
|
+
from aevum.core.engine import Engine
|
|
65
|
+
|
|
66
|
+
if graph == "memory":
|
|
67
|
+
typer.echo("Graph backend: in-memory (dev only -- data lost on restart)")
|
|
68
|
+
return Engine()
|
|
69
|
+
|
|
70
|
+
if graph.startswith("oxigraph:"):
|
|
71
|
+
path = graph[len("oxigraph:"):]
|
|
72
|
+
from aevum.store.oxigraph import OxigraphStore
|
|
73
|
+
typer.echo(f"Graph backend: Oxigraph at {path}")
|
|
74
|
+
return Engine(graph_store=OxigraphStore(path=path))
|
|
75
|
+
|
|
76
|
+
if graph.startswith("postgres:"):
|
|
77
|
+
dsn = graph[len("postgres:"):]
|
|
78
|
+
try:
|
|
79
|
+
import psycopg
|
|
80
|
+
from aevum.store.postgres import PostgresStore
|
|
81
|
+
from aevum.store.postgres.store import initialize_schema
|
|
82
|
+
conn = psycopg.connect(dsn)
|
|
83
|
+
initialize_schema(conn)
|
|
84
|
+
typer.echo("Graph backend: PostgreSQL")
|
|
85
|
+
return Engine(graph_store=PostgresStore(conn))
|
|
86
|
+
except ImportError:
|
|
87
|
+
typer.echo("Error: aevum-store-postgres is not installed.", err=True)
|
|
88
|
+
raise typer.Exit(code=1) from None
|
|
89
|
+
|
|
90
|
+
typer.echo(f"Unknown graph backend: {graph!r}", err=True)
|
|
91
|
+
typer.echo("Options: memory | oxigraph:<path> | postgres:<dsn>", err=True)
|
|
92
|
+
raise typer.Exit(code=1)
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
"""
|
|
2
|
-
aevum store migrate -- migrate between graph backends.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from typing import Annotated
|
|
8
|
-
|
|
9
|
-
import typer
|
|
10
|
-
|
|
11
|
-
app = typer.Typer(help="Manage graph store backends.")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@app.command("migrate")
|
|
15
|
-
def migrate(
|
|
16
|
-
from_backend: Annotated[str, typer.Option("--from", help="Source backend (oxigraph:<path>)")] = "",
|
|
17
|
-
to_backend: Annotated[str, typer.Option("--to", help="Target backend (postgres:<dsn>)")] = "",
|
|
18
|
-
) -> None:
|
|
19
|
-
"""Migrate graph data between backends."""
|
|
20
|
-
if not from_backend or not to_backend:
|
|
21
|
-
typer.echo("Both --from and --to are required.", err=True)
|
|
22
|
-
raise typer.Exit(code=1)
|
|
23
|
-
|
|
24
|
-
if not from_backend.startswith("oxigraph:"):
|
|
25
|
-
typer.echo(f"Unsupported source backend: {from_backend!r}", err=True)
|
|
26
|
-
typer.echo("Currently supported source: oxigraph:<path>", err=True)
|
|
27
|
-
raise typer.Exit(code=1)
|
|
28
|
-
|
|
29
|
-
if not to_backend.startswith("postgres:"):
|
|
30
|
-
typer.echo(f"Unsupported target backend: {to_backend!r}", err=True)
|
|
31
|
-
typer.echo("Currently supported target: postgres:<dsn>", err=True)
|
|
32
|
-
raise typer.Exit(code=1)
|
|
33
|
-
|
|
34
|
-
try:
|
|
35
|
-
from aevum.store.postgres.store import migrate_from_oxigraph
|
|
36
|
-
except ImportError:
|
|
37
|
-
typer.echo("Error: aevum-store-postgres is not installed.", err=True)
|
|
38
|
-
raise typer.Exit(code=1) from None
|
|
39
|
-
|
|
40
|
-
oxigraph_path = from_backend[len("oxigraph:"):]
|
|
41
|
-
postgres_dsn = to_backend[len("postgres:"):]
|
|
42
|
-
|
|
43
|
-
typer.echo(f"Migrating: {oxigraph_path} -> PostgreSQL")
|
|
44
|
-
try:
|
|
45
|
-
import psycopg
|
|
46
|
-
conn = psycopg.connect(postgres_dsn)
|
|
47
|
-
migrated = migrate_from_oxigraph(oxigraph_path, conn)
|
|
48
|
-
typer.echo(f"Migration complete: {migrated} entities transferred.")
|
|
49
|
-
except Exception as e:
|
|
50
|
-
typer.echo(f"Migration failed: {e}", err=True)
|
|
51
|
-
raise typer.Exit(code=1) from e
|
|
1
|
+
"""
|
|
2
|
+
aevum store migrate -- migrate between graph backends.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="Manage graph store backends.")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("migrate")
|
|
15
|
+
def migrate(
|
|
16
|
+
from_backend: Annotated[str, typer.Option("--from", help="Source backend (oxigraph:<path>)")] = "",
|
|
17
|
+
to_backend: Annotated[str, typer.Option("--to", help="Target backend (postgres:<dsn>)")] = "",
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Migrate graph data between backends."""
|
|
20
|
+
if not from_backend or not to_backend:
|
|
21
|
+
typer.echo("Both --from and --to are required.", err=True)
|
|
22
|
+
raise typer.Exit(code=1)
|
|
23
|
+
|
|
24
|
+
if not from_backend.startswith("oxigraph:"):
|
|
25
|
+
typer.echo(f"Unsupported source backend: {from_backend!r}", err=True)
|
|
26
|
+
typer.echo("Currently supported source: oxigraph:<path>", err=True)
|
|
27
|
+
raise typer.Exit(code=1)
|
|
28
|
+
|
|
29
|
+
if not to_backend.startswith("postgres:"):
|
|
30
|
+
typer.echo(f"Unsupported target backend: {to_backend!r}", err=True)
|
|
31
|
+
typer.echo("Currently supported target: postgres:<dsn>", err=True)
|
|
32
|
+
raise typer.Exit(code=1)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
from aevum.store.postgres.store import migrate_from_oxigraph
|
|
36
|
+
except ImportError:
|
|
37
|
+
typer.echo("Error: aevum-store-postgres is not installed.", err=True)
|
|
38
|
+
raise typer.Exit(code=1) from None
|
|
39
|
+
|
|
40
|
+
oxigraph_path = from_backend[len("oxigraph:"):]
|
|
41
|
+
postgres_dsn = to_backend[len("postgres:"):]
|
|
42
|
+
|
|
43
|
+
typer.echo(f"Migrating: {oxigraph_path} -> PostgreSQL")
|
|
44
|
+
try:
|
|
45
|
+
import psycopg
|
|
46
|
+
conn = psycopg.connect(postgres_dsn)
|
|
47
|
+
migrated = migrate_from_oxigraph(oxigraph_path, conn)
|
|
48
|
+
typer.echo(f"Migration complete: {migrated} entities transferred.")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
typer.echo(f"Migration failed: {e}", err=True)
|
|
51
|
+
raise typer.Exit(code=1) from e
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
"""
|
|
2
|
-
aevum version -- print installed package versions.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
8
|
-
|
|
9
|
-
import typer
|
|
10
|
-
|
|
11
|
-
_PACKAGES = [
|
|
12
|
-
"aevum-core",
|
|
13
|
-
"aevum-server",
|
|
14
|
-
"aevum-sdk",
|
|
15
|
-
"aevum-store-oxigraph",
|
|
16
|
-
"aevum-store-postgres",
|
|
17
|
-
"aevum-mcp",
|
|
18
|
-
"aevum-oidc",
|
|
19
|
-
"aevum-llm",
|
|
20
|
-
"aevum-cli",
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def version_command() -> None:
|
|
25
|
-
"""Print versions of all installed Aevum packages."""
|
|
26
|
-
typer.echo("Aevum package versions:")
|
|
27
|
-
for pkg in _PACKAGES:
|
|
28
|
-
try:
|
|
29
|
-
ver = version(pkg)
|
|
30
|
-
typer.echo(f" {pkg}: {ver}")
|
|
31
|
-
except PackageNotFoundError:
|
|
32
|
-
typer.echo(f" {pkg}: not installed")
|
|
1
|
+
"""
|
|
2
|
+
aevum version -- print installed package versions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
_PACKAGES = [
|
|
12
|
+
"aevum-core",
|
|
13
|
+
"aevum-server",
|
|
14
|
+
"aevum-sdk",
|
|
15
|
+
"aevum-store-oxigraph",
|
|
16
|
+
"aevum-store-postgres",
|
|
17
|
+
"aevum-mcp",
|
|
18
|
+
"aevum-oidc",
|
|
19
|
+
"aevum-llm",
|
|
20
|
+
"aevum-cli",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def version_command() -> None:
|
|
25
|
+
"""Print versions of all installed Aevum packages."""
|
|
26
|
+
typer.echo("Aevum package versions:")
|
|
27
|
+
for pkg in _PACKAGES:
|
|
28
|
+
try:
|
|
29
|
+
ver = version(pkg)
|
|
30
|
+
typer.echo(f" {pkg}: {ver}")
|
|
31
|
+
except PackageNotFoundError:
|
|
32
|
+
typer.echo(f" {pkg}: not installed")
|
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for aevum-cli commands.
|
|
3
|
-
Uses typer's CliRunner -- no real server, no real graph backend.
|
|
4
|
-
|
|
5
|
-
NO tests/__init__.py (standing rule).
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import re
|
|
11
|
-
|
|
12
|
-
from typer.testing import CliRunner
|
|
13
|
-
|
|
14
|
-
from aevum.cli.app import app
|
|
15
|
-
|
|
16
|
-
runner = CliRunner()
|
|
17
|
-
|
|
18
|
-
_ANSI = re.compile(r"\x1b\[[0-9;]*[mGKH]")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def plain(text: str) -> str:
|
|
22
|
-
"""Strip ANSI escape codes so assertions work regardless of FORCE_COLOR."""
|
|
23
|
-
return _ANSI.sub("", text)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def test_version_command() -> None:
|
|
27
|
-
result = runner.invoke(app, ["version"])
|
|
28
|
-
assert result.exit_code == 0
|
|
29
|
-
assert "aevum-core" in plain(result.output)
|
|
30
|
-
assert "aevum-cli" in plain(result.output)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_version_shows_not_installed_for_missing() -> None:
|
|
34
|
-
result = runner.invoke(app, ["version"])
|
|
35
|
-
assert "not installed" in plain(result.output) or result.exit_code == 0
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_server_start_help() -> None:
|
|
39
|
-
result = runner.invoke(app, ["server", "start", "--help"])
|
|
40
|
-
assert result.exit_code == 0
|
|
41
|
-
out = plain(result.output)
|
|
42
|
-
assert "--host" in out
|
|
43
|
-
assert "--port" in out
|
|
44
|
-
assert "--graph" in out
|
|
45
|
-
assert "--workers" in out
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def test_store_migrate_help() -> None:
|
|
49
|
-
result = runner.invoke(app, ["store", "migrate", "--help"])
|
|
50
|
-
assert result.exit_code == 0
|
|
51
|
-
out = plain(result.output)
|
|
52
|
-
assert "--from" in out
|
|
53
|
-
assert "--to" in out
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_complication_list_empty() -> None:
|
|
57
|
-
result = runner.invoke(app, ["complication", "list"])
|
|
58
|
-
assert result.exit_code == 0
|
|
59
|
-
assert "No complications installed" in plain(result.output)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def test_complication_list_help() -> None:
|
|
63
|
-
result = runner.invoke(app, ["complication", "list", "--help"])
|
|
64
|
-
assert result.exit_code == 0
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def test_complication_suspend_help() -> None:
|
|
68
|
-
result = runner.invoke(app, ["complication", "suspend", "--help"])
|
|
69
|
-
assert result.exit_code == 0
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_complication_resume_help() -> None:
|
|
73
|
-
result = runner.invoke(app, ["complication", "resume", "--help"])
|
|
74
|
-
assert result.exit_code == 0
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def test_conformance_run_help() -> None:
|
|
78
|
-
result = runner.invoke(app, ["conformance", "run", "--help"])
|
|
79
|
-
assert result.exit_code == 0
|
|
80
|
-
assert "--impl" in plain(result.output)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def test_store_migrate_requires_both_flags() -> None:
|
|
84
|
-
result = runner.invoke(app, ["store", "migrate"])
|
|
85
|
-
assert result.exit_code != 0
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def test_store_migrate_unsupported_source() -> None:
|
|
89
|
-
result = runner.invoke(app, ["store", "migrate", "--from", "memory:", "--to", "postgres:dsn"])
|
|
90
|
-
assert result.exit_code != 0
|
|
91
|
-
assert "Unsupported source" in plain(result.output)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_top_level_help() -> None:
|
|
95
|
-
result = runner.invoke(app, ["--help"])
|
|
96
|
-
assert result.exit_code == 0
|
|
97
|
-
out = plain(result.output)
|
|
98
|
-
for cmd in ["version", "server", "store", "complication", "conformance"]:
|
|
99
|
-
assert cmd in out
|
|
1
|
+
"""
|
|
2
|
+
Tests for aevum-cli commands.
|
|
3
|
+
Uses typer's CliRunner -- no real server, no real graph backend.
|
|
4
|
+
|
|
5
|
+
NO tests/__init__.py (standing rule).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
from typer.testing import CliRunner
|
|
13
|
+
|
|
14
|
+
from aevum.cli.app import app
|
|
15
|
+
|
|
16
|
+
runner = CliRunner()
|
|
17
|
+
|
|
18
|
+
_ANSI = re.compile(r"\x1b\[[0-9;]*[mGKH]")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def plain(text: str) -> str:
|
|
22
|
+
"""Strip ANSI escape codes so assertions work regardless of FORCE_COLOR."""
|
|
23
|
+
return _ANSI.sub("", text)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_version_command() -> None:
|
|
27
|
+
result = runner.invoke(app, ["version"])
|
|
28
|
+
assert result.exit_code == 0
|
|
29
|
+
assert "aevum-core" in plain(result.output)
|
|
30
|
+
assert "aevum-cli" in plain(result.output)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_version_shows_not_installed_for_missing() -> None:
|
|
34
|
+
result = runner.invoke(app, ["version"])
|
|
35
|
+
assert "not installed" in plain(result.output) or result.exit_code == 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_server_start_help() -> None:
|
|
39
|
+
result = runner.invoke(app, ["server", "start", "--help"])
|
|
40
|
+
assert result.exit_code == 0
|
|
41
|
+
out = plain(result.output)
|
|
42
|
+
assert "--host" in out
|
|
43
|
+
assert "--port" in out
|
|
44
|
+
assert "--graph" in out
|
|
45
|
+
assert "--workers" in out
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_store_migrate_help() -> None:
|
|
49
|
+
result = runner.invoke(app, ["store", "migrate", "--help"])
|
|
50
|
+
assert result.exit_code == 0
|
|
51
|
+
out = plain(result.output)
|
|
52
|
+
assert "--from" in out
|
|
53
|
+
assert "--to" in out
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_complication_list_empty() -> None:
|
|
57
|
+
result = runner.invoke(app, ["complication", "list"])
|
|
58
|
+
assert result.exit_code == 0
|
|
59
|
+
assert "No complications installed" in plain(result.output)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_complication_list_help() -> None:
|
|
63
|
+
result = runner.invoke(app, ["complication", "list", "--help"])
|
|
64
|
+
assert result.exit_code == 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_complication_suspend_help() -> None:
|
|
68
|
+
result = runner.invoke(app, ["complication", "suspend", "--help"])
|
|
69
|
+
assert result.exit_code == 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_complication_resume_help() -> None:
|
|
73
|
+
result = runner.invoke(app, ["complication", "resume", "--help"])
|
|
74
|
+
assert result.exit_code == 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_conformance_run_help() -> None:
|
|
78
|
+
result = runner.invoke(app, ["conformance", "run", "--help"])
|
|
79
|
+
assert result.exit_code == 0
|
|
80
|
+
assert "--impl" in plain(result.output)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_store_migrate_requires_both_flags() -> None:
|
|
84
|
+
result = runner.invoke(app, ["store", "migrate"])
|
|
85
|
+
assert result.exit_code != 0
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_store_migrate_unsupported_source() -> None:
|
|
89
|
+
result = runner.invoke(app, ["store", "migrate", "--from", "memory:", "--to", "postgres:dsn"])
|
|
90
|
+
assert result.exit_code != 0
|
|
91
|
+
assert "Unsupported source" in plain(result.output)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_top_level_help() -> None:
|
|
95
|
+
result = runner.invoke(app, ["--help"])
|
|
96
|
+
assert result.exit_code == 0
|
|
97
|
+
out = plain(result.output)
|
|
98
|
+
for cmd in ["version", "server", "store", "complication", "conformance"]:
|
|
99
|
+
assert cmd in out
|
|
File without changes
|