From f88725bfed3c06d8cae11bfbc95ac5e39dce90ab Mon Sep 17 00:00:00 2001 From: Cristian Marcos Martin Date: Fri, 27 Feb 2026 10:21:44 +0100 Subject: [PATCH 1/2] Add the list command --- cloudisk/cli/parser.py | 13 ++++++++++++- cloudisk/db/models/space.py | 17 ++++++++++++++++- cloudisk/fs/commands.py | 30 +++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/cloudisk/cli/parser.py b/cloudisk/cli/parser.py index 3352ef9..c6a3a1f 100644 --- a/cloudisk/cli/parser.py +++ b/cloudisk/cli/parser.py @@ -4,7 +4,13 @@ import typer from cloudisk.cli.vars import HEADER_ART -from cloudisk.fs.commands import create_space, init_cloudisk_root, link_path, unlink_path +from cloudisk.fs.commands import ( + create_space, + init_cloudisk_root, + link_path, + list_spaces, + unlink_path, +) from cloudisk.http import server from cloudisk.vars import CLOUDISK_ROOT @@ -54,6 +60,11 @@ def create( create_space(name, protect) +@app.command(help="Lists all created spaces") +def list(): + list_spaces() + + @app.command(help=f"Creates a symlink inside '{CLOUDISK_ROOT}'") def link( path: Annotated[ diff --git a/cloudisk/db/models/space.py b/cloudisk/db/models/space.py index 27919a8..42e78a1 100644 --- a/cloudisk/db/models/space.py +++ b/cloudisk/db/models/space.py @@ -1,5 +1,5 @@ from sqlalchemy.exc import IntegrityError -from sqlmodel import Field, Session, SQLModel +from sqlmodel import Field, Session, SQLModel, select from cloudisk.db.models.base import ModelManager @@ -59,3 +59,18 @@ def create(self, name: str, protect: bool) -> SpaceModel: session.refresh(space) return space + + def list(self) -> list[str]: + """ + List all instances of `SpaceModel`. + + Returns + ------- + list[str] + The names of the instances. + """ + with Session(self.engine) as session: + statement = select(self.model.name) + results = session.exec(statement) + + return results.all() diff --git a/cloudisk/fs/commands.py b/cloudisk/fs/commands.py index 302157c..32a17d1 100644 --- a/cloudisk/fs/commands.py +++ b/cloudisk/fs/commands.py @@ -1,11 +1,13 @@ import os from pathlib import Path +import typer + from cloudisk.db.models import Space from cloudisk.fs.utils import ask_remove_dir, ask_remove_path from cloudisk.logger import get_logger from cloudisk.tools.settings import Settings -from cloudisk.vars import CLOUDISK_ROOT +from cloudisk.vars import CLOUDISK_DB_FILE, CLOUDISK_ROOT logger = get_logger("cloudisk.fs") @@ -121,3 +123,29 @@ def create_space(name: str, protect: bool) -> None: Space().create(name=name, protect=protect) logger.info(f"Created the '{name}' space") + + +# TODO maybe a space is in the database but not found in ROOT +def list_spaces() -> None: + spaces = Space().list() + + if spaces: + typer.echo("Tracked spaces:") + for space in spaces: + typer.echo(f"- {space}") + + root = os.listdir(CLOUDISK_ROOT) + root = [x for x in root if x != CLOUDISK_DB_FILE] + + if len(root): + untracked = list(filter(lambda x: x not in spaces, root)) + + message = "Untracked spaces:" + if spaces: + message = "\n" + message + + typer.echo(message) + for space in untracked: + typer.echo(f"- {space}") + + return From 75804a75ebcf78522d340218a3bc7864a1ece5dd Mon Sep 17 00:00:00 2001 From: Cristian Marcos Martin Date: Fri, 27 Feb 2026 10:22:02 +0100 Subject: [PATCH 2/2] Tests for the list command --- tests/db/models/test_space.py | 11 +++++++++++ tests/fs/test_commands.py | 37 ++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/tests/db/models/test_space.py b/tests/db/models/test_space.py index 3844d09..fec26f4 100644 --- a/tests/db/models/test_space.py +++ b/tests/db/models/test_space.py @@ -29,3 +29,14 @@ def test_create_raises_AlreadyExists(): with pytest.raises(Space.AlreadyExists): manager.create(name="test", protect=True) + + +def test_list(): + manager = Space() + + manager.create(name="test", protect=True) + + result = manager.list() + execpted = ["test"] + + assert result == execpted diff --git a/tests/fs/test_commands.py b/tests/fs/test_commands.py index 2d5b03a..f4eb42a 100644 --- a/tests/fs/test_commands.py +++ b/tests/fs/test_commands.py @@ -1,16 +1,18 @@ import os import shutil from pathlib import Path -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest +from cloudisk.db.models.space import Space from cloudisk.fs import commands from cloudisk.fs.commands import ( _try_link, create_space, init_cloudisk_root, link_path, + list_spaces, unlink_path, ) @@ -25,6 +27,13 @@ def fake_root(tmp_path, monkeypatch) -> Path: return fake_path +@pytest.fixture +def mock_echo(monkeypatch): + echo_mock = MagicMock() + monkeypatch.setattr("cloudisk.fs.commands.typer.echo", echo_mock) + return echo_mock + + def test_init_cloudisk_root_ok(fake_root): fake_root.rmdir() @@ -222,3 +231,29 @@ def test_create_space_ask_remove_dir_is_False(fake_root): create_space(name=space_name, protect=True) assert space_path.exists() + + +def test_list_spaces_full(fake_root): + Space().create(name="test", protect=False) + + untracked = fake_root / "untracked" + untracked.mkdir() + + list_spaces() + + +def test_list_spaces_only_tracked(fake_root): + Space().create(name="test", protect=False) + + list_spaces() + + +def test_list_spaces_only_untracked(fake_root): + untracked = fake_root / "untracked" + untracked.mkdir() + + list_spaces() + + +def test_list_space_empty(): + list_spaces()