package ar.com.companeros.runtime; import ar.com.companeros.Companeros; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.UUID; import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.world.level.storage.LevelResource; import net.minecraftforge.gametest.GameTestHolder; import net.minecraftforge.gametest.PrefixGameTestTemplate; /** Archivos temporales dentro del mundo aislado de GameTest; sin acceder al entorno o a claves. */ @GameTestHolder(Companeros.ID) @PrefixGameTestTemplate(false) public final class AgentConfigTests { @GameTest(template = "empty") public void defaultConfigAndPopulationPersistWithoutCredentials(GameTestHelper helper) throws IOException { Path directory = testDirectory(helper); try { AgentConfig config = AgentConfig.load(directory); helper.assertTrue(config.populationLimit() == 2 && config.autoConnect() && !config.decisionConfig().enabled() && !config.decisionConfig().foundationVerified() && !config.decisionConfig().solEnabled() && config.decisionConfig().maxSessionUsd().signum() == 0, "Defaults permiten dos sesiones y mantienen proveedores desactivados y sin presupuesto"); helper.assertTrue(!config.webEnabled() && Files.exists(directory.resolve("raps/research.json")), "Investigación web se crea desactivada"); AgentInstructions.load(directory.resolve("raps")); Files.writeString(directory.resolve("raps/agents.md"), "Priorizar comida y colaborar con cautela", StandardCharsets.UTF_8); helper.assertTrue(AgentInstructions.load(directory.resolve("raps")).equals("Priorizar comida y colaborar con cautela"), "La configuración editable conserva instrucciones del operador"); for (int population : new int[]{4, 6, 10, 2}) { config.setPopulationLimit(population); helper.assertTrue(AgentConfig.load(directory).populationLimit() == population, "Cada escalón permitido se guarda y recarga"); } String contents = Files.readString(directory.resolve("raps/agents.json"), StandardCharsets.UTF_8); helper.assertTrue(!contents.contains("OPENAI_API_KEY") && !Files.exists(directory.resolve("raps/agents.json.tmp")), "La configuración no guarda claves ni deja el archivo temporal tras el commit"); boolean rejected = false; try { config.setPopulationLimit(3); } catch (IllegalArgumentException expected) { rejected = true; } helper.assertTrue(rejected && config.populationLimit() == 2 && AgentConfig.load(directory).populationLimit() == 2, "Un tamaño no permitido no modifica memoria ni archivo"); } finally { clean(directory); } helper.succeed(); } @GameTest(template = "empty") public void malformedConfigDoesNotResetOrEnableModels(GameTestHelper helper) throws IOException { Path directory = testDirectory(helper); try { AgentConfig.load(directory); Path file = directory.resolve("raps/agents.json"); String invalid = "{\"schemaVersion\":1,\"populationLimit\":3,\"modelsEnabled\":true}"; Files.writeString(file, invalid, StandardCharsets.UTF_8); boolean rejected = false; try { AgentConfig.load(directory); } catch (IllegalStateException expected) { rejected = true; } helper.assertTrue(rejected && Files.readString(file, StandardCharsets.UTF_8).equals(invalid), "Configuración inválida falla sin borrar el archivo ni aplicar flags parciales"); } finally { clean(directory); } helper.succeed(); } private static Path testDirectory(GameTestHelper helper) { return helper.getLevel().getServer().getWorldPath(LevelResource.ROOT) .resolve("config-test-" + UUID.randomUUID()); } private static void clean(Path directory) throws IOException { // Solo destinos fijos creados por esta prueba; nunca un borrado recursivo. Files.deleteIfExists(directory.resolve("raps/agents.json.tmp")); Files.deleteIfExists(directory.resolve("raps/agents.json")); Files.deleteIfExists(directory.resolve("raps/research.json")); Files.deleteIfExists(directory.resolve("raps/agents.md")); Files.deleteIfExists(directory.resolve("raps")); Files.deleteIfExists(directory); } }