package cc.unitmesh.agent.artifact import cc.unitmesh.agent.ArtifactAgent import kotlinx.coroutines.runBlocking import java.io.File import java.util.zip.ZipFile import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue class ArtifactBundlePackerTest { private fun createTempDir(name: String): File { val dir = File(System.getProperty("java.io.tmpdir"), "artifact-test-$name-${System.currentTimeMillis()}") dir.mkdirs() return dir } @Test fun packShouldCreateValidZipFile() = runBlocking { val tempDir = createTempDir("pack") try { // Create a test artifact val artifact = ArtifactAgent.Artifact( identifier = "test-artifact", type = ArtifactAgent.Artifact.ArtifactType.HTML, title = "Test HTML Page", content = """ Test

Hello World

""".trimIndent() ) // Create bundle val bundle = ArtifactBundle.fromArtifact(artifact) // Pack to file val outputFile = File(tempDir, "test.unit") val packer = ArtifactBundlePacker() val result = packer.pack(bundle, outputFile.absolutePath) // Verify result assertTrue(result is PackResult.Success, "Pack should succeed: $result") assertTrue(outputFile.exists(), "Output file should exist") assertTrue(outputFile.length() > 0, "Output file should not be empty") // Verify it's a valid ZIP file ZipFile(outputFile).use { zip -> val entries = zip.entries().toList() println("ZIP entries: ${entries.map { it.name }}") // Should contain expected files assertTrue(entries.any { it.name == "ARTIFACT.md" }, "Should contain ARTIFACT.md") assertTrue(entries.any { it.name == "package.json" }, "Should contain package.json") assertTrue(entries.any { it.name == "index.html" }, "Should contain index.html") assertTrue(entries.any { it.name == ".artifact/context.json" }, "Should contain context.json") // Verify content val htmlEntry = entries.find { it.name == "index.html" }!! val htmlContent = zip.getInputStream(htmlEntry).bufferedReader().readText() assertTrue(htmlContent.contains("Hello World"), "HTML should contain content") } } finally { tempDir.deleteRecursively() } } @Test fun unpackShouldRestoreBundleFromZip() = runBlocking { val tempDir = createTempDir("unpack") try { // Create and pack a bundle val originalArtifact = ArtifactAgent.Artifact( identifier = "roundtrip-test", type = ArtifactAgent.Artifact.ArtifactType.HTML, title = "Roundtrip Test", content = "Roundtrip Content" ) val originalBundle = ArtifactBundle.fromArtifact(originalArtifact) val outputFile = File(tempDir, "roundtrip.unit") val packer = ArtifactBundlePacker() // Pack val packResult = packer.pack(originalBundle, outputFile.absolutePath) assertTrue(packResult is PackResult.Success, "Pack should succeed: $packResult") // Unpack val unpackResult = packer.unpack(outputFile.absolutePath) assertTrue(unpackResult is UnpackResult.Success, "Unpack should succeed: $unpackResult") val restoredBundle = (unpackResult as UnpackResult.Success).bundle // Verify restored data assertEquals(originalBundle.name, restoredBundle.name) assertEquals(originalBundle.type, restoredBundle.type) assertTrue(restoredBundle.mainContent.contains("Roundtrip Content")) } finally { tempDir.deleteRecursively() } } @Test fun packedFileCanBeUnzippedWithStandardTools() = runBlocking { val tempDir = createTempDir("unzip") // Don't delete this one to allow manual inspection val artifact = ArtifactAgent.Artifact( identifier = "unzip-test", type = ArtifactAgent.Artifact.ArtifactType.HTML, title = "Unzip Test", content = "Test" ) val bundle = ArtifactBundle.fromArtifact(artifact) val outputFile = File(tempDir, "unzip-test.unit") val packer = ArtifactBundlePacker() packer.pack(bundle, outputFile.absolutePath) // Verify file starts with ZIP magic bytes (PK) val magicBytes = outputFile.inputStream().use { it.readNBytes(2) } assertEquals(0x50, magicBytes[0].toInt() and 0xFF, "First byte should be 'P'") assertEquals(0x4B, magicBytes[1].toInt() and 0xFF, "Second byte should be 'K'") println("File path: ${outputFile.absolutePath}") println("File size: ${outputFile.length()} bytes") println("Magic bytes: ${magicBytes.joinToString(" ") { String.format("%02X", it) }}") // Verify it can be opened as a ZipFile (this is the real test) ZipFile(outputFile).use { zip -> val entries = zip.entries().toList() println("ZIP contains ${entries.size} entries: ${entries.map { it.name }}") assertTrue(entries.isNotEmpty(), "ZIP should contain entries") } // Also test with unzip command if available try { val process = ProcessBuilder("unzip", "-l", outputFile.absolutePath) .redirectErrorStream(true) .start() val output = process.inputStream.bufferedReader().readText() val exitCode = process.waitFor() println("unzip -l output:\n$output") assertEquals(0, exitCode, "unzip command should succeed") } catch (e: Exception) { println("unzip command not available: ${e.message}") } // Keep the file for manual inspection println("\n📦 Test file kept at: ${outputFile.absolutePath}") println(" To inspect: unzip -l ${outputFile.absolutePath}") println(" To extract: unzip ${outputFile.absolutePath} -d /tmp/extracted") } }