extends GutTest const WORLD_ID := &"regional_world" const ORIGIN_LOCATION_ID := &"location_origin" const DESTINATION_LOCATION_ID := &"location_destination" const ORIGIN_SETTLEMENT_ID := &"settlement_origin" const DESTINATION_SETTLEMENT_ID := &"settlement_destination" const ROUTE_ID := &"route_trade" const DEPARTURE_TICK := 10 const ARRIVAL_TICK := 15 func test_mid_route_round_trip_preserves_exact_envelope_and_continuation() -> void: var service := _fixture_service(1) assert_true(_depart_group(service, 0)) var expected_envelope := service.to_dictionary() var archive := RegionalChunkedPersistence.capture(service, 1) assert_not_null(archive) assert_eq(archive.get_manifest()["envelope_checksum"], service.checksum()) assert_eq(archive.get_chunk_ids_for_type(RegionalChunkedPersistence.CHUNK_EVENTS).size(), 2) var restored := archive.restore_service() assert_not_null(restored) assert_eq(restored.to_dictionary(), expected_envelope) assert_eq(restored.checksum(), service.checksum()) assert_eq(service.process_due(ARRIVAL_TICK, 1).size(), 1) assert_eq(restored.process_due(ARRIVAL_TICK, 1).size(), 1) assert_eq(restored.to_dictionary(), service.to_dictionary()) func test_active_location_metadata_does_not_hydrate_or_discard_unloaded_authority() -> void: var service := _fixture_service(2) var archive := RegionalChunkedPersistence.capture(service) assert_not_null(archive) var archive_before := archive.to_dictionary() var partial := archive.load_active_location_metadata(ORIGIN_LOCATION_ID) assert_false(partial.is_empty()) assert_eq( partial["loaded_chunk_ids"], [ RegionalChunkedPersistence.GLOBAL_CHUNK_ID, "location:%s" % ORIGIN_LOCATION_ID, ] ) assert_eq(partial["mobile_group_refs"].size(), 2) assert_true("scheduler:regional" in partial["unloaded_chunk_ids"]) assert_true("mobile_group:group_00" in partial["unloaded_chunk_ids"]) assert_false((partial["global_index"] as Dictionary).has("mobile_groups")) (partial["active_location"] as Dictionary)["settlements"] = [] (partial["unloaded_chunk_ids"] as Array).clear() assert_eq(archive.to_dictionary(), archive_before) assert_eq(archive.restore_service().checksum(), service.checksum()) func test_missing_duplicate_tampered_and_noncanonical_chunks_are_rejected() -> void: var archive := RegionalChunkedPersistence.capture(_fixture_service(1)) assert_not_null(archive) var missing := archive.to_dictionary() (missing["chunks"] as Array).pop_back() assert_null(RegionalChunkedPersistence.from_dictionary(missing)) var duplicate := archive.to_dictionary() var duplicated_chunk: Dictionary = (duplicate["chunks"] as Array)[-1].duplicate(true) var duplicated_descriptor: Dictionary = ( ((duplicate["manifest"] as Dictionary)["chunks"] as Array)[-1].duplicate(true) ) (duplicate["chunks"] as Array).append(duplicated_chunk) ((duplicate["manifest"] as Dictionary)["chunks"] as Array).append(duplicated_descriptor) (duplicate["manifest"] as Dictionary)["chunk_count"] = ((duplicate["chunks"] as Array).size()) assert_null(RegionalChunkedPersistence.from_dictionary(duplicate)) var tampered_payload := archive.to_dictionary() var origin_chunk := _find_chunk(tampered_payload, "location:%s" % ORIGIN_LOCATION_ID) (origin_chunk["payload"] as Dictionary)["location_id"] = "location_forged" assert_null(RegionalChunkedPersistence.from_dictionary(tampered_payload)) var tampered_checksum := archive.to_dictionary() var descriptors: Array = (tampered_checksum["manifest"] as Dictionary)["chunks"] descriptors[0]["checksum"] = "forged" assert_null(RegionalChunkedPersistence.from_dictionary(tampered_checksum)) var self_consistent_but_noncanonical := archive.to_dictionary() var noncanonical_chunk := _find_chunk( self_consistent_but_noncanonical, "location:%s" % ORIGIN_LOCATION_ID ) (noncanonical_chunk["payload"] as Dictionary)["ignored_authority"] = true _refresh_chunk_descriptor(self_consistent_but_noncanonical, noncanonical_chunk) assert_null(RegionalChunkedPersistence.from_dictionary(self_consistent_but_noncanonical)) var noncanonical := archive.to_dictionary() var chunks: Array = noncanonical["chunks"] var noncanonical_descriptors: Array = (noncanonical["manifest"] as Dictionary)["chunks"] var first_chunk: Variant = chunks[0] chunks[0] = chunks[1] chunks[1] = first_chunk var first_descriptor: Variant = noncanonical_descriptors[0] noncanonical_descriptors[0] = noncanonical_descriptors[1] noncanonical_descriptors[1] = first_descriptor assert_null(RegionalChunkedPersistence.from_dictionary(noncanonical)) func test_payload_contract_rejects_nodes_resources_and_node_paths() -> void: var archive := RegionalChunkedPersistence.capture(_fixture_service(1)) assert_not_null(archive) assert_true(_contains_only_primitive_values(archive.to_dictionary())) var node_bundle := archive.to_dictionary() var node := Node.new() (_first_chunk_payload(node_bundle))["forbidden"] = node assert_null(RegionalChunkedPersistence.from_dictionary(node_bundle)) node.free() var resource_bundle := archive.to_dictionary() (_first_chunk_payload(resource_bundle))["forbidden"] = Resource.new() assert_null(RegionalChunkedPersistence.from_dictionary(resource_bundle)) var path_bundle := archive.to_dictionary() (_first_chunk_payload(path_bundle))["forbidden"] = NodePath("World/Caravan") assert_null(RegionalChunkedPersistence.from_dictionary(path_bundle)) func test_twenty_caravans_remain_structurally_chunked_and_bounded() -> void: var service := _fixture_service(20) for index in range(20): assert_true(_depart_group(service, index)) var archive := RegionalChunkedPersistence.capture(service, 8) assert_not_null(archive) assert_eq( archive.get_chunk_ids_for_type(RegionalChunkedPersistence.CHUNK_MOBILE_GROUP).size(), 20 ) assert_eq(archive.get_chunk_ids_for_type(RegionalChunkedPersistence.CHUNK_EVENTS).size(), 5) assert_eq(archive.get_manifest()["chunk_count"], 29) for event_chunk_id: String in archive.get_chunk_ids_for_type( RegionalChunkedPersistence.CHUNK_EVENTS ): assert_true( ( (archive.get_chunk(StringName(event_chunk_id))["payload"]["events"] as Array).size() <= 8 ) ) var encoded_size := JSON.stringify(archive.to_dictionary()).to_utf8_buffer().size() assert_lt(encoded_size, 250_000) assert_eq(archive.restore_service().checksum(), service.checksum()) func _fixture_service(group_count: int) -> RegionalCaravanService: var origin_location := LocationStateRecord.create( ORIGIN_LOCATION_ID, WORLD_ID, &"settlement", "Origin", _address(ORIGIN_LOCATION_ID) ) var destination_location := LocationStateRecord.create( DESTINATION_LOCATION_ID, WORLD_ID, &"settlement", "Destination", _address(DESTINATION_LOCATION_ID) ) var origin := SettlementStateRecord.create( ORIGIN_SETTLEMENT_ID, ORIGIN_LOCATION_ID, "Origin", &"polity_origin", 1, &"founders_origin", 0, {&"food": float(group_count * 2 + 10)} ) var destination := SettlementStateRecord.create( DESTINATION_SETTLEMENT_ID, DESTINATION_LOCATION_ID, "Destination", &"polity_destination", 1, &"founders_destination", 0, {&"food": 4.0} ) var route := RouteStateRecord.create(ROUTE_ID, ORIGIN_LOCATION_ID, DESTINATION_LOCATION_ID, 5) var groups: Array[MobileGroupStateRecord] = [] for index in range(group_count): groups.append( MobileGroupStateRecord.create_at_location( _group_id(index), &"caravan", "Caravan %02d" % index, &"polity_origin", _address(ORIGIN_LOCATION_ID), [], {}, {}, 2.0 ) ) var origin_polity := PolityStateRecord.create( &"polity_origin", "Origin polity", ORIGIN_LOCATION_ID, [ORIGIN_SETTLEMENT_ID] ) var destination_polity := PolityStateRecord.create( &"polity_destination", "Destination polity", DESTINATION_LOCATION_ID, [DESTINATION_SETTLEMENT_ID] ) var locations: Array[LocationStateRecord] = [destination_location, origin_location] var settlements: Array[SettlementStateRecord] = [destination, origin] var routes: Array[RouteStateRecord] = [route] var persons: Array[PersonStateRecord] = [] var cohorts: Array[PopulationCohortRecord] = [] var polities: Array[PolityStateRecord] = [destination_polity, origin_polity] var relations: Array[DiplomaticRelationRecord] = [] var world_state := RegionalWorldState.create( WORLD_ID, locations, settlements, routes, groups, persons, cohorts, polities, relations ) return RegionalCaravanService.create( world_state, RegionalJobScheduler.new(), WorldEventStore.new() ) func _depart_group(service: RegionalCaravanService, index: int) -> bool: return service.depart( _group_id(index), ORIGIN_SETTLEMENT_ID, DESTINATION_SETTLEMENT_ID, ROUTE_ID, {&"food": 1.0}, DEPARTURE_TICK ) func _group_id(index: int) -> StringName: return StringName("group_%02d" % index) func _address(location_id: StringName) -> SpatialAddress: return SpatialAddress.create(WORLD_ID, location_id, Vector3.ZERO) func _find_chunk(bundle: Dictionary, chunk_id: String) -> Dictionary: for raw_chunk: Variant in bundle["chunks"]: if raw_chunk is Dictionary and String(raw_chunk.get("chunk_id", "")) == chunk_id: return raw_chunk return {} func _first_chunk_payload(bundle: Dictionary) -> Dictionary: return ((bundle["chunks"] as Array)[0] as Dictionary)["payload"] func _refresh_chunk_descriptor(bundle: Dictionary, chunk: Dictionary) -> void: var serialized := JSON.stringify(chunk) for raw_descriptor: Variant in (bundle["manifest"] as Dictionary)["chunks"]: if ( raw_descriptor is Dictionary and String(raw_descriptor["chunk_id"]) == String(chunk["chunk_id"]) ): raw_descriptor["byte_size"] = serialized.to_utf8_buffer().size() raw_descriptor["checksum"] = serialized.sha256_text() return func _contains_only_primitive_values(value: Variant) -> bool: match typeof(value): TYPE_NIL, TYPE_BOOL, TYPE_INT, TYPE_STRING: return true TYPE_FLOAT: return is_finite(float(value)) TYPE_ARRAY: for item: Variant in value: if not _contains_only_primitive_values(item): return false return true TYPE_DICTIONARY: for key: Variant in value: if not key is String or not _contains_only_primitive_values(value[key]): return false return true return false