- instance.brand (env AGNES_INSTANCE_BRAND, default "Agnes") + instance.workspace_dir replace hard-coded "Agnes" / "~/Agnes" across /home, /setup, /setup-advanced, /login, /install, /me/debug, and the Claude Code clipboard setup script. Terraform-friendly env override; defaults preserve existing Agnes branding. - Explicit "create workspace folder" step on /home (OS-tabbed mkdir+cd) + same step baked into the clipboard script as step 2. Drops the implicit assumption that `agnes init --workspace .` lands in a sensibly-cd'd shell. - Final "Restart Claude Code" step in the setup script (unconditional, between connectors and Confirm) so freshly-installed plugins, MCP servers, and SessionStart hooks load on the next Claude Code session. - Asana reverted from hosted Remote MCP back to PAT + raw REST against app.asana.com/api/1.0. MCP envelope shape consumed ~5x tokens per call; the PAT path lets the agent read flat REST fields. Existing MCP registration is detected and the user is asked whether to remove it (default Y, with benefits listed: token cost, no third-party hop, no OAuth refresh dance, deterministic envelope shape). - Atlassian connector instructs picking the longest API-token expiry (today "1 year") to cut re-mint friction. No public query-parameter hook exists on id.atlassian.com to pre-select expiry, so the prompt documents the manual click and acknowledges that limitation. - Uniform ✅ / ❌ per-connector marker contract (Asana, GWS, Atlassian) for the Confirm summary to grep. Each connector now ends with a Claude-driven end-to-end test that uses Claude Code's own bash to exercise the stored credential and prints "✅ <Connector> integration verified — ..." (or the failure variant).
156 lines
6.9 KiB
Python
156 lines
6.9 KiB
Python
"""Tests for instance_config loading."""
|
|
import pytest
|
|
|
|
|
|
class TestInstanceConfig:
|
|
def test_missing_config_returns_defaults(self, tmp_path, monkeypatch):
|
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
|
monkeypatch.setenv("TESTING", "1")
|
|
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-key-minimum-32-characters!!")
|
|
from app.instance_config import get_instance_name
|
|
name = get_instance_name()
|
|
assert isinstance(name, str)
|
|
|
|
def test_reads_nested_instance_name(self, tmp_path, monkeypatch):
|
|
"""get_instance_name should read instance.name from YAML, not flat instance_name."""
|
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
|
monkeypatch.setenv("TESTING", "1")
|
|
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-key-minimum-32-characters!!")
|
|
|
|
state_dir = tmp_path / "state"
|
|
state_dir.mkdir(exist_ok=True)
|
|
(state_dir / "instance.yaml").write_text(
|
|
"instance:\n name: Acme Analytics\n subtitle: Data Team\n"
|
|
)
|
|
|
|
import importlib
|
|
import app.instance_config as mod
|
|
# Reset cached config to force reload
|
|
mod._instance_config = None
|
|
importlib.reload(mod)
|
|
|
|
assert mod.get_instance_name() == "Acme Analytics"
|
|
assert mod.get_instance_subtitle() == "Data Team"
|
|
|
|
# Cleanup: reset cache after test
|
|
mod._instance_config = None
|
|
|
|
|
|
class TestInstanceBrand:
|
|
"""Brand and workspace_dir resolution: env > YAML > default,
|
|
workspace_dir derives from brand when not explicitly set."""
|
|
|
|
def _reload(self, tmp_path, monkeypatch):
|
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
|
monkeypatch.setenv("TESTING", "1")
|
|
monkeypatch.setenv("JWT_SECRET_KEY", "test-secret-key-minimum-32-characters!!")
|
|
import importlib
|
|
import app.instance_config as mod
|
|
mod._instance_config = None
|
|
importlib.reload(mod)
|
|
return mod
|
|
|
|
def test_brand_defaults_to_agnes(self, tmp_path, monkeypatch):
|
|
monkeypatch.delenv("AGNES_INSTANCE_BRAND", raising=False)
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_instance_brand() == "Agnes"
|
|
mod._instance_config = None
|
|
|
|
def test_brand_from_yaml(self, tmp_path, monkeypatch):
|
|
monkeypatch.delenv("AGNES_INSTANCE_BRAND", raising=False)
|
|
state_dir = tmp_path / "state"
|
|
state_dir.mkdir(exist_ok=True)
|
|
(state_dir / "instance.yaml").write_text(
|
|
"instance:\n name: Acme\n brand: Foundry AI\n"
|
|
)
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_instance_brand() == "Foundry AI"
|
|
mod._instance_config = None
|
|
|
|
def test_brand_env_overrides_yaml(self, tmp_path, monkeypatch):
|
|
state_dir = tmp_path / "state"
|
|
state_dir.mkdir(exist_ok=True)
|
|
(state_dir / "instance.yaml").write_text(
|
|
"instance:\n name: Acme\n brand: FromYaml\n"
|
|
)
|
|
monkeypatch.setenv("AGNES_INSTANCE_BRAND", "FromEnv")
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_instance_brand() == "FromEnv"
|
|
mod._instance_config = None
|
|
|
|
def test_brand_empty_falls_back_to_default(self, tmp_path, monkeypatch):
|
|
# Empty env should not override the YAML/default to empty.
|
|
monkeypatch.setenv("AGNES_INSTANCE_BRAND", " ")
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_instance_brand() == "Agnes"
|
|
mod._instance_config = None
|
|
|
|
def test_workspace_dir_derives_from_brand(self, tmp_path, monkeypatch):
|
|
monkeypatch.delenv("AGNES_WORKSPACE_DIR_NAME", raising=False)
|
|
monkeypatch.setenv("AGNES_INSTANCE_BRAND", "Foundry AI")
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_workspace_dir_name() == "FoundryAI"
|
|
mod._instance_config = None
|
|
|
|
def test_workspace_dir_strips_all_non_alphanumeric(self, tmp_path, monkeypatch):
|
|
monkeypatch.delenv("AGNES_WORKSPACE_DIR_NAME", raising=False)
|
|
monkeypatch.setenv("AGNES_INSTANCE_BRAND", "ACME's Data!")
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_workspace_dir_name() == "ACMEsData"
|
|
mod._instance_config = None
|
|
|
|
def test_workspace_dir_default_when_brand_unset(self, tmp_path, monkeypatch):
|
|
monkeypatch.delenv("AGNES_WORKSPACE_DIR_NAME", raising=False)
|
|
monkeypatch.delenv("AGNES_INSTANCE_BRAND", raising=False)
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_workspace_dir_name() == "Agnes"
|
|
mod._instance_config = None
|
|
|
|
def test_workspace_dir_explicit_env_overrides_derivation(self, tmp_path, monkeypatch):
|
|
monkeypatch.setenv("AGNES_INSTANCE_BRAND", "Foundry AI")
|
|
monkeypatch.setenv("AGNES_WORKSPACE_DIR_NAME", "fdry")
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_workspace_dir_name() == "fdry"
|
|
mod._instance_config = None
|
|
|
|
def test_workspace_dir_explicit_yaml_overrides_derivation(self, tmp_path, monkeypatch):
|
|
monkeypatch.delenv("AGNES_WORKSPACE_DIR_NAME", raising=False)
|
|
monkeypatch.delenv("AGNES_INSTANCE_BRAND", raising=False)
|
|
state_dir = tmp_path / "state"
|
|
state_dir.mkdir(exist_ok=True)
|
|
(state_dir / "instance.yaml").write_text(
|
|
"instance:\n name: Acme\n brand: Foundry AI\n workspace_dir: fdry\n"
|
|
)
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
assert mod.get_workspace_dir_name() == "fdry"
|
|
mod._instance_config = None
|
|
|
|
def test_brand_flows_into_resolve_lines(self, tmp_path, monkeypatch):
|
|
"""Brand + workspace_dir substitute into the setup script lines."""
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
from app.web.setup_instructions import resolve_lines
|
|
joined = "\n".join(resolve_lines(
|
|
"agnes.whl",
|
|
instance_brand="Foundry AI",
|
|
workspace_dir="FoundryAI",
|
|
))
|
|
assert "Set up the Foundry AI CLI on this machine." in joined
|
|
assert "mkdir -p \"$HOME/FoundryAI\"" in joined
|
|
assert "Bootstrap your Foundry AI workspace" in joined
|
|
assert "Foundry AI workspace is ready" in joined
|
|
# No raw placeholders survive substitution.
|
|
assert "{instance_brand}" not in joined
|
|
assert "{workspace_dir}" not in joined
|
|
mod._instance_config = None
|
|
|
|
def test_default_brand_keeps_agnes_branding(self, tmp_path, monkeypatch):
|
|
"""Backwards-compat: callers that don't pass brand/workspace_dir
|
|
get the literal 'Agnes' / '~/Agnes' rendering."""
|
|
mod = self._reload(tmp_path, monkeypatch)
|
|
from app.web.setup_instructions import resolve_lines
|
|
joined = "\n".join(resolve_lines("agnes.whl"))
|
|
assert "Set up the Agnes CLI on this machine." in joined
|
|
assert "mkdir -p \"$HOME/Agnes\"" in joined
|
|
assert "Bootstrap your Agnes workspace" in joined
|
|
assert "Agnes workspace is ready" in joined
|
|
mod._instance_config = None
|