Follows the portal: the noun is "instance" everywhere the app says it — UI strings, CLI output, error details, docs and comments. The wire keys (`instance_id`, `instance_token`) and the hub route this calls move with it. An existing cloud.json is adopted rather than refused: without the key alias the dataclass fails to parse, which the caller swallows and reads as "never enrolled" instead of "reconnect". `instance_key` on a node type becomes `target_key`. It means the outside thing a node points at, which is a different sense of the word, and keeping both would put two meanings of "instance" in one codebase. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YrQnKV3bnQd4K342y8tKj
129 lines
4.2 KiB
Python
129 lines
4.2 KiB
Python
"""Which instance a command is for, and where its token lives.
|
|
|
|
A repository with its own venv gets its own engine, so "which one" is a fact
|
|
about the working directory rather than about the machine.
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from fluksio.sdk import client
|
|
|
|
|
|
@pytest.fixture
|
|
def elsewhere(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
|
"""A working directory with no instance above it."""
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "xdg"))
|
|
monkeypatch.setattr(client, "GLOBAL_DATA_DIR", tmp_path / "home" / ".fluksio")
|
|
return tmp_path
|
|
|
|
|
|
def test_a_project_local_instance_is_found_from_below(elsewhere: Path):
|
|
(elsewhere / ".fluksio").mkdir()
|
|
deep = elsewhere / "src" / "pkg" / "sub"
|
|
deep.mkdir(parents=True)
|
|
|
|
assert client.find_data_dir(deep) == elsewhere / ".fluksio"
|
|
assert client.config_path(client.data_dir(deep)).parent.name == ".fluksio"
|
|
|
|
|
|
def test_the_nearest_one_wins(elsewhere: Path):
|
|
"""An instance inside another belongs to the directory it is in."""
|
|
(elsewhere / ".fluksio").mkdir()
|
|
inner = elsewhere / "inner"
|
|
(inner / ".fluksio").mkdir(parents=True)
|
|
|
|
assert client.find_data_dir(inner) == inner / ".fluksio"
|
|
|
|
|
|
def test_with_none_above_it_the_shared_one_answers(elsewhere: Path):
|
|
assert client.find_data_dir() is None
|
|
assert client.data_dir() == elsewhere / "home" / ".fluksio"
|
|
|
|
|
|
def test_a_token_is_written_only_for_its_owner(elsewhere: Path):
|
|
path = client.write_config("http://127.0.0.1:8000", "abc", elsewhere / ".fluksio")
|
|
|
|
assert json.loads(path.read_text()) == {
|
|
"url": "http://127.0.0.1:8000",
|
|
"token": "abc",
|
|
}
|
|
# It is a credential sitting in somebody's project directory.
|
|
assert path.stat().st_mode & 0o077 == 0
|
|
# And one nothing can commit by accident.
|
|
assert (path.parent / ".gitignore").read_text().endswith("*\n")
|
|
|
|
|
|
def test_the_credential_beside_the_data_is_the_one_used(elsewhere: Path):
|
|
client.write_config("http://127.0.0.1:8131", "local", elsewhere / ".fluksio")
|
|
|
|
assert client._stored()["token"] == "local"
|
|
|
|
|
|
def test_a_login_from_before_instances_were_local_still_works(elsewhere: Path):
|
|
"""`fluksio login` wrote to XDG once; that must not stop answering."""
|
|
legacy = client._legacy_config_path()
|
|
legacy.parent.mkdir(parents=True)
|
|
legacy.write_text(json.dumps({"url": "http://elsewhere:8000", "token": "old"}))
|
|
|
|
assert client._stored()["token"] == "old"
|
|
|
|
|
|
def test_a_local_credential_wins_over_the_legacy_one(elsewhere: Path):
|
|
legacy = client._legacy_config_path()
|
|
legacy.parent.mkdir(parents=True)
|
|
legacy.write_text(json.dumps({"url": "http://elsewhere:8000", "token": "old"}))
|
|
client.write_config("http://127.0.0.1:8131", "local", elsewhere / ".fluksio")
|
|
|
|
assert client._stored()["token"] == "local"
|
|
|
|
|
|
def test_ignoring_itself_leaves_an_existing_gitignore_alone(elsewhere: Path):
|
|
directory = elsewhere / ".fluksio"
|
|
directory.mkdir()
|
|
(directory / ".gitignore").write_text("mine\n")
|
|
|
|
client.ignore_self(directory)
|
|
|
|
assert (directory / ".gitignore").read_text() == "mine\n"
|
|
|
|
|
|
def test_a_repository_whose_code_is_in_a_package_is_discovered(
|
|
elsewhere: Path, monkeypatch: pytest.MonkeyPatch
|
|
):
|
|
"""`fluksio sync` in a repo root has to find `myresearch/`, not just *.py.
|
|
|
|
This is the ordinary layout, and it is what `run` leans on to sync without
|
|
being told where to look.
|
|
"""
|
|
from fluksio.sdk import FLOWS
|
|
from fluksio.sdk.cli import discover
|
|
|
|
package = elsewhere / "mystudy"
|
|
package.mkdir()
|
|
(package / "__init__.py").write_text("")
|
|
(package / "pipeline.py").write_text(
|
|
"from fluksio import Flow, Port, node\n"
|
|
"\n"
|
|
"@node(provides=[Port('score', 'float')])\n"
|
|
"def scoring():\n"
|
|
" return {'score': 1.0}\n"
|
|
"\n"
|
|
"study = Flow('study', nodes=[scoring], outputs=['score'])\n"
|
|
)
|
|
# The things a repository root also holds, none of which is importable.
|
|
(elsewhere / ".venv").mkdir()
|
|
(elsewhere / ".fluksio").mkdir()
|
|
(elsewhere / "data").mkdir()
|
|
|
|
FLOWS.clear()
|
|
try:
|
|
found = discover(["."])
|
|
finally:
|
|
FLOWS.clear()
|
|
|
|
assert [flow.name for flow in found] == ["study"]
|