## NEXTER Container Installation Tests ## ## Tests for the NEXTER container installation workflow. ## Verifies atomic installation, rollback, and verification. import std/[unittest, os, tempfiles, options, strutils, times, tables] import nip/nexter_installer import nip/nexter import nip/nexter_manifest import nip/manifest_parser # Helper to create a test container proc createTestContainer(name: string, version: string): NEXTERContainer = let buildDate = parse("2025-11-28T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'") return NEXTERContainer( manifest: NEXTERManifest( name: name, version: parseSemanticVersion(version), buildDate: buildDate, metadata: ContainerInfo( description: "Test container", license: "MIT" ), provenance: ProvenanceInfo( source: "https://example.com/source.tar.gz", sourceHash: "xxh3-source-hash", buildTimestamp: buildDate ), buildConfig: BuildConfiguration( configureFlags: @[], compilerFlags: @[], compilerVersion: "gcc-13", targetArchitecture: "x86_64", libc: "musl", allocator: "jemalloc", buildSystem: "custom" ), base: BaseConfig( baseImage: some("alpine"), baseVersion: some("3.18") ), environment: initTable[string, string](), casChunks: @[], namespace: ContainerNamespace( isolationType: "full", capabilities: @[], mounts: @[], devices: @[] ), startup: StartupConfig( command: @["/bin/sh"], workingDir: "/", user: none(string), entrypoint: none(string) ), buildHash: "xxh3-build-hash", signature: SignatureInfo( algorithm: "ed25519", keyId: "test-key", signature: "test-sig" ) ), environment: "PATH=/usr/bin:/bin", chunks: @[ ChunkData( hash: "xxh3-chunk1", data: "chunk1 data", size: 11, chunkType: Binary ) ], signature: "ed25519-signature", archivePath: "" ) suite "NEXTER Container Installation Tests": setup: let tempDir = createTempDir("nexter_install_test_", "") let storageRoot = tempDir / "storage" createDir(storageRoot) teardown: removeDir(tempDir) test "Install NEXTER container successfully": ## Verify container can be installed atomically let container = createTestContainer("test-container", "1.0.0") let archivePath = storageRoot / "test-container.nexter" # Create archive createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) # Install container let result = installNEXTER(archivePath, storageRoot) check result.success check result.containerName == "test-container" check result.version == "1.0.0" check result.chunksInstalled >= 0 check result.error == "" test "Installation creates correct directory structure": ## Verify installation creates all required directories and files let container = createTestContainer("struct-test", "2.0.0") let archivePath = storageRoot / "struct-test.nexter" createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) let result = installNEXTER(archivePath, storageRoot) check result.success check dirExists(storageRoot / "nexters" / "struct-test") check fileExists(storageRoot / "nexters" / "struct-test" / "manifest.kdl") check fileExists(storageRoot / "nexters" / "struct-test" / "environment.kdl") check fileExists(storageRoot / "nexters" / "struct-test" / "signature.sig") test "Installation fails for already installed container": ## Verify error when container already installed let container = createTestContainer("dup-test", "1.0.0") let archivePath = storageRoot / "dup-test.nexter" createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) # First installation should succeed let result1 = installNEXTER(archivePath, storageRoot) check result1.success # Second installation should fail let result2 = installNEXTER(archivePath, storageRoot) check not result2.success check "already installed" in result2.error.toLowerAscii() test "Check if container is installed": ## Verify isContainerInstalled() works correctly let container = createTestContainer("check-test", "1.0.0") let archivePath = storageRoot / "check-test.nexter" # Before installation check not isContainerInstalled("check-test", storageRoot) # Install createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) discard installNEXTER(archivePath, storageRoot) # After installation check isContainerInstalled("check-test", storageRoot) test "Get installed container version": ## Verify getInstalledContainerVersion() returns correct version let container = createTestContainer("version-test", "3.2.1") let archivePath = storageRoot / "version-test.nexter" createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) discard installNEXTER(archivePath, storageRoot) let version = getInstalledContainerVersion("version-test", storageRoot) check version.isSome check version.get() == "3.2.1" test "List installed containers": ## Verify listInstalledContainers() returns all installed containers # Install multiple containers for i in 1..3: let container = createTestContainer("list-test-" & $i, "1.0.0") let archivePath = storageRoot / ("list-test-" & $i & ".nexter") createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) discard installNEXTER(archivePath, storageRoot) let containers = listInstalledContainers(storageRoot) check containers.len == 3 check "list-test-1" in containers check "list-test-2" in containers check "list-test-3" in containers test "Verify valid container installation": ## Verify verifyContainerInstallation() detects valid installations let container = createTestContainer("verify-test", "1.0.0") let archivePath = storageRoot / "verify-test.nexter" createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) discard installNEXTER(archivePath, storageRoot) check verifyContainerInstallation("verify-test", storageRoot) test "Verify fails for non-existent container": ## Verify verifyContainerInstallation() fails for missing containers check not verifyContainerInstallation("nonexistent", storageRoot) test "Format installation result - success": ## Verify formatting of successful installation result let result = ContainerInstallResult( success: true, containerName: "test", version: "1.0.0", installPath: "/path/to/test", chunksInstalled: 5, error: "" ) let formatted = $result check "✅" in formatted check "test" in formatted check "1.0.0" in formatted test "Format installation result - failure": ## Verify formatting of failed installation result let result = ContainerInstallResult( success: false, containerName: "", version: "", installPath: "", chunksInstalled: 0, error: "Test error" ) let formatted = $result check "❌" in formatted check "Test error" in formatted suite "NEXTER Container Installation Property Tests": test "Property: Successful installation has ALL artifacts": ## Verify successful installation creates all required artifacts let tempDir = createTempDir("nexter_prop_", "") defer: removeDir(tempDir) let container = createTestContainer("prop-test-1", "1.0.0") let archivePath = tempDir / "prop-test-1.nexter" createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) let result = installNEXTER(archivePath, tempDir / "storage") if result.success: check dirExists(tempDir / "storage" / "nexters" / "prop-test-1") check fileExists(tempDir / "storage" / "nexters" / "prop-test-1" / "manifest.kdl") check fileExists(tempDir / "storage" / "nexters" / "prop-test-1" / "environment.kdl") check fileExists(tempDir / "storage" / "nexters" / "prop-test-1" / "signature.sig") test "Property: Failed installation has NO artifacts (complete rollback)": ## Verify failed installation leaves no partial state let tempDir = createTempDir("nexter_prop_", "") defer: removeDir(tempDir) # Try to install to non-existent parent directory let result = installNEXTER("/nonexistent/path/container.nexter", tempDir / "storage") check not result.success # Verify no partial state was created check not dirExists(tempDir / "storage" / "nexters") test "Property: Multiple containers share CAS chunks": ## Verify CAS deduplication works across multiple containers let tempDir = createTempDir("nexter_prop_", "") defer: removeDir(tempDir) let storageRoot = tempDir / "storage" createDir(storageRoot) # Create two containers with same chunk let sharedChunk = ChunkData( hash: "xxh3-shared", data: "shared chunk data", size: 16, chunkType: Binary ) var container1 = createTestContainer("shared-1", "1.0.0") container1.chunks = @[sharedChunk] var container2 = createTestContainer("shared-2", "1.0.0") container2.chunks = @[sharedChunk] let archive1 = storageRoot / "shared-1.nexter" let archive2 = storageRoot / "shared-2.nexter" createNEXTER(container1.manifest, container1.environment, container1.chunks, container1.signature, archive1) createNEXTER(container2.manifest, container2.environment, container2.chunks, container2.signature, archive2) let result1 = installNEXTER(archive1, storageRoot) let result2 = installNEXTER(archive2, storageRoot) check result1.success check result2.success # Verify both containers reference the same chunk let refsPath1 = storageRoot / "cas" / "refs" / "nexters" / "shared-1.refs" let refsPath2 = storageRoot / "cas" / "refs" / "nexters" / "shared-2.refs" if fileExists(refsPath1) and fileExists(refsPath2): let refs1 = readFile(refsPath1).split('\n') let refs2 = readFile(refsPath2).split('\n') check refs1[0] == refs2[0] # Same chunk hash test "Property: Installation preserves manifest integrity": ## Verify manifest is preserved exactly through installation let tempDir = createTempDir("nexter_prop_", "") defer: removeDir(tempDir) let container = createTestContainer("integrity-test", "2.5.3") let archivePath = tempDir / "integrity-test.nexter" createNEXTER(container.manifest, container.environment, container.chunks, container.signature, archivePath) let result = installNEXTER(archivePath, tempDir / "storage") if result.success: let manifestPath = tempDir / "storage" / "nexters" / "integrity-test" / "manifest.kdl" check fileExists(manifestPath) let manifestContent = readFile(manifestPath) check "integrity-test" in manifestContent check "2.5.3" in manifestContent