package p.packer.services; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import p.packer.messages.AssetReference; import p.packer.messages.GetAssetDetailsRequest; import p.packer.messages.PackerOperationStatus; import p.packer.messages.PackerProjectContext; import p.packer.messages.assets.OutputCodecCatalog; import p.packer.messages.assets.PackerAssetState; import p.packer.messages.assets.PackerBuildParticipation; import p.packer.testing.PackerFixtureLocator; import java.nio.file.Files; import java.nio.file.Path; import java.util.Comparator; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; final class PackerAssetDetailsServiceTest { @TempDir Path tempDir; @Test void returnsRegisteredDetailsForRegisteredAssetReferenceById() throws Exception { final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed")); final PackerAssetDetailsService service = service(); final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1))); assertEquals(PackerOperationStatus.SUCCESS, result.status()); assertEquals(PackerAssetState.REGISTERED, result.details().summary().state()); assertEquals(PackerBuildParticipation.INCLUDED, result.details().summary().buildParticipation()); assertEquals("fixture-uuid-1", result.details().summary().identity().assetUuid()); assertEquals("ui_atlas", result.details().summary().identity().assetName()); assertEquals("TILES/indexed_v1", result.details().outputFormat().displayName()); assertEquals(List.of(OutputCodecCatalog.NONE), result.details().availableOutputCodecs()); assertEquals(List.of(), result.details().codecConfigurationFieldsByCodec().get(OutputCodecCatalog.NONE)); assertTrue(result.diagnostics().isEmpty()); } @Test void returnsUnregisteredDetailsForValidUnregisteredRootReference() throws Exception { final Path projectRoot = copyFixture("workspaces/orphan-valid", tempDir.resolve("orphan")); final PackerAssetDetailsService service = service(); final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forRelativeAssetRoot("orphans/ui_sounds"))); assertEquals(PackerOperationStatus.SUCCESS, result.status()); assertEquals(PackerAssetState.UNREGISTERED, result.details().summary().state()); assertEquals(PackerBuildParticipation.EXCLUDED, result.details().summary().buildParticipation()); assertEquals("orphan-uuid-1", result.details().summary().identity().assetUuid()); assertEquals("ui_sounds", result.details().summary().identity().assetName()); assertEquals(List.of(OutputCodecCatalog.NONE), result.details().availableOutputCodecs()); assertEquals(List.of(), result.details().codecConfigurationFieldsByCodec().get(OutputCodecCatalog.NONE)); } @Test void exposesMetadataFieldsFromOutputMetadataContract() throws Exception { final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed-with-metadata")); final Path manifestPath = projectRoot.resolve("assets/ui/atlas/asset.json"); final ObjectMapper mapper = new ObjectMapper(); final var manifest = mapper.readTree(manifestPath.toFile()); final ObjectNode output = (ObjectNode) manifest.path("output"); final ObjectNode metadata = output.putObject("metadata"); metadata.put("tile_size", "16"); metadata.put("channels", "1"); mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest); final PackerAssetDetailsService service = service(); final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1))); assertEquals(PackerOperationStatus.SUCCESS, result.status()); assertEquals( Map.of("tile_size", "16", "channels", "1"), result.details().metadataFields().stream().collect(java.util.stream.Collectors.toMap( field -> field.key(), field -> field.value()))); } @Test void returnsInvalidDetailsForInvalidDeclaration() throws Exception { final Path projectRoot = copyFixture("workspaces/invalid-missing-fields", tempDir.resolve("invalid")); final PackerAssetDetailsService service = service(); final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forRelativeAssetRoot("bad"))); assertEquals(PackerOperationStatus.FAILED, result.status()); assertEquals(PackerAssetState.UNREGISTERED, result.details().summary().state()); assertEquals(PackerBuildParticipation.EXCLUDED, result.details().summary().buildParticipation()); assertFalse(result.diagnostics().isEmpty()); } @Test void returnsFailureWhenReferenceCannotBeResolved() { final PackerAssetDetailsService service = service(); final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(tempDir.resolve("empty")), AssetReference.forRelativeAssetRoot("missing/root"))); assertEquals(PackerOperationStatus.FAILED, result.status()); assertEquals(PackerAssetState.UNREGISTERED, result.details().summary().state()); assertEquals(PackerBuildParticipation.EXCLUDED, result.details().summary().buildParticipation()); assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("could not be resolved"))); } private PackerProjectContext project(Path root) { return new PackerProjectContext("main", root); } private PackerAssetDetailsService service() { final var foundation = new p.packer.services.PackerWorkspaceFoundation(); final var parser = new PackerAssetDeclarationParser(new ObjectMapper()); final var runtimeRegistry = new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser)); final var resolver = new PackerAssetReferenceResolver(foundation.lookup()); return new PackerAssetDetailsService(runtimeRegistry, resolver); } private Path copyFixture(String relativePath, Path targetRoot) throws Exception { final Path sourceRoot = PackerFixtureLocator.fixtureRoot(relativePath); try (var stream = Files.walk(sourceRoot)) { for (Path source : stream.sorted(Comparator.naturalOrder()).toList()) { final Path target = targetRoot.resolve(sourceRoot.relativize(source).toString()); if (Files.isDirectory(source)) { Files.createDirectories(target); } else { Files.createDirectories(target.getParent()); Files.copy(source, target); } } } return targetRoot; } }