Fix Zotero table models for Zotero 9:
- Remove stale Annotations/Highlights/Transaction* models
- Add ItemAnnotations, RetractedItems, DeletedCollections,
DeletedSearches, DbDebug1
- Fix ItemAttachments, Libraries, Users column mismatches
New test files covering all major modules:
- cli/{bib,prisma,rec,zot,mail,run} deep exercising tests
- mail/{droplet,postmark,resend,cloudflare} lifecycle tests
- bib/{iom,oig,pincite,sync,regulations_gov,email_ingest,format,store}
- prisma/{vpn,fetch,export,llm,screen,eligibility,extract,project,ingest,flow}
- aco/lake/{unity,quality,deploy} + api/aco coverage gaps
- zot/{ops,db,extract,duck} + rec/{report,engine,base,pricers}
- pfs/{pipe,rules,eq,files}
Add pytest-xdist for parallel test execution.
Tracks #353
273 lines
8.1 KiB
Python
273 lines
8.1 KiB
Python
"""Exercise aco.lake.unity — grants, secrets, volume, constraints, warehouses, lineage."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from aco.lake.unity import UnityClient
|
|
|
|
|
|
def _mock_client():
|
|
ws = MagicMock()
|
|
with patch("aco.lake.unity.WorkspaceClient", return_value=ws):
|
|
c = UnityClient.__new__(UnityClient)
|
|
c._ws = ws
|
|
return c, ws
|
|
|
|
|
|
class TestDeleteVolume:
|
|
def test_calls_sdk(self):
|
|
c, ws = _mock_client()
|
|
c.delete_volume("cat", "schema", "vol")
|
|
ws.volumes.delete.assert_called_once_with("cat.schema.vol")
|
|
|
|
|
|
class TestListGrants:
|
|
def test_returns_grants(self):
|
|
c, ws = _mock_client()
|
|
priv = MagicMock()
|
|
priv.privilege = MagicMock(value="SELECT")
|
|
pa = MagicMock()
|
|
pa.principal = "group1"
|
|
pa.privileges = [priv]
|
|
resp = MagicMock()
|
|
resp.privilege_assignments = [pa]
|
|
ws.grants.get.return_value = resp
|
|
|
|
result = c.list_grants("CATALOG", "my_catalog")
|
|
assert len(result) == 1
|
|
assert result[0]["principal"] == "group1"
|
|
assert "SELECT" in result[0]["privileges"]
|
|
|
|
def test_empty_grants(self):
|
|
c, ws = _mock_client()
|
|
resp = MagicMock()
|
|
resp.privilege_assignments = None
|
|
ws.grants.get.return_value = resp
|
|
assert c.list_grants("TABLE", "cat.schema.tbl") == []
|
|
|
|
|
|
class TestUpdateGrants:
|
|
def test_add_and_remove(self):
|
|
c, ws = _mock_client()
|
|
c.update_grants(
|
|
"CATALOG",
|
|
"my_catalog",
|
|
"group1",
|
|
add=["SELECT", "USAGE"],
|
|
remove=["CREATE_TABLE"],
|
|
)
|
|
ws.grants.update.assert_called_once()
|
|
|
|
def test_add_only(self):
|
|
c, ws = _mock_client()
|
|
c.update_grants("SCHEMA", "cat.schema", "user1", add=["SELECT"])
|
|
ws.grants.update.assert_called_once()
|
|
|
|
|
|
class TestEnsureScope:
|
|
def test_creates_if_missing(self):
|
|
c, ws = _mock_client()
|
|
scope1 = MagicMock()
|
|
scope1.name = "existing"
|
|
ws.secrets.list_scopes.return_value = [scope1]
|
|
c.ensure_scope("new-scope")
|
|
ws.secrets.create_scope.assert_called_once_with(scope="new-scope")
|
|
|
|
def test_noop_if_exists(self):
|
|
c, ws = _mock_client()
|
|
scope1 = MagicMock()
|
|
scope1.name = "existing"
|
|
ws.secrets.list_scopes.return_value = [scope1]
|
|
c.ensure_scope("existing")
|
|
ws.secrets.create_scope.assert_not_called()
|
|
|
|
|
|
class TestPutSecret:
|
|
def test_stores(self):
|
|
c, ws = _mock_client()
|
|
c.put_secret("scope", "key", "val")
|
|
ws.secrets.put_secret.assert_called_once_with(
|
|
scope="scope", key="key", string_value="val"
|
|
)
|
|
|
|
|
|
class TestGetSecret:
|
|
def test_returns_value(self):
|
|
c, ws = _mock_client()
|
|
resp = MagicMock()
|
|
resp.value = "secret-value"
|
|
ws.secrets.get_secret.return_value = resp
|
|
assert c.get_secret("scope", "key") == "secret-value"
|
|
|
|
def test_empty(self):
|
|
c, ws = _mock_client()
|
|
resp = MagicMock()
|
|
resp.value = None
|
|
ws.secrets.get_secret.return_value = resp
|
|
assert c.get_secret("scope", "key") == ""
|
|
|
|
|
|
class TestListSecrets:
|
|
def test_returns_keys(self):
|
|
c, ws = _mock_client()
|
|
s1 = MagicMock()
|
|
s1.key = "key1"
|
|
s2 = MagicMock()
|
|
s2.key = "key2"
|
|
s3 = MagicMock()
|
|
s3.key = None
|
|
ws.secrets.list_secrets.return_value = [s1, s2, s3]
|
|
assert c.list_secrets("scope") == ["key1", "key2"]
|
|
|
|
|
|
class TestSyncSecrets:
|
|
@patch.dict("os.environ", {"DB_HOST": "localhost", "DB_PASS": "secret"})
|
|
def test_dry_run(self):
|
|
c, ws = _mock_client()
|
|
actions = c.sync_secrets(
|
|
"scope", {"DB_HOST": "db_host", "DB_PASS": "db_pass"}, dry_run=True
|
|
)
|
|
assert len(actions) == 2
|
|
assert all("[dry-run]" in a for a in actions)
|
|
|
|
@patch.dict("os.environ", {"DB_HOST": "localhost", "DB_PASS": "secret"})
|
|
def test_real_sync(self):
|
|
c, ws = _mock_client()
|
|
actions = c.sync_secrets(
|
|
"scope", {"DB_HOST": "db_host", "DB_PASS": "db_pass"}, dry_run=False
|
|
)
|
|
assert len(actions) == 2
|
|
assert all("synced" in a for a in actions)
|
|
assert ws.secrets.put_secret.call_count == 2
|
|
|
|
@patch.dict("os.environ", {"DB_HOST": "", "MISSING": ""}, clear=False)
|
|
def test_skips_unset(self):
|
|
c, ws = _mock_client()
|
|
actions = c.sync_secrets("scope", {"MISSING": "missing_key"}, dry_run=False)
|
|
assert len(actions) == 1
|
|
assert "[skip]" in actions[0]
|
|
|
|
|
|
class TestSetPrimaryKey:
|
|
def test_calls_sdk(self):
|
|
c, ws = _mock_client()
|
|
c.set_primary_key("cat", "schema", "table", ["id"])
|
|
ws.table_constraints.create.assert_called_once()
|
|
|
|
def test_custom_name(self):
|
|
c, ws = _mock_client()
|
|
c.set_primary_key("cat", "schema", "table", ["id"], constraint_name="my_pk")
|
|
ws.table_constraints.create.assert_called_once()
|
|
|
|
|
|
class TestSetForeignKey:
|
|
def test_calls_sdk(self):
|
|
c, ws = _mock_client()
|
|
c.set_foreign_key(
|
|
"cat",
|
|
"schema",
|
|
"table",
|
|
["ref_id"],
|
|
"cat",
|
|
"schema",
|
|
"ref_table",
|
|
["id"],
|
|
)
|
|
ws.table_constraints.create.assert_called_once()
|
|
|
|
|
|
class TestFindWarehouse:
|
|
def test_found(self):
|
|
c, ws = _mock_client()
|
|
wh = MagicMock()
|
|
wh.name = "my-wh"
|
|
wh.id = "wh-123"
|
|
ws.warehouses.list.return_value = [wh]
|
|
assert c.find_warehouse("my-wh") == "wh-123"
|
|
|
|
def test_not_found(self):
|
|
c, ws = _mock_client()
|
|
ws.warehouses.list.return_value = []
|
|
assert c.find_warehouse("nope") is None
|
|
|
|
|
|
class TestEnsureWarehouse:
|
|
def test_existing(self):
|
|
c, ws = _mock_client()
|
|
wh = MagicMock()
|
|
wh.name = "my-wh"
|
|
wh.id = "wh-123"
|
|
ws.warehouses.list.return_value = [wh]
|
|
assert c.ensure_warehouse("my-wh") == "wh-123"
|
|
|
|
def test_creates(self):
|
|
c, ws = _mock_client()
|
|
ws.warehouses.list.return_value = []
|
|
resp = MagicMock()
|
|
resp.id = "wh-new"
|
|
ws.warehouses.create_and_wait.return_value = resp
|
|
assert c.ensure_warehouse("new-wh") == "wh-new"
|
|
|
|
|
|
class TestStartStopWarehouse:
|
|
def test_start(self):
|
|
c, ws = _mock_client()
|
|
c.start_warehouse("wh-1")
|
|
ws.warehouses.start.assert_called_once_with("wh-1")
|
|
|
|
def test_stop(self):
|
|
c, ws = _mock_client()
|
|
c.stop_warehouse("wh-1")
|
|
ws.warehouses.stop.assert_called_once_with("wh-1")
|
|
|
|
|
|
class TestEnableSystemSchema:
|
|
def test_calls(self):
|
|
c, ws = _mock_client()
|
|
c.enable_system_schema("meta-1", "access")
|
|
ws.system_schemas.enable.assert_called_once()
|
|
|
|
|
|
class TestQueryLineage:
|
|
def test_returns_data(self):
|
|
c, ws = _mock_client()
|
|
col = MagicMock()
|
|
col.name = "table_name"
|
|
resp = MagicMock()
|
|
resp.result = MagicMock()
|
|
resp.result.data_array = [["my_table"]]
|
|
resp.manifest.schema.columns = [col]
|
|
ws.statement_execution.execute_statement.return_value = resp
|
|
result = c.query_lineage("cat", "wh-1")
|
|
assert len(result) == 1
|
|
assert result[0]["table_name"] == "my_table"
|
|
|
|
def test_empty(self):
|
|
c, ws = _mock_client()
|
|
resp = MagicMock()
|
|
resp.result = None
|
|
ws.statement_execution.execute_statement.return_value = resp
|
|
assert c.query_lineage("cat", "wh-1") == []
|
|
|
|
|
|
class TestQueryAuditLog:
|
|
def test_returns_data(self):
|
|
c, ws = _mock_client()
|
|
col = MagicMock()
|
|
col.name = "action_name"
|
|
resp = MagicMock()
|
|
resp.result = MagicMock()
|
|
resp.result.data_array = [["CREATE_TABLE"]]
|
|
resp.manifest.schema.columns = [col]
|
|
ws.statement_execution.execute_statement.return_value = resp
|
|
result = c.query_audit_log("cat", "wh-1")
|
|
assert len(result) == 1
|
|
|
|
def test_empty(self):
|
|
c, ws = _mock_client()
|
|
resp = MagicMock()
|
|
resp.result = None
|
|
ws.statement_execution.execute_statement.return_value = resp
|
|
assert c.query_audit_log("cat", "wh-1") == []
|