Built the Oracle Tab (#14)

This commit is contained in:
2026-04-11 19:35:45 +05:30
committed by Sagnik
parent 8e1ffe0e43
commit fb656d1443
54 changed files with 10651 additions and 818 deletions

View File

@@ -0,0 +1 @@
"""Tests for Oracle backend services — runs without any live database or model runtime."""

View File

@@ -0,0 +1,133 @@
"""
test_canvas_service.py — Unit tests for CanvasService (demo mode in-memory store).
"""
import asyncio
import pytest
import sys
import os
# Ensure backend is on path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
from oracle.canvas_service import CanvasService
TENANT = "tenant_test_001"
ACTOR = "user_test_001"
def run(coro):
return asyncio.get_event_loop().run_until_complete(coro)
@pytest.fixture
def svc():
"""Fresh CanvasService instance with empty demo store per test."""
from oracle import canvas_service as _mod
_mod._DEMO_PAGES.clear()
_mod._DEMO_REVISIONS.clear()
_mod._DEMO_COMPONENTS.clear()
return CanvasService()
def test_create_page(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR, title="Test Canvas"))
assert page["tenantId"] == TENANT
assert page["ownerId"] == ACTOR
assert page["title"] == "Test Canvas"
assert page["headRevision"] == 0
assert page["pageType"] == "main"
assert page["branchName"] == "main"
def test_get_page_not_found(svc):
result = run(svc.get_page("nonexistent_id", TENANT))
assert result is None
def test_get_page_returns_page(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR))
retrieved = run(svc.get_page(page["pageId"], TENANT))
assert retrieved is not None
assert retrieved["pageId"] == page["pageId"]
def test_commit_revision_advances_head(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR))
comps = [{"componentId": "cmp_001", "type": "barChart", "title": "Test"}]
rev = run(svc.commit_revision(
page_id=page["pageId"],
tenant_id=TENANT,
actor_id=ACTOR,
commit_kind="prompt",
commit_summary="Test prompt",
components=comps,
idempotency_key="ikey_001",
))
assert rev["revisionNumber"] == 1
updated = run(svc.get_page(page["pageId"], TENANT))
assert updated["headRevision"] == 1
assert len(updated["components"]) == 1
def test_idempotency_key_prevents_double_commit(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR))
comps = [{"componentId": "cmp_001", "type": "barChart", "title": "Test"}]
rev1 = run(svc.commit_revision(
page_id=page["pageId"], tenant_id=TENANT, actor_id=ACTOR,
commit_kind="prompt", commit_summary="First", components=comps,
idempotency_key="ikey_idempotent",
))
rev2 = run(svc.commit_revision(
page_id=page["pageId"], tenant_id=TENANT, actor_id=ACTOR,
commit_kind="prompt", commit_summary="Duplicate", components=comps,
idempotency_key="ikey_idempotent",
))
assert rev1["revisionId"] == rev2["revisionId"]
# Head should still be 1
updated = run(svc.get_page(page["pageId"], TENANT))
assert updated["headRevision"] == 1
def test_rollback_creates_new_revision(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR))
comps_v1 = [{"componentId": "cmp_v1", "type": "barChart", "title": "V1"}]
comps_v2 = [{"componentId": "cmp_v2", "type": "lineChart", "title": "V2"}]
run(svc.commit_revision(
page_id=page["pageId"], tenant_id=TENANT, actor_id=ACTOR,
commit_kind="prompt", commit_summary="V1", components=comps_v1, idempotency_key="key_v1",
))
run(svc.commit_revision(
page_id=page["pageId"], tenant_id=TENANT, actor_id=ACTOR,
commit_kind="prompt", commit_summary="V2", components=comps_v2, idempotency_key="key_v2",
))
# Rollback to revision 1
rollback_rev = run(svc.rollback(
page_id=page["pageId"], tenant_id=TENANT, actor_id=ACTOR,
target_revision=1, idempotency_key="key_rollback",
))
assert rollback_rev["revisionNumber"] == 3
assert rollback_rev["commitKind"] == "rollback"
revisions = run(svc.list_revisions(page["pageId"], TENANT))
assert len(revisions) == 3 # 3 revisions total
def test_list_revisions_returns_newest_first(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR))
for i in range(3):
run(svc.commit_revision(
page_id=page["pageId"], tenant_id=TENANT, actor_id=ACTOR,
commit_kind="prompt", commit_summary=f"Rev {i+1}",
components=[], idempotency_key=f"key_{i}",
))
revisions = run(svc.list_revisions(page["pageId"], TENANT))
assert revisions[0]["revisionNumber"] > revisions[-1]["revisionNumber"]
def test_tenant_isolation(svc):
page = run(svc.create_page(tenant_id=TENANT, owner_id=ACTOR))
# Different tenant cannot access the page
result = run(svc.get_page(page["pageId"], "tenant_different_999"))
assert result is None

View File

@@ -0,0 +1,207 @@
"""
test_collaboration_service.py — Unit tests for three-way diff, fork, merge request lifecycle.
"""
import asyncio
import copy
import sys
import os
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
from oracle.collaboration_service import CollaborationService, three_way_diff
def run(coro):
return asyncio.get_event_loop().run_until_complete(coro)
def _comp(cid, title="Test", order=100, content="default"):
return {
"componentId": cid,
"type": "barChart",
"title": title,
"dataSourceDescriptor": {"dataset": "test_ds", "queryTemplate": content},
"accessControls": {"allowedRoles": ["sales_director"], "visibilityScope": "private"},
"layout": {"orderIndex": order, "sectionId": "sec_test", "widthMode": "full"},
}
# ── Three-way diff tests ──────────────────────────────────────────────────────
def test_safe_append_in_source():
base = [_comp("cmp_a")]
source = [_comp("cmp_a"), _comp("cmp_b")] # cmp_b added in source
target = [_comp("cmp_a")]
merged, conflicts = three_way_diff(base, source, target)
assert any(c["conflictClass"] == "safe_append" and c["componentId"] == "cmp_b" for c in conflicts)
assert any(c["componentId"] == "cmp_b" for c in merged)
def test_no_conflict_when_identical():
base = [_comp("cmp_a")]
source = [_comp("cmp_a")]
target = [_comp("cmp_a")]
merged, conflicts = three_way_diff(base, source, target)
assert len(merged) == 1
assert all(c["conflictClass"] not in ("component_content_conflict", "query_descriptor_conflict") for c in conflicts)
def test_component_content_conflict():
base = [_comp("cmp_a", content="SELECT 1")]
source = [_comp("cmp_a", content="SELECT 2")]
target = [_comp("cmp_a", content="SELECT 3")]
merged, conflicts = three_way_diff(base, source, target)
# Expect query_descriptor_conflict or component_content_conflict
conflict_classes = {c["conflictClass"] for c in conflicts}
assert conflict_classes & {"component_content_conflict", "query_descriptor_conflict"}
def test_delete_edit_conflict_source_deletes():
base = [_comp("cmp_a"), _comp("cmp_b")]
source = [_comp("cmp_a")] # cmp_b deleted in source
target = [_comp("cmp_a"), _comp("cmp_b", title="Edited in target")] # cmp_b edited in target
merged, conflicts = three_way_diff(base, source, target)
assert any(c["conflictClass"] == "delete_edit_conflict" and c["componentId"] == "cmp_b" for c in conflicts)
# Default: keep target (edited version)
assert any(c["componentId"] == "cmp_b" for c in merged)
def test_deleted_in_both_is_removed():
base = [_comp("cmp_a"), _comp("cmp_b")]
source = [_comp("cmp_a")]
target = [_comp("cmp_a")]
merged, conflicts = three_way_diff(base, source, target)
assert not any(c["componentId"] == "cmp_b" for c in merged)
def test_orderindex_normalization():
base = []
source = [_comp("c1", order=100), _comp("c2", order=200)]
target = [_comp("c1", order=100), _comp("c2", order=200)]
merged, _ = three_way_diff(base, source, target)
orders = [c["layout"]["orderIndex"] for c in merged]
# Orders should be normalized (multiples of 100, sequential)
assert orders == sorted(orders)
assert all(o % 100 == 0 for o in orders)
# ── CollaborationService tests ────────────────────────────────────────────────
@pytest.fixture
def collab():
from oracle import collaboration_service as _mod
_mod._DEMO_FORKS.clear()
_mod._DEMO_MRS.clear()
return CollaborationService()
def test_create_fork(collab):
source_page = {
"pageId": "page_src",
"branchId": "branch_main",
"headRevision": 5,
"components": [],
}
fork = run(collab.create_fork(
source_page=source_page,
recipient_user_id="user_recipient",
created_by="user_src",
))
assert fork["sourcePageId"] == "page_src"
assert fork["sourceRevision"] == 5
assert fork["status"] == "active"
assert fork["recipientUserId"] == "user_recipient"
def test_merge_request_lifecycle(collab):
mr = run(collab.open_merge_request(
tenant_id="tenant_test",
source_page_id="page_fork",
source_branch_id="branch_fork",
source_head_revision=2,
target_page_id="page_main",
target_branch_id="branch_main",
target_base_revision=5,
title="Test MR",
description="My changes",
created_by="user_a",
source_components=[_comp("cmp_a"), _comp("cmp_new")],
target_components=[_comp("cmp_a")],
base_components=[_comp("cmp_a")],
))
assert mr["status"] == "open"
assert "mergeRequestId" in mr
# Approve it
reviewed = run(collab.review_merge_request(
mr_id=mr["mergeRequestId"],
decision="approve",
reviewer_id="user_reviewer",
))
assert reviewed["status"] == "merged"
def test_merge_request_reject(collab):
mr = run(collab.open_merge_request(
tenant_id="tenant_test",
source_page_id="page_fork",
source_branch_id="branch_fork",
source_head_revision=1,
target_page_id="page_main",
target_branch_id="branch_main",
target_base_revision=1,
title="Rejected MR",
created_by="user_a",
source_components=[],
target_components=[],
base_components=[],
))
reviewed = run(collab.review_merge_request(
mr_id=mr["mergeRequestId"],
decision="reject",
reviewer_id="user_reviewer",
))
assert reviewed["status"] == "closed"
def test_list_merge_requests_filters_by_target(collab):
for i in range(3):
run(collab.open_merge_request(
tenant_id="tenant_t",
source_page_id=f"page_fork_{i}",
source_branch_id=f"branch_{i}",
source_head_revision=1,
target_page_id="page_target",
target_branch_id="branch_main",
target_base_revision=1,
title=f"MR {i}",
created_by="user_a",
source_components=[],
target_components=[],
base_components=[],
))
# Different target
run(collab.open_merge_request(
tenant_id="tenant_t",
source_page_id="page_other",
source_branch_id="branch_other",
source_head_revision=1,
target_page_id="page_other_target",
target_branch_id="branch_main",
target_base_revision=1,
title="Different target MR",
created_by="user_b",
source_components=[],
target_components=[],
base_components=[],
))
mrs = run(collab.list_merge_requests("page_target"))
assert len(mrs) == 3
assert all(mr["targetPageId"] == "page_target" for mr in mrs)

View File

@@ -0,0 +1,142 @@
"""
test_policy_service.py — Unit tests for Oracle policy engine.
"""
import sys
import os
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
from oracle.policy_service import PolicyService, PolicyContext
@pytest.fixture
def svc():
return PolicyService()
def _ctx(role: str = "sales_director") -> PolicyContext:
return PolicyContext(
tenant_id="tenant_test_001",
actor_id="user_test_001",
actor_role=role,
)
# ── Privacy tier tests ────────────────────────────────────────────────────────
def test_junior_broker_denied_restricted(svc):
plan = {"dataset": "lead_contacts", "privacyTier": "restricted", "rowLimit": 50, "joins": []}
result = svc.validate_retrieval_plan(plan, _ctx("junior_broker"))
assert not result.passed
assert any("POLICY_PRIVACY_TIER_ESCALATION" in e for e in result.errors)
def test_senior_broker_allowed_restricted_with_redaction(svc):
plan = {"dataset": "lead_contacts", "privacyTier": "restricted", "rowLimit": 50, "joins": []}
result = svc.validate_retrieval_plan(plan, _ctx("senior_broker"))
assert result.passed
assert result.redaction_policy == "aggregate_only"
def test_junior_broker_denied_sensitive(svc):
plan = {"dataset": "pii_records", "privacyTier": "sensitive", "rowLimit": 10, "joins": []}
result = svc.validate_retrieval_plan(plan, _ctx("junior_broker"))
assert not result.passed
def test_data_steward_allowed_sensitive(svc):
plan = {"dataset": "pii_records", "privacyTier": "sensitive", "rowLimit": 100, "joins": []}
result = svc.validate_retrieval_plan(plan, _ctx("data_steward"))
assert result.passed
# ── Row limit tests ───────────────────────────────────────────────────────────
def test_row_limit_capped_for_junior_broker(svc):
plan = {"dataset": "leads", "privacyTier": "standard", "rowLimit": 5000, "joins": []}
result = svc.validate_retrieval_plan(plan, _ctx("junior_broker"))
assert result.passed
assert result.effective_row_limit == 100
assert any("ROW_LIMIT_CAPPED" in w for w in result.warnings)
def test_row_limit_respected_for_admin(svc):
plan = {"dataset": "leads", "privacyTier": "standard", "rowLimit": 5000, "joins": []}
result = svc.validate_retrieval_plan(plan, _ctx("platform_admin"))
assert result.passed
assert result.effective_row_limit == 5000
# ── Cross-tenant join tests ───────────────────────────────────────────────────
def test_cross_tenant_join_denied(svc):
plan = {
"dataset": "global_lead_market",
"privacyTier": "standard",
"rowLimit": 50,
"joins": [],
}
result = svc.validate_retrieval_plan(plan, _ctx("sales_director"))
assert not result.passed
assert any("POLICY_CROSS_TENANT_JOIN_DENIED" in e for e in result.errors)
def test_explicit_cross_tenant_join_denied(svc):
plan = {
"dataset": "deals",
"privacyTier": "standard",
"rowLimit": 50,
"joins": [{"tenantId": "tenant_other_999"}],
}
result = svc.validate_retrieval_plan(plan, _ctx("sales_director"))
assert not result.passed
# ── Tenant predicate enforcement ──────────────────────────────────────────────
def test_enforce_tenant_predicate_overrides(svc):
params = {"tenant_id": "attacker_tenant", "limit": 100}
ctx = _ctx("sales_director")
enforced = svc.enforce_tenant_predicate(params, ctx)
assert enforced["tenant_id"] == "tenant_test_001"
assert enforced["limit"] == 100
# ── Component access control ──────────────────────────────────────────────────
def test_component_access_granted_for_allowed_role(svc):
ac = {"allowedRoles": ["sales_director", "senior_broker"], "visibilityScope": "private"}
assert svc.validate_component_access(ac, _ctx("sales_director")) is True
def test_component_access_denied_for_wrong_role(svc):
ac = {"allowedRoles": ["data_steward"], "visibilityScope": "private"}
assert svc.validate_component_access(ac, _ctx("junior_broker")) is False
# ── Redaction tests ───────────────────────────────────────────────────────────
def test_redact_full():
svc = PolicyService()
rows = [{"name": "Alice", "email": "alice@test.com", "deal": 1000}]
redacted = svc.redact(rows, "full_redact")
assert redacted == [{"__redacted__": True, "count": 1}]
def test_redact_aggregate_only():
svc = PolicyService()
rows = [{"name": "Alice", "count": 5, "stage": "Qualified", "email": "alice@test.com"}]
redacted = svc.redact(rows, "aggregate_only")
assert len(redacted) == 1
assert "email" not in redacted[0]
assert "name" not in redacted[0]
assert redacted[0].get("count") == 5
assert redacted[0].get("stage") == "Qualified"
def test_redact_none_passes_through():
svc = PolicyService()
rows = [{"name": "Alice", "value": 999}]
result = svc.redact(rows, "none")
assert result == rows

View File

@@ -0,0 +1,143 @@
"""
test_prompt_orchestrator.py — Unit tests for PromptOrchestrator (demo/fallback mode).
"""
import asyncio
import sys
import os
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
def run(coro):
return asyncio.get_event_loop().run_until_complete(coro)
@pytest.fixture(autouse=True)
def clear_demo_stores():
from oracle import canvas_service as _cs
from oracle import collaboration_service as _col
_cs._DEMO_PAGES.clear()
_cs._DEMO_REVISIONS.clear()
_cs._DEMO_COMPONENTS.clear()
_col._DEMO_MRS.clear()
yield
@pytest.fixture
def page():
"""Create a demo canvas page and return it."""
from oracle.canvas_service import canvas_service
return run(canvas_service.create_page(
tenant_id="tenant_test",
owner_id="user_test",
title="Test Oracle Page",
))
def test_pipeline_prompt_produces_components(page):
from oracle.prompt_orchestrator import PromptOrchestrator
orch = PromptOrchestrator()
result = run(orch.execute(
tenant_id="tenant_test",
page_id=page["pageId"],
branch_id=page["branchId"],
actor_id="user_test",
actor_role="sales_director",
prompt="Show me an active pipeline view by stage",
client_request_id="cli_test_001",
))
assert result["status"] == "completed"
assert len(result["componentsCreated"]) > 0
assert result["summary"]
def test_geo_map_prompt_produces_geo_component(page):
from oracle.prompt_orchestrator import PromptOrchestrator
orch = PromptOrchestrator()
result = run(orch.execute(
tenant_id="tenant_test",
page_id=page["pageId"],
branch_id=page["branchId"],
actor_id="user_test",
actor_role="sales_director",
prompt="Show me a map of whale leads by Dubai district",
client_request_id="cli_test_002",
))
assert result["status"] == "completed"
vp = result.get("visualizationPlan", {}).get("components", [])
assert any(c["type"] == "geoMap" for c in vp)
def test_broker_table_prompt(page):
from oracle.prompt_orchestrator import PromptOrchestrator
orch = PromptOrchestrator()
result = run(orch.execute(
tenant_id="tenant_test",
page_id=page["pageId"],
branch_id=page["branchId"],
actor_id="user_test",
actor_role="sales_director",
prompt="Give me a table of brokers ranked by performance",
client_request_id="cli_test_003",
))
assert result["status"] == "completed"
vp = result.get("visualizationPlan", {}).get("components", [])
assert any(c["type"] == "table" for c in vp)
def test_policy_denial_on_restricted_for_junior_broker(page):
"""Junior broker should get warnings/denial on restricted tier dataset."""
from oracle.prompt_orchestrator import PromptOrchestrator, _build_demo_retrieval_plan
from oracle.policy_service import PolicyService, PolicyContext
plan = {
"components": [{
"suggestedType": "table",
"dataset": "pii_leads",
"privacyTier": "sensitive",
"rowLimit": 500,
"joins": [],
}]
}
svc = PolicyService()
ctx = PolicyContext(tenant_id="tenant_test", actor_id="user_junior", actor_role="junior_broker")
for comp_plan in plan["components"]:
result = svc.validate_retrieval_plan(comp_plan, ctx)
assert not result.passed
def test_idempotency_key_prevents_double_execution(page):
from oracle.prompt_orchestrator import PromptOrchestrator
orch = PromptOrchestrator()
prompt_kwargs = dict(
tenant_id="tenant_test",
page_id=page["pageId"],
branch_id=page["branchId"],
actor_id="user_test",
actor_role="sales_director",
prompt="Pipeline view",
client_request_id="cli_idempotent_key",
)
result1 = run(orch.execute(**prompt_kwargs))
result2 = run(orch.execute(**prompt_kwargs))
# Both should succeed; canvas should not double-create components
assert result1["status"] == "completed"
assert result2["status"] == "completed"
def test_kpi_prompt_type(page):
from oracle.prompt_orchestrator import PromptOrchestrator
orch = PromptOrchestrator()
result = run(orch.execute(
tenant_id="tenant_test",
page_id=page["pageId"],
branch_id=page["branchId"],
actor_id="user_test",
actor_role="sales_director",
prompt="How many total leads do we have?",
client_request_id="cli_test_kpi",
))
vp = result.get("visualizationPlan", {}).get("components", [])
assert any(c["type"] == "kpiTile" for c in vp)