ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,329 @@
"""Tests for both application scenarios."""
import pytest
import time
from pedo.core.models import AccessContext, DataObject, PermissionRule, Operation, PrivilegeType
from pedo.core.store import ObjectStore, PermissionDeniedError, ValidationError, ReferentialIntegrityError
from pedo.scenarios.hiring import register_hiring_types
from pedo.scenarios.project_mgmt import register_project_mgmt_types
DSN = "dbname=pedo_test"
# ── Scenario A: Hiring Pipeline ───────────────────────────────
@pytest.fixture
def hiring_store():
s = ObjectStore(DSN)
s.clear_all()
register_hiring_types(s)
return s
@pytest.fixture
def recruiter():
return AccessContext(user_id="recruiter1", role="recruiter", org_id="acme")
@pytest.fixture
def hiring_manager():
return AccessContext(user_id="hm1", role="hiring_manager", org_id="acme")
@pytest.fixture
def admin():
return AccessContext(user_id="admin1", role="admin", org_id="acme")
@pytest.fixture
def system_ctx():
return AccessContext(user_id="system", role="system", org_id="acme")
def _create_position(store, ctx, status="open"):
return store.create(DataObject(
type_name="position",
content={"title": "Engineer", "department": "Eng", "status": status,
"salary_min": 80000, "salary_max": 150000},
org_id="acme",
), ctx)
def test_candidate_lifecycle(hiring_store, recruiter, hiring_manager, system_ctx):
"""Test the full candidate lifecycle through the state machine."""
pos = _create_position(hiring_store, system_ctx)
# Create candidate (must start as 'applied')
cand = hiring_store.create(DataObject(
type_name="candidate",
content={"name": "Alice", "email": "alice@test.com", "status": "applied",
"position_id": pos.id, "salary_expectation": 100000},
org_id="acme",
), recruiter)
# Valid transitions
hiring_store.update(cand.id, {"status": "screened"}, recruiter)
hiring_store.update(cand.id, {"status": "interviewed"}, recruiter)
hiring_store.update(cand.id, {"status": "offered"}, recruiter)
hiring_store.update(cand.id, {"status": "hired"}, recruiter)
# Process reactions (audit logs)
hiring_store.process_reactions_sync()
logs = hiring_store.get_reaction_log()
assert len(logs) >= 4 # create + 4 status changes
def test_invalid_status_transition(hiring_store, recruiter, system_ctx):
pos = _create_position(hiring_store, system_ctx)
cand = hiring_store.create(DataObject(
type_name="candidate",
content={"name": "Bob", "email": "bob@test.com", "status": "applied",
"position_id": pos.id},
org_id="acme",
), recruiter)
# Skip screened -> directly to offered (invalid)
with pytest.raises(ValidationError, match="Invalid status transition"):
hiring_store.update(cand.id, {"status": "offered"}, recruiter)
def test_closed_position_rejects_candidate(hiring_store, recruiter, system_ctx):
pos = _create_position(hiring_store, system_ctx, status="closed")
with pytest.raises(ValidationError, match="not open"):
hiring_store.create(DataObject(
type_name="candidate",
content={"name": "Charlie", "email": "c@test.com", "status": "applied",
"position_id": pos.id},
org_id="acme",
), recruiter)
def test_salary_out_of_range(hiring_store, recruiter, system_ctx):
pos = _create_position(hiring_store, system_ctx)
with pytest.raises(ValidationError, match="Salary.*outside position range"):
hiring_store.create(DataObject(
type_name="candidate",
content={"name": "Dave", "email": "d@test.com", "status": "applied",
"position_id": pos.id, "salary_expectation": 200000},
org_id="acme",
), recruiter)
def test_hiring_manager_cannot_modify_candidate(hiring_store, recruiter, hiring_manager, system_ctx):
pos = _create_position(hiring_store, system_ctx)
cand = hiring_store.create(DataObject(
type_name="candidate",
content={"name": "Eve", "email": "e@test.com", "status": "applied",
"position_id": pos.id},
org_id="acme",
), recruiter)
# Hiring manager can read but not write
read = hiring_store.get(cand.id, hiring_manager)
assert read is not None
with pytest.raises(PermissionDeniedError):
hiring_store.update(cand.id, {"status": "screened"}, hiring_manager)
def test_recruiter_cannot_see_evaluations(hiring_store, recruiter, hiring_manager, system_ctx):
pos = _create_position(hiring_store, system_ctx)
cand = hiring_store.create(DataObject(
type_name="candidate",
content={"name": "Frank", "email": "f@test.com", "status": "applied",
"position_id": pos.id},
org_id="acme",
), recruiter)
hiring_store.update(cand.id, {"status": "screened"}, recruiter)
interview = hiring_store.create(DataObject(
type_name="interview",
content={"candidate_id": cand.id, "interviewer": "hm1",
"scheduled_at": "2024-01-15", "notes": "", "score": 0},
org_id="acme",
), hiring_manager)
evaluation = hiring_store.create(DataObject(
type_name="evaluation",
content={"interview_id": interview.id, "decision": "proceed", "comments": "Strong candidate"},
org_id="acme",
), hiring_manager)
# Recruiter cannot see evaluation
with pytest.raises(PermissionDeniedError):
hiring_store.get(evaluation.id, recruiter)
# ── Scenario B: Multi-tenant Project Management ───────────────
@pytest.fixture
def pm_store():
s = ObjectStore(DSN)
s.clear_all()
register_project_mgmt_types(s)
return s
@pytest.fixture
def org_admin():
return AccessContext(user_id="admin1", role="org_admin", org_id="tenant_a")
@pytest.fixture
def project_admin():
return AccessContext(user_id="pa1", role="project_admin", org_id="tenant_a")
@pytest.fixture
def member():
return AccessContext(user_id="m1", role="member", org_id="tenant_a")
@pytest.fixture
def other_tenant_member():
return AccessContext(user_id="other1", role="member", org_id="tenant_b")
@pytest.fixture
def guest():
return AccessContext(user_id="guest1", role="guest", org_id="tenant_a")
def test_task_lifecycle(pm_store, project_admin, member):
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Alpha", "status": "active", "members": ["m1", "pa1"]},
org_id="tenant_a",
), project_admin)
task = pm_store.create(DataObject(
type_name="task",
content={"title": "Build feature", "status": "todo", "priority": "high",
"assignee_id": "m1", "project_id": proj.id},
org_id="tenant_a",
), member)
# Valid transitions
pm_store.update(task.id, {"status": "in_progress"}, member)
pm_store.update(task.id, {"status": "review"}, member)
pm_store.update(task.id, {"status": "done"}, member)
def test_invalid_task_transition(pm_store, project_admin, member):
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Beta", "status": "active", "members": ["m1", "pa1"]},
org_id="tenant_a",
), project_admin)
task = pm_store.create(DataObject(
type_name="task",
content={"title": "Bug fix", "status": "todo", "priority": "medium",
"project_id": proj.id},
org_id="tenant_a",
), member)
# Can't go from todo -> done (must go through in_progress, review)
with pytest.raises(ValidationError, match="Invalid task transition"):
pm_store.update(task.id, {"status": "done"}, member)
def test_invalid_priority(pm_store, project_admin, member):
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Gamma", "status": "active", "members": ["m1"]},
org_id="tenant_a",
), project_admin)
with pytest.raises(ValidationError, match="Invalid priority"):
pm_store.create(DataObject(
type_name="task",
content={"title": "Task", "status": "todo", "priority": "URGENT",
"project_id": proj.id},
org_id="tenant_a",
), member)
def test_assignee_must_be_member(pm_store, project_admin, member):
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Delta", "status": "active", "members": ["m1"]},
org_id="tenant_a",
), project_admin)
with pytest.raises(ValidationError, match="not a member"):
pm_store.create(DataObject(
type_name="task",
content={"title": "Task", "status": "todo", "priority": "low",
"assignee_id": "nonexistent_user", "project_id": proj.id},
org_id="tenant_a",
), member)
def test_tenant_isolation(pm_store, project_admin, other_tenant_member):
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Secret Project", "status": "active", "members": ["pa1"]},
org_id="tenant_a",
), project_admin)
# Other tenant can't read the project
with pytest.raises(PermissionDeniedError):
pm_store.get(proj.id, other_tenant_member)
def test_guest_cannot_modify_task(pm_store, project_admin, guest):
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Public", "status": "active", "members": ["pa1", "guest1"]},
org_id="tenant_a",
), project_admin)
task = pm_store.create(DataObject(
type_name="task",
content={"title": "Read-only task", "status": "todo", "priority": "low",
"project_id": proj.id},
org_id="tenant_a",
), project_admin)
# Guest can read
read = pm_store.get(task.id, guest)
assert read is not None
# Guest cannot modify
with pytest.raises(PermissionDeniedError):
pm_store.update(task.id, {"status": "in_progress"}, guest)
def test_temporal_guest_access(pm_store, project_admin):
"""Test that guest access with expired temporal bounds is denied."""
proj = pm_store.create(DataObject(
type_name="project",
content={"name": "Temporal", "status": "active", "members": ["pa1"]},
org_id="tenant_a",
), project_admin)
task = pm_store.create(DataObject(
type_name="task",
content={"title": "Temporal task", "status": "todo", "priority": "low",
"project_id": proj.id},
org_id="tenant_a",
# Override permission rules with temporal bound for guest
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ,
{"roles": ["member", "project_admin", "org_admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.WRITE,
{"roles": ["member", "project_admin", "org_admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.READ,
{"role": "guest"},
valid_from=time.time() - 200,
valid_until=time.time() - 100), # expired
],
), project_admin)
expired_guest = AccessContext(user_id="old_guest", role="guest", org_id="tenant_a")
with pytest.raises(PermissionDeniedError):
pm_store.get(task.id, expired_guest)