Files
Emil 89c53d7058 Expand material support and checkpoint the completed building toolkit
Expose the runtime block/item registry through compact material search and
single-material descriptions. Preserve private block-entity data through
checked edits and durable undo without sending payloads to model context.

Include the completed terrain tools, isolated world/map plugin, station lift,
ACP streaming and guidance fixes, local camera auto-connect, construction
scripts, and their public documentation, references and verification records.
Active station decoration and private runtime data remain outside this commit.

Validation: 145 Maven tests, 47 Bridge tests, successful camera Gradle build,
and isolated Paper verification of all 1,196 block defaults plus 5,392
independent property cases for placement, same-material edits and restoration.
2026-09-13 16:59:13 +03:00

200 lines
10 KiB
Python

"""Read-only survey completeness, cache isolation and path-height regression tests."""
import importlib.util
import json
from pathlib import Path
import tempfile
import unittest
_spec = importlib.util.spec_from_file_location('foundation_survey', Path(__file__).with_name('foundation-survey.py'))
survey = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(survey)
SCOPE = {'project_id': 'project', 'world_id': 'world', 'world_epoch': 'epoch'}
class Backend:
def __init__(self, states=None):
self.states = states or {}
self.calls = []
self.mutate = None
self.fail_call = None
self.context = {**SCOPE, 'region': {'min': {'x': -64, 'y': -64, 'z': -64},
'max': {'x': 63, 'y': 127, 'z': 63}}}
def call(self, method, **params):
self.calls.append(method)
if method == 'project_context':
return self.context
if method != 'region_inspect':
raise AssertionError('Only read-only RPC is allowed')
if self.fail_call == self.calls.count('region_inspect'):
raise RuntimeError('chunk_not_loaded')
if survey.volume(params) > 4096:
raise AssertionError('Unbounded inspection')
lo, hi = params['min'], params['max']
result = {'world_epoch': self.context['world_epoch'], 'truncated': False,
'blocks': [{'pos': {'x': x, 'y': y, 'z': z}, 'state': self.states.get((x, y, z), 'minecraft:air')}
for x in range(lo['x'], hi['x'] + 1) for y in range(lo['y'], hi['y'] + 1)
for z in range(lo['z'], hi['z'] + 1)]}
if self.mutate:
self.mutate(result)
return result
def box(lo=(-2, 64, -2), hi=(2, 68, 2)):
return survey.box_cells(dict(zip(survey.AXES, lo)), dict(zip(survey.AXES, hi)))
class SurveyTests(unittest.TestCase):
def capture(self, backend=None, cells=None):
backend = backend or Backend()
cells = cells or box()
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / 'survey.json.gz'
result = survey.scan(backend, cells, path)
loaded = survey.load_snapshot(path, SCOPE)
self.assertEqual(loaded.document, result.document)
return loaded
def test_negative_cell_partition_and_exact_states(self):
states = {(-2, 64, -2): 'minecraft:smooth_stone_slab[type=bottom,waterlogged=false]'}
result = self.capture(Backend(states))
self.assertEqual(result.state(-2, 64, -2), states[(-2, 64, -2)])
self.assertEqual(result.state(2, 68, 2), 'minecraft:air')
self.assertEqual(len(result.cells), 4)
with self.assertRaises(KeyError):
result.state(-3, 64, -2)
with self.assertRaises(KeyError):
result.state(17, 64, 0)
def test_columns_do_not_assume_air_in_unread_cells(self):
cells = survey.column_cells({'version': 1, 'columns': [
{'x': -20, 'z': 1, 'min_y': 64, 'max_y': 68},
{'x': 20, 'z': 1, 'min_y': 66, 'max_y': 72}]})
result = self.capture(cells=cells)
self.assertEqual(result.state(20, 69, 1), 'minecraft:air')
with self.assertRaises(KeyError):
result.state(0, 69, 1)
with self.assertRaises(KeyError):
result.state(20, 65, 1)
def test_rejects_missing_duplicate_truncated_and_wrong_epoch(self):
changes = [lambda r: r['blocks'].pop(),
lambda r: r['blocks'].append(r['blocks'][0]),
lambda r: r.update(truncated=True),
lambda r: r.update(world_epoch='different')]
for mutate in changes:
with self.subTest(mutate=mutate), tempfile.TemporaryDirectory() as directory:
backend = Backend()
backend.mutate = mutate
path = Path(directory) / 'survey.json.gz'
with self.assertRaises(RuntimeError):
survey.scan(backend, box(), path)
self.assertFalse(path.exists())
def test_out_of_area_fails_before_any_inspection(self):
backend = Backend()
with tempfile.TemporaryDirectory() as directory:
with self.assertRaises(ValueError):
survey.scan(backend, box((-65, 64, 0), (-60, 68, 0)), Path(directory) / 'survey.json.gz')
self.assertEqual(backend.calls, ['project_context'])
def test_explicit_resume_keeps_readonly_cache_and_checks_scope(self):
backend = Backend()
backend.fail_call = 2
cells = box((-16, 64, 0), (16, 68, 0))
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / 'survey.json.gz'
with self.assertRaisesRegex(RuntimeError, 'chunk_not_loaded'):
survey.scan(backend, cells, path)
self.assertFalse(path.exists())
with self.assertRaisesRegex(ValueError, '--resume'):
survey.scan(backend, cells, path)
backend.context['world_epoch'] = 'another-epoch'
with self.assertRaisesRegex(ValueError, 'scope'):
survey.scan(backend, cells, path, resume=True)
backend.context['world_epoch'] = 'epoch'
backend.fail_call = None
result = survey.scan(backend, cells, path, resume=True)
self.assertTrue(result.document['resumed_cache'])
self.assertEqual(backend.calls.count('region_inspect'), 4)
with self.assertRaisesRegex(ValueError, 'fresh path'):
survey.scan(backend, cells, path, resume=True)
def test_corrupt_cache_and_missing_snapshot_indices_are_rejected(self):
snapshot = self.capture()
snapshot.document['cells'][0]['indices'].pop()
with self.assertRaisesRegex(ValueError, 'indices'):
survey.Snapshot(snapshot.document, SCOPE)
def test_foreign_scope_rejected(self):
snapshot = self.capture()
with self.assertRaisesRegex(ValueError, 'another project'):
survey.Snapshot(snapshot.document, {**SCOPE, 'project_id': 'other'})
def test_walkability_half_slab_fullblock_clearance_and_gradient(self):
states = {(0, 64, 0): 'minecraft:stone',
(1, 65, 0): 'minecraft:smooth_stone_slab[type=bottom,waterlogged=false]',
(2, 65, 0): 'minecraft:stone'}
snapshot = self.capture(Backend(states), box((0, 64, 0), (3, 69, 0)))
points = [{'x': x, 'z': 0, 'standing_y': feet, 'route': 'main'}
for x, feet in [(0, 65), (1, 65.5), (2, 66)]]
result = survey.verify_walkable(snapshot, points)
self.assertTrue(result['passed'])
self.assertEqual(result['checked_route_edges'], 2)
points[1]['standing_y'] = 66
result = survey.verify_walkable(snapshot, points)
self.assertFalse(result['passed'])
self.assertIn('missing_support_at_feet', [f['reason'] for f in result['failures']])
self.assertIn('route_step_exceeds_half_block', [f['reason'] for f in result['failures']])
def test_headroom_unknown_stairs_and_absent_observations_fail(self):
states = {(0, 64, 0): 'minecraft:stone', (0, 66, 0): 'minecraft:stone',
(1, 64, 0): 'minecraft:stone_brick_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]'}
snapshot = self.capture(Backend(states), box((0, 64, 0), (1, 68, 0)))
result = survey.verify_walkable(snapshot, [{'x': x, 'z': 0, 'standing_y': 65} for x in (0, 1, 2)])
self.assertEqual([f['reason'] for f in result['failures']],
['blocked_headroom', 'unknown_support_shape', 'unobserved_block'])
def test_straight_bottom_stair_profile_all_directions(self):
sides = {'north': ((.5, .75), (.5, .25)), 'south': ((.5, .25), (.5, .75)),
'east': ((.25, .5), (.75, .5)), 'west': ((.75, .5), (.25, .5))}
for facing, (low, high) in sides.items():
with self.subTest(facing=facing):
state = f'minecraft:stone_brick_stairs[facing={facing},half=bottom,shape=straight,waterlogged=false]'
snapshot = self.capture(Backend({(0, 64, 0): state}), box((0, 64, 0), (0, 68, 0)))
points = [{'x': 0, 'z': 0, 'sub_x': sub[0], 'sub_z': sub[1], 'standing_y': feet, 'route': facing}
for sub, feet in [(low, 64.5), (high, 65)]]
result = survey.verify_walkable(snapshot, points)
self.assertTrue(result['passed'], result)
self.assertEqual(result['checked_route_edges'], 1)
points[0]['standing_y'] = 65
self.assertEqual(survey.verify_walkable(snapshot, points)['failures'][0]['reason'], 'missing_support_at_feet')
def test_stair_headroom_riser_boundary_and_unsupported_shapes(self):
base = 'minecraft:stone_brick_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]'
snapshot = self.capture(Backend({(0, 64, 0): base, (0, 66, 0): 'minecraft:stone'}),
box((0, 64, 0), (0, 68, 0)))
result = survey.verify_walkable(snapshot, [{'x': 0, 'z': 0, 'sub_z': .25, 'standing_y': 65}])
self.assertEqual(result['failures'][0]['reason'], 'blocked_headroom')
self.assertIsNone(survey.vertical_shape(base, .5, .5))
self.assertIsNone(survey.vertical_shape(base.replace('straight', 'inner_left'), .5, .25))
self.assertIsNone(survey.vertical_shape(base.replace('half=bottom', 'half=top'), .5, .25))
self.assertIsNone(survey.vertical_shape(base.replace('waterlogged=false', 'waterlogged=true'), .5, .25))
def test_route_subsamples_use_physical_positions_across_blocks(self):
state = 'minecraft:stone_brick_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]'
snapshot = self.capture(Backend({(0, 64, 0): state, (0, 65, -1): state}),
box((0, 64, -1), (0, 70, 0)))
points = [{'x': 0, 'z': z, 'sub_z': sub_z, 'standing_y': feet, 'route': 'stair'}
for z, sub_z, feet in [(0, .75, 64.5), (0, .25, 65), (-1, .75, 65.5), (-1, .25, 66)]]
result = survey.verify_walkable(snapshot, points)
self.assertTrue(result['passed'], result)
self.assertEqual(result['checked_route_edges'], 3)
points[1]['sub_x'] = .6
result = survey.verify_walkable(snapshot, points)
self.assertIn('non_cardinal_route_step', [f['reason'] for f in result['failures']])
if __name__ == '__main__':
unittest.main()