test(12.1): owner-confirmed expected root manifest

- Create backend/tests/fixtures/cloud/nextcloud_expected_root.json with
  owner_confirmed: true and corrected 10-item name/kind manifest
- Add test_nextcloud_expected_root_manifest: exact set and kind equality
  against fixture (T-12.1-19 — count-only acceptance blocked)
- Update 12.1-VALIDATION.md: manifest status OWNER CONFIRMED, add
  exact-acceptance row to Nyquist matrix
This commit is contained in:
curo1305
2026-06-22 09:35:36 +02:00
parent 779b05086b
commit 1ec5158c65
3 changed files with 142 additions and 12 deletions
@@ -0,0 +1,16 @@
{
"owner_confirmed": true,
"provenance": "Owner reconciled 2026-06-22 — three fixture names corrected from prior candidate manifest; no provider content exposed",
"items": [
{"name": "Documents", "kind": "folder"},
{"name": "docuvault", "kind": "folder"},
{"name": "Photos", "kind": "folder"},
{"name": "Templates", "kind": "folder"},
{"name": "Nextcloud.png", "kind": "file"},
{"name": "Nextcloud Manual.pdf", "kind": "file"},
{"name": "Nextcloud intro.mp4", "kind": "file"},
{"name": "Readme.md", "kind": "file"},
{"name": "Reasons to use Nextcloud.pdf", "kind": "file"},
{"name": "Templates credits.md", "kind": "file"}
]
}
+110
View File
@@ -509,3 +509,113 @@ async def test_nextcloud_connection_id_browse_as_designated_user(
captured = capfd.readouterr()
_assert_no_secrets_in_string(captured.out)
_assert_no_secrets_in_string(captured.err)
# ── Test 4: Exact-name acceptance (owner-confirmed fixture) ──────────────────
@pytest.mark.live_nextcloud
async def test_nextcloud_expected_root_manifest(capfd):
"""Exact root-name and kind acceptance against the owner-confirmed fixture.
Loads the tracked manifest from
backend/tests/fixtures/cloud/nextcloud_expected_root.json.
Requires ``owner_confirmed: true`` in the fixture; raises if absent
(T-12.1-19 guard).
Asserts:
- Exact set equality of names between live root and confirmed fixture.
- Exact kind equality for every fixture item.
- No unexpected provider names are printed (T-12.1-20).
- Fixture contains no credential, full URL, or unexpected-name data.
"""
import json
import pathlib
creds = _skip_if_missing()
url, user, password = creds
# Load and validate the fixture
fixture_path = (
pathlib.Path(__file__).parent / "fixtures" / "cloud" / "nextcloud_expected_root.json"
)
if not fixture_path.exists():
pytest.skip("Expected root manifest fixture not found — run Task 2 reconciliation first.")
with fixture_path.open() as fh:
manifest = json.load(fh)
assert manifest.get("owner_confirmed") is True, (
"T-12.1-19: exact-name acceptance requires owner_confirmed: true in fixture. "
"Do not set this flag without explicit owner reconciliation."
)
expected_names = frozenset(item["name"] for item in manifest["items"])
expected_kinds = {item["name"]: item["kind"] for item in manifest["items"]}
# Fetch the live listing via production adapter
from storage.cloud_backend_factory import build_cloud_resource_adapter
from storage.cloud_utils import normalize_nextcloud_url
try:
canonical_url = normalize_nextcloud_url(url, user)
except ValueError as exc:
code = _stable_error_code(exc)
pytest.skip(f"URL normalization [{code}].")
credentials = {
"server_url": canonical_url,
"username": user,
"password": password,
}
connection_id = _uuid.uuid4()
user_id = _uuid.uuid4()
try:
adapter = build_cloud_resource_adapter("nextcloud", credentials)
except ValueError as exc:
code = _stable_error_code(exc)
pytest.skip(f"Adapter construction [{code}].")
try:
listing = await adapter.list_folder(
connection_id=connection_id,
user_id=user_id,
parent_ref=None,
page_token=None,
)
except Exception as exc:
code = _stable_error_code(exc)
pytest.fail(
f"list_folder failed [{code}] — "
"check NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_APP_PASSWORD in .env."
)
actual_names = frozenset(item.name for item in listing.items)
actual_kinds = {item.name: item.kind for item in listing.items}
# Exact set equality (T-12.1-19)
missing_from_live = sorted(expected_names - actual_names)
extra_in_live_count = len(actual_names - expected_names)
assert not missing_from_live, (
f"T-12.1-19: {len(missing_from_live)} owner-confirmed name(s) missing from live root: "
f"{missing_from_live}. "
"Unexpected live names are withheld (T-12.1-20)."
)
assert extra_in_live_count == 0, (
f"T-12.1-19: live root has {extra_in_live_count} extra item(s) beyond the confirmed manifest. "
"Unexpected live names are withheld (T-12.1-20)."
)
# Exact kind equality for every confirmed item
for name, expected_kind in expected_kinds.items():
live_kind = actual_kinds.get(name)
assert live_kind == expected_kind, (
f"T-12.1-19: item {name!r} — expected kind {expected_kind!r}, got {live_kind!r}"
)
# Assert no secrets leaked
captured = capfd.readouterr()
_assert_no_secrets_in_string(captured.out)
_assert_no_secrets_in_string(captured.err)