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,145 @@
"""Tests for the core object store."""
import pytest
from pedo.core.models import (
AccessContext, DataObject, ObjectType, Operation,
PermissionRule, PrivilegeType, Relationship, RelationshipAction,
ReactionDeclaration,
)
from pedo.core.store import ObjectStore, PermissionDeniedError, ValidationError, ReferentialIntegrityError
DSN = "dbname=pedo_test"
@pytest.fixture
def store():
s = ObjectStore(DSN)
s.clear_all()
# Register a simple type
s.register_type(ObjectType(
name="document",
fields={"title": "str", "body": "str"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"role": "reader"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"role": "writer"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.WRITE, {"role": "writer"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"role": "writer"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"role": "reader"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"role": "writer"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.UPDATE, {"role": "writer"}),
PermissionRule(Operation.ACCEPT, PrivilegeType.DELETE, {"role": "writer"}),
],
default_policy=Operation.DENY,
))
return s
@pytest.fixture
def writer_ctx():
return AccessContext(user_id="user1", role="writer", org_id="org1")
@pytest.fixture
def reader_ctx():
return AccessContext(user_id="user2", role="reader", org_id="org1")
@pytest.fixture
def anon_ctx():
return AccessContext(user_id="anon", role="anonymous")
def test_create_and_read(store, writer_ctx, reader_ctx):
obj = DataObject(type_name="document", content={"title": "Hello", "body": "World"})
created = store.create(obj, writer_ctx)
assert created.id == obj.id
read = store.get(created.id, reader_ctx)
assert read is not None
assert read.content["title"] == "Hello"
def test_permission_denied_on_read(store, writer_ctx, anon_ctx):
obj = DataObject(type_name="document", content={"title": "Secret"})
created = store.create(obj, writer_ctx)
with pytest.raises(PermissionDeniedError):
store.get(created.id, anon_ctx)
def test_permission_denied_on_write(store, writer_ctx, reader_ctx):
obj = DataObject(type_name="document", content={"title": "Hello"})
created = store.create(obj, writer_ctx)
with pytest.raises(PermissionDeniedError):
store.update(created.id, {"title": "Changed"}, reader_ctx)
def test_update(store, writer_ctx, reader_ctx):
obj = DataObject(type_name="document", content={"title": "V1", "body": "text"})
created = store.create(obj, writer_ctx)
updated = store.update(created.id, {"title": "V2"}, writer_ctx)
assert updated.content["title"] == "V2"
assert updated.content["body"] == "text"
def test_delete(store, writer_ctx, reader_ctx):
obj = DataObject(type_name="document", content={"title": "ToDelete"})
created = store.create(obj, writer_ctx)
store.delete(created.id, writer_ctx)
assert store.raw_read(created.id) is None
def test_validator_rejects(store, writer_ctx):
def no_empty_title(proposed, existing, accessor, st):
if not proposed.content.get("title"):
return "Title cannot be empty"
return True
store.types["document"].validators = [no_empty_title]
obj = DataObject(type_name="document", content={"title": "", "body": "text"})
with pytest.raises(ValidationError, match="Title cannot be empty"):
store.create(obj, writer_ctx)
def test_reactions_fire(store, writer_ctx):
log = []
def on_create(event, st):
log.append(event["event"])
store.types["document"].reactions = [
ReactionDeclaration(event="after_create", handler="log_create"),
]
store.register_reaction_handler("log_create", on_create)
obj = DataObject(type_name="document", content={"title": "Test"})
store.create(obj, writer_ctx)
store.process_reactions_sync()
assert "after_create" in log
def test_temporal_permission():
"""Test that time-bounded permissions work."""
import time
rule = PermissionRule(
Operation.ACCEPT, PrivilegeType.READ,
condition={"role": "guest"},
valid_from=time.time() - 100,
valid_until=time.time() + 100,
)
ctx = AccessContext(user_id="g", role="guest")
assert rule.matches(ctx, PrivilegeType.READ, time.time()) is True
expired_rule = PermissionRule(
Operation.ACCEPT, PrivilegeType.READ,
condition={"role": "guest"},
valid_from=time.time() - 200,
valid_until=time.time() - 100,
)
assert expired_rule.matches(ctx, PrivilegeType.READ, time.time()) is False
@@ -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)