XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
package com.xfestudio.xfeservermanager.core.content;

import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.xfestudio.xfeservermanager.core.content.ContentDefinition.Kind.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ContentDefinitionTest {
    @Test
    void catalogDefaultsAndTemplatesAreAcceptedByTheSameStrictSchema() {
        ContentCatalog catalog = ContentCatalog.builtIn();
        assertThat(catalog.schemaVersion()).isEqualTo(1);
        assertThat(catalog.archetypes()).hasSize(19);
        for (var archetype : catalog.archetypes()) {
            Map<String, String> properties = new HashMap<>();
            archetype.properties().forEach(property -> properties.put(property.key(), property.defaultValue()));
            new ContentDefinition("xfesmcontent:" + archetype.id(), archetype.kind(), "Test", "", archetype.id(), properties, List.of());
            assertThat(archetype.nameZh()).isNotBlank();
            assertThat(archetype.descriptionZh()).isNotBlank();
            assertThat(archetype.properties()).allMatch(property -> !property.nameZh().isBlank() && !property.descriptionZh().isBlank());
        }
        assertThat(catalog.templates()).hasSize(2);
        assertThat(catalog.actions()).extracting(ContentCatalog.Action::id).containsExactly("trigger", "open_menu", "sit", "play_sound", "stop_sound");
    }

    @Test
    void rejectsUnknownCapabilitiesAndUnsafeResources() {
        assertThatThrownBy(() -> item("minecraft:stone", "generic", Map.of(), List.of())).hasMessageContaining("xfesmcontent");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of("nbt", "{}"), List.of())).hasMessageContaining("unsupported property");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of("texture", "https://example.com/a.png"), List.of())).hasMessageContaining("texture");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of("texture", "xfesmcontent:../secret"), List.of())).hasMessageContaining("texture");
        assertThatThrownBy(() -> item("xfesmcontent:test", "script_item", Map.of(), List.of())).hasMessageContaining("unsupported content archetype");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of(), List.of(behavior("right_click", "java", Map.of(), false))))
                .hasMessageContaining("unsupported behavior action");
    }

    @Test
    void enforcesItemAndNumericConstraintsWithoutNaNOrOverflow() {
        assertThatThrownBy(() -> item("xfesmcontent:test", "sword", Map.of("max_stack_size", "64"), List.of())).hasMessageContaining("max_stack_size");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of("durability", "100"), List.of())).hasMessageContaining("max_stack_size=1");
        assertThatThrownBy(() -> item("xfesmcontent:test", "food", Map.of("durability", "100"), List.of())).hasMessageContaining("unsupported property");
        assertThatThrownBy(() -> item("xfesmcontent:test", "sword", Map.of("attack_damage", "1.5"), List.of())).hasMessageContaining("integer");
        assertThatThrownBy(() -> item("xfesmcontent:test", "sword", Map.of("attack_speed", "NaN"), List.of())).hasMessageContaining("attack_speed");
        assertThatThrownBy(() -> item("xfesmcontent:test", "sword", Map.of("attack_speed", "1e999"), List.of())).hasMessageContaining("out of range");
        item("xfesmcontent:test", "shovel", Map.of("attack_damage", "1.5"), List.of());
    }

    @Test
    void enforcesEventApplicabilityAndRequiredReferences() {
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of(), List.of(behavior("right_click", "sit", Map.of(), false))))
                .hasMessageContaining("not available");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of(), List.of(behavior("consume", "stop_sound", Map.of("sound", "minecraft:test"), false))))
                .hasMessageContaining("not available");
        assertThatThrownBy(() -> item("xfesmcontent:test", "food", Map.of(), List.of(behavior("consume", "stop_sound", Map.of("sound", "minecraft:test"), true))))
                .hasMessageContaining("cannot cancel");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of(), List.of(behavior("right_click", "open_menu", Map.of(), false))))
                .hasMessageContaining("menu_id");
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of(), List.of(behavior("right_click", "stop_sound", Map.of(), false))))
                .hasMessageContaining("sound");
    }

    @Test
    void rejectsInvalidShapesAndUnsupportedAnimalAttributes() {
        assertThatThrownBy(() -> new ContentDefinition("xfesmcontent:test", BLOCK, "Test", "", "solid", Map.of("shape_min_x", "16"), List.of()))
                .hasMessageContaining("shape_min_x");
        assertThatThrownBy(() -> new ContentDefinition("xfesmcontent:test", ENTITY, "Test", "", "cow", Map.of("attack_damage", "20"), List.of()))
                .hasMessageContaining("unsupported property");
        assertThatThrownBy(() -> new ContentDefinition("xfesmcontent:test", ENTITY, "Test", "", "zombie", Map.of("texture", "minecraft:entity/zombie"), List.of()))
                .hasMessageContaining("unsupported property");
    }

    @Test
    void enforcesSharedNativeAttributeLimitsBeforeMinecraftCanSilentlyClampThem() {
        for (var entry : Map.of("max_health", "1025", "armor", "31", "attack_damage", "2049").entrySet()) {
            assertThatThrownBy(() -> new ContentDefinition("xfesmcontent:test", ENTITY, "Test", "", "zombie",
                    Map.of(entry.getKey(), entry.getValue()), List.of())).hasMessageContaining("out of range");
        }
        new ContentDefinition("xfesmcontent:test", ENTITY, "Test", "", "zombie",
                Map.of("max_health", "1024", "armor", "30", "attack_damage", "2048"), List.of());
        assertThatThrownBy(() -> item("xfesmcontent:test", "sword", Map.of("attack_damage", "2044"), List.of()))
                .hasMessageContaining("out of range");
        assertThatThrownBy(() -> item("xfesmcontent:test", "shovel", Map.of("attack_damage", "2043.1"), List.of()))
                .hasMessageContaining("out of range");
        item("xfesmcontent:test", "sword", Map.of("tier", "netherite", "attack_damage", "2043"), List.of());
        assertThat(ContentCatalog.archetype(ITEM, "sword").properties()).anySatisfy(property -> {
            assertThat(property.key()).isEqualTo("attack_damage");
            assertThat(property.descriptionZh()).contains("玩家属性", "附魔");
        });
    }

    @Test
    void takesImmutableCopiesAndEnforcesWorkspaceCapacity() {
        var mutable = new HashMap<String, String>();
        mutable.put("max_stack_size", "16");
        ContentDefinition original = item("xfesmcontent:test", "generic", mutable, List.of());
        mutable.put("max_stack_size", "32");
        assertThat(original.properties()).containsEntry("max_stack_size", "16");
        assertThatThrownBy(() -> original.properties().put("max_stack_size", "1")).isInstanceOf(UnsupportedOperationException.class);
        assertThatThrownBy(() -> new ContentWorkspace(1, 0, true, List.of(original, original), Instant.EPOCH, "owner"))
                .hasMessageContaining("duplicate content id");
        List<ContentDefinition> definitions = new ArrayList<>();
        for (int i = 0; i < 257; i++) definitions.add(item("xfesmcontent:item_" + i, "generic", Map.of(), List.of()));
        assertThatThrownBy(() -> new ContentWorkspace(1, 0, true, definitions, Instant.EPOCH, "owner")).hasMessageContaining("limit exceeded");
        var duplicate = behavior("right_click", "play_sound", Map.of("sound", "minecraft:test"), false);
        assertThatThrownBy(() -> item("xfesmcontent:test", "generic", Map.of(), List.of(duplicate, duplicate))).hasMessageContaining("duplicate behavior");
    }

    private static ContentDefinition item(String id, String archetype, Map<String, String> properties, List<ContentDefinition.Behavior> behaviors) {
        return new ContentDefinition(id, ITEM, "Test", "", archetype, properties, behaviors);
    }

    private static ContentDefinition.Behavior behavior(String event, String action, Map<String, String> parameters, boolean cancel) {
        return new ContentDefinition.Behavior("behavior", event, action, parameters, 0, cancel);
    }
}