import importlib.util from pathlib import Path import threading import unittest from munch import Munch from mosfet.info import blocks from mosfet.protocol.types import Slot ENTRY = Path(__file__).resolve().parents[1] / 'mosfet.py' SPEC = importlib.util.spec_from_file_location('mosfet_web_entry', ENTRY) mosfet_web = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(mosfet_web) class FakeGame: def __init__(self): self.drop_calls = [] self.dump_item = None self.dump_result = 2 def drop_inventory_slot(self, slot, whole_stack=True): self.drop_calls.append((slot, whole_stack)) return True def start_dump(self, item_id): self.dump_item = item_id return self.dump_result class TestWebInventoryAPI(unittest.TestCase): def setUp(self): self.g = mosfet_web.g self.original = { key: self.g.get(key) for key in ('connected', 'game', 'inv', 'chest_viewers', 'time', 'time_base', 'chunks', 'pos', 'yaw') } self.game = FakeGame() self.g.connected = True self.g.game = self.game self.g.inv = { 9: Slot(True, 1145, 3, components={'custom_name': {'text': 'Test Paper'}}) } self.g.reload_lock = threading.RLock() self.client = mosfet_web.app.test_client() def tearDown(self): for key, value in self.original.items(): self.g[key] = value def test_inventory_api_exposes_slot_components(self): response = self.client.get('/api/global') self.assertEqual(response.status_code, 200) item = response.get_json()['inv']['9'] self.assertEqual(item['components']['custom_name']['text'], 'Test Paper') def test_minimap_api_returns_surface_blocks_centered_on_bot(self): grass = blocks.MCD_BLOCKS['grass_block']['minStateId'] class FakeChunk: sections = { 0: {'non_empty_blocks': 1}, 1: {'non_empty_blocks': 0}, } @staticmethod def get_block_at(x, y, z): return grass if (x, y, z) == (1, 5, 1) else blocks.AIR class FakeChunks: min_y = 0 chunks = {(0, 0): FakeChunk()} self.g.connected = True self.g.chunks = FakeChunks() self.g.pos = Munch(x=1.5, y=6.0, z=1.5) self.g.yaw = 90 response = self.client.get( '/api/minimap?radius=4&radius_x=6&radius_z=4') self.assertEqual(response.status_code, 200) data = response.get_json() self.assertTrue(data['ready']) self.assertEqual(data['center'], {'x': 1, 'y': 6, 'z': 1}) self.assertEqual(data['yaw'], 90) self.assertEqual(data['radius_x'], 6) self.assertEqual(data['radius_z'], 4) self.assertEqual(len(data['cells']), 9) self.assertEqual(len(data['cells'][0]), 13) self.assertEqual(data['cells'][4][6], {'block': 'grass_block', 'y': 5}) def test_global_api_exposes_world_time_ticks(self): self.g.time = 12345.5 self.g.time_base = 12345.5 response = self.client.get('/api/global') self.assertEqual(response.status_code, 200) self.assertEqual(response.get_json()['time'], 12345.5) self.assertEqual(response.get_json()['time_base'], 12345.5) def test_global_api_stringifies_tuple_keys(self): self.g.chest_viewers = {(12, 64, -7): 1} response = self.client.get('/api/global') self.assertEqual(response.status_code, 200) self.assertEqual(response.get_json()['chest_viewers'], { '(12, 64, -7)': 1, }) def test_drop_endpoint_supports_one_stack_and_matching_actions(self): one = self.client.post('/api/inventory/drop', json={ 'slot': 9, 'action': 'one'}) stack = self.client.post('/api/inventory/drop', json={ 'slot': 9, 'action': 'stack'}) matching = self.client.post('/api/inventory/drop', json={ 'slot': 9, 'action': 'matching'}) self.assertEqual(one.status_code, 200) self.assertEqual(stack.status_code, 200) self.assertEqual(matching.status_code, 200) self.assertEqual(self.game.drop_calls, [(9, False), (9, True)]) self.assertEqual(self.game.dump_item, 1145) self.assertEqual(matching.get_json()['stacks'], 2) def test_drop_endpoint_rejects_cross_origin_actions(self): response = self.client.post('/api/inventory/drop', json={ 'slot': 9, 'action': 'stack'}, headers={'Origin': 'https://elsewhere.example'}) self.assertEqual(response.status_code, 403) if __name__ == '__main__': unittest.main()