Complete independent projection runtime
This commit is contained in:
parent
1134c2d375
commit
f1fabaf0ca
38 changed files with 4907 additions and 87 deletions
|
|
@ -30,6 +30,8 @@ from docforge.projection_contract import (
|
|||
canonical_projection_bytes,
|
||||
projection_hash,
|
||||
)
|
||||
from docforge.projection_fragments import FRAGMENT_CACHE_DIRECTORY, FragmentRecord
|
||||
from docforge.projection_worker import render_projection_in_worker
|
||||
from docforge.render_contract import GenericHtmlRenderer
|
||||
from docforge_renderers.manual import ManualHtmlRenderer
|
||||
|
||||
|
|
@ -412,6 +414,171 @@ class ProjectionContractTests(unittest.TestCase):
|
|||
prepared.output,
|
||||
)
|
||||
|
||||
def test_manual_fragments_are_incremental_and_full_worker_output_is_the_oracle(
|
||||
self,
|
||||
) -> None:
|
||||
renderer = GenericHtmlRenderer()
|
||||
with mock.patch(
|
||||
"docforge.render_contract.render_projection_in_worker",
|
||||
wraps=render_projection_in_worker,
|
||||
) as detached:
|
||||
cold = renderer.prepare(
|
||||
self.snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(2, detached.call_count)
|
||||
fragment_root = self.snapshot.descriptor.cache_root / FRAGMENT_CACHE_DIRECTORY
|
||||
self.assertEqual(
|
||||
len(self.plan.document["pages"]),
|
||||
len(list(fragment_root.glob("*.json"))),
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"docforge.render_contract.render_projection_in_worker",
|
||||
wraps=render_projection_in_worker,
|
||||
) as detached:
|
||||
warm = renderer.prepare(
|
||||
self.snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(1, detached.call_count)
|
||||
self.assertEqual(cold.output, warm.output)
|
||||
|
||||
with mock.patch(
|
||||
"docforge.render_contract.render_projection_in_worker",
|
||||
wraps=render_projection_in_worker,
|
||||
) as detached:
|
||||
full = GenericHtmlRenderer(incremental=False).prepare(
|
||||
self.snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(1, detached.call_count)
|
||||
self.assertEqual(full.output, warm.output)
|
||||
|
||||
first_fragment = sorted(fragment_root.glob("*.json"))[0]
|
||||
first_fragment.write_bytes(b"{corrupt")
|
||||
with mock.patch(
|
||||
"docforge.render_contract.render_projection_in_worker",
|
||||
wraps=render_projection_in_worker,
|
||||
) as detached:
|
||||
recovered = renderer.prepare(
|
||||
self.snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(2, detached.call_count)
|
||||
self.assertEqual(full.output, recovered.output)
|
||||
|
||||
original_record = FragmentRecord.from_bytes(first_fragment.read_bytes())
|
||||
first_fragment.write_bytes(
|
||||
FragmentRecord.create(
|
||||
original_record.key,
|
||||
b"<script>forged fragment</script>",
|
||||
).to_bytes()
|
||||
)
|
||||
with mock.patch(
|
||||
"docforge.render_contract.render_projection_in_worker",
|
||||
wraps=render_projection_in_worker,
|
||||
) as detached:
|
||||
forged = renderer.prepare(
|
||||
self.snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(2, detached.call_count)
|
||||
self.assertEqual(full.output, forged.output)
|
||||
self.assertNotIn(b"forged fragment", forged.output)
|
||||
|
||||
def test_oversized_manual_fragment_bypasses_cache_and_uses_full_worker(self) -> None:
|
||||
content = "x" * 4_100_000
|
||||
node = replace(
|
||||
self.snapshot.nodes[0],
|
||||
content=content,
|
||||
content_hash=hashlib.sha256(content.encode()).hexdigest(),
|
||||
)
|
||||
snapshot = replace(
|
||||
self.snapshot,
|
||||
descriptor=replace(
|
||||
self.snapshot.descriptor,
|
||||
limits=replace(
|
||||
self.snapshot.descriptor.limits,
|
||||
max_render_bytes=20_000_000,
|
||||
),
|
||||
),
|
||||
nodes=(node,),
|
||||
edges=(),
|
||||
source_hash=hashlib.sha256(b"oversized-fragment").hexdigest(),
|
||||
)
|
||||
with mock.patch(
|
||||
"docforge.render_contract.render_projection_in_worker",
|
||||
wraps=render_projection_in_worker,
|
||||
) as detached:
|
||||
prepared = GenericHtmlRenderer().prepare(
|
||||
snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(1, detached.call_count)
|
||||
full = GenericHtmlRenderer(incremental=False).prepare(
|
||||
snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertEqual(full.output, prepared.output)
|
||||
|
||||
def test_fragment_package_overflow_falls_back_to_full_worker(self) -> None:
|
||||
content = "x" * 2_700_000
|
||||
base = self.snapshot.nodes[0]
|
||||
nodes = tuple(
|
||||
replace(
|
||||
base,
|
||||
node_id=f"guide.large-{index}",
|
||||
title=f"Large {index}",
|
||||
content=content,
|
||||
source_path=f"docs/content/large-{index}.md",
|
||||
content_hash=hashlib.sha256(f"{index}:{content}".encode()).hexdigest(),
|
||||
)
|
||||
for index in range(4)
|
||||
)
|
||||
descriptor = replace(
|
||||
self.snapshot.descriptor,
|
||||
limits=replace(
|
||||
self.snapshot.descriptor.limits,
|
||||
max_render_bytes=20_000_000,
|
||||
),
|
||||
)
|
||||
snapshot = replace(
|
||||
self.snapshot,
|
||||
descriptor=descriptor,
|
||||
nodes=nodes,
|
||||
edges=(),
|
||||
source_hash=hashlib.sha256(b"large-fragment-fixture").hexdigest(),
|
||||
)
|
||||
incremental = GenericHtmlRenderer().prepare(
|
||||
snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
full = GenericHtmlRenderer(incremental=False).prepare(
|
||||
snapshot,
|
||||
self.view,
|
||||
self.template,
|
||||
changeset_hash=None,
|
||||
)
|
||||
self.assertGreater(len(full.output), 10_000_000)
|
||||
self.assertEqual(full.output, incremental.output)
|
||||
|
||||
def test_manual_renderer_rejects_project_provided_active_content(self) -> None:
|
||||
for active in (
|
||||
"<script>alert(1)</script>{{ docforge_content }}",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue