feat(template): add --store-credential to prepare-win + full docstring
- --store-credential: after snapshot, writes build-user credentials into the system keyring using the two-entry scheme (service:meta + service) that KeyringCredentialStore.get() reads. Works on Windows (Credential Manager) and Linux (SecretService / file backend). - --credential-target: keyring target name, default BuildVMGuest (matches config.guest_cred_target). - Rewrote module docstring: full prose documentation of both `template backup` and `template prepare-win` including all 12 provisioning steps. - 2 new tests covering --store-credential with custom and default targets. - mypy --strict clean; coverage 90.03%. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -915,3 +915,89 @@ def test_prepare_win_vm_shutdown_timeout(
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
assert "power off" in result.output
|
||||
|
||||
|
||||
def test_prepare_win_store_credential(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""--store-credential calls keyring.set_password with the two-entry scheme."""
|
||||
vmx = _setup_prepare_env(tmp_path, with_static_ip=False)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
backend = _make_backend([True, False])
|
||||
transport = _make_transport()
|
||||
_patch_prepare(monkeypatch, backend, transport)
|
||||
|
||||
stored: list[tuple[str, str, str]] = []
|
||||
|
||||
fake_keyring = types.SimpleNamespace(
|
||||
set_password=lambda svc, user, pw: stored.append((svc, user, pw))
|
||||
)
|
||||
monkeypatch.setattr(tmpl_mod, "keyring", fake_keyring, raising=False)
|
||||
|
||||
# Patch the import inside the function so it returns our fake module.
|
||||
import builtins
|
||||
real_import = builtins.__import__
|
||||
|
||||
def _fake_import(name: str, *args: object, **kwargs: object) -> object:
|
||||
if name == "keyring":
|
||||
return fake_keyring
|
||||
return real_import(name, *args, **kwargs) # type: ignore[arg-type]
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", _fake_import)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"template", "prepare-win",
|
||||
"--vmx", str(vmx),
|
||||
"--ip", "192.168.1.100",
|
||||
"--admin-password", "adminpass",
|
||||
"--build-password", "buildpass",
|
||||
"--store-credential",
|
||||
"--credential-target", "TestTarget",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0, result.output
|
||||
assert any(s[0] == "TestTarget:meta" for s in stored), "username meta-entry not stored"
|
||||
assert any(s[0] == "TestTarget" for s in stored), "password entry not stored"
|
||||
assert "Credentials stored" in result.output
|
||||
|
||||
|
||||
def test_prepare_win_store_credential_default_target(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Default credential target is BuildVMGuest."""
|
||||
vmx = _setup_prepare_env(tmp_path, with_static_ip=False)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
backend = _make_backend([True, False])
|
||||
transport = _make_transport()
|
||||
_patch_prepare(monkeypatch, backend, transport)
|
||||
|
||||
stored: list[tuple[str, str, str]] = []
|
||||
|
||||
import builtins
|
||||
real_import = builtins.__import__
|
||||
|
||||
def _fake_import(name: str, *args: object, **kwargs: object) -> object:
|
||||
if name == "keyring":
|
||||
return types.SimpleNamespace(
|
||||
set_password=lambda svc, user, pw: stored.append((svc, user, pw))
|
||||
)
|
||||
return real_import(name, *args, **kwargs) # type: ignore[arg-type]
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", _fake_import)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
cli,
|
||||
[
|
||||
"template", "prepare-win",
|
||||
"--vmx", str(vmx),
|
||||
"--ip", "192.168.1.100",
|
||||
"--admin-password", "adminpass",
|
||||
"--build-password", "buildpass",
|
||||
"--store-credential",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0, result.output
|
||||
assert any(s[0] == "BuildVMGuest:meta" for s in stored)
|
||||
assert any(s[0] == "BuildVMGuest" for s in stored)
|
||||
|
||||
Reference in New Issue
Block a user