## NEXTER Container Namespace Tests
##
## Tests for container namespace isolation and configuration.
## Verifies namespace creation, mount setup, and environment configuration.

import std/[unittest, os, tempfiles, options, strutils, times, tables]
import nip/container
import nip/nexter_manifest
import nip/manifest_parser

# Helper to create a test manifest
proc createTestManifest(name: string, version: string): NEXTERManifest =
  let buildDate = parse("2025-11-28T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
  return 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"
    )
  )

suite "Container Namespace Configuration Tests":

  test "Create container config from manifest":
    ## Verify container config can be created from manifest
    
    let manifest = createTestManifest("config-test", "1.0.0")
    let casRoot = "/tmp/cas"
    
    let config = createContainerConfig(manifest, casRoot)
    
    check config.isolationType == "full"
    check config.mounts.len >= 3  # CAS mount + tmpfs mounts
    check config.environment.len >= 0

  test "Container config includes CAS mount":
    ## Verify CAS mount is configured correctly
    
    let manifest = createTestManifest("cas-mount-test", "1.0.0")
    let casRoot = "/var/lib/nexus/cas"
    
    let config = createContainerConfig(manifest, casRoot)
    
    # Find CAS mount
    var casMountFound = false
    for mount in config.mounts:
      if mount.target == "/cas":
        casMountFound = true
        check mount.source == casRoot / "chunks"
        check mount.mountType == "bind"
        check mount.readOnly == true
        break
    
    check casMountFound

  test "Container config includes tmpfs mounts":
    ## Verify tmpfs mounts are configured
    
    let manifest = createTestManifest("tmpfs-test", "1.0.0")
    let casRoot = "/tmp/cas"
    
    let config = createContainerConfig(manifest, casRoot)
    
    # Find tmpfs mounts
    var tmpMountFound = false
    var runMountFound = false
    
    for mount in config.mounts:
      if mount.target == "/tmp" and mount.mountType == "tmpfs":
        tmpMountFound = true
      if mount.target == "/run" and mount.mountType == "tmpfs":
        runMountFound = true
    
    check tmpMountFound
    check runMountFound

  test "Container config with custom environment":
    ## Verify custom environment variables are preserved
    
    let manifest = createTestManifest("env-test", "1.0.0")
    var envManifest = manifest
    envManifest.environment["PATH"] = "/usr/bin:/bin"
    envManifest.environment["HOME"] = "/root"
    
    let config = createContainerConfig(envManifest, "/tmp/cas")
    
    check config.environment["PATH"] == "/usr/bin:/bin"
    check config.environment["HOME"] == "/root"

  test "Container config with capabilities":
    ## Verify capabilities are preserved
    
    let manifest = createTestManifest("cap-test", "1.0.0")
    var capManifest = manifest
    capManifest.namespace.capabilities = @["net_bind_service", "sys_admin"]
    
    let config = createContainerConfig(capManifest, "/tmp/cas")
    
    check config.capabilities.len == 2
    check "net_bind_service" in config.capabilities
    check "sys_admin" in config.capabilities

  test "Container config with different isolation types":
    ## Verify different isolation types are supported
    
    for isolationType in ["full", "network", "pid", "ipc", "uts"]:
      let manifest = createTestManifest("iso-" & isolationType, "1.0.0")
      var isoManifest = manifest
      isoManifest.namespace.isolationType = isolationType
      
      let config = createContainerConfig(isoManifest, "/tmp/cas")
      
      check config.isolationType == isolationType

suite "Container Runtime Tests":

  test "Create container runtime":
    ## Verify container runtime can be created
    
    let manifest = createTestManifest("runtime-test", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    
    let runtime = createContainerRuntime("test-container", manifest, config)
    
    check runtime.name == "test-container"
    check runtime.manifest.name == "runtime-test"
    check runtime.status == Created
    check runtime.pid == 0

  test "Container runtime has unique ID":
    ## Verify each runtime gets unique ID
    
    let manifest = createTestManifest("id-test", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    
    let runtime1 = createContainerRuntime("container1", manifest, config)
    let runtime2 = createContainerRuntime("container2", manifest, config)
    
    check runtime1.id != runtime2.id

  test "Get container status - created":
    ## Verify status for created container
    
    let manifest = createTestManifest("status-test", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    
    let runtime = createContainerRuntime("test", manifest, config)
    
    check runtime.status == Created

  test "Format container config":
    ## Verify container config formatting
    
    let manifest = createTestManifest("format-test", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    
    let formatted = $config
    
    check "Container Config" in formatted
    check "Isolation" in formatted
    check "Mounts" in formatted

  test "Format container runtime":
    ## Verify container runtime formatting
    
    let manifest = createTestManifest("format-runtime", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    let runtime = createContainerRuntime("test", manifest, config)
    
    let formatted = $runtime
    
    check "Container" in formatted
    check "test" in formatted
    check "Created" in formatted

suite "Container Namespace Property Tests":

  test "Property: Config preserves manifest isolation type":
    ## Verify isolation type is preserved through config creation
    
    for isolationType in ["full", "network", "pid"]:
      let manifest = createTestManifest("prop-iso", "1.0.0")
      var isoManifest = manifest
      isoManifest.namespace.isolationType = isolationType
      
      let config = createContainerConfig(isoManifest, "/tmp/cas")
      
      check config.isolationType == isolationType

  test "Property: Config preserves all environment variables":
    ## Verify all environment variables are preserved
    
    let manifest = createTestManifest("prop-env", "1.0.0")
    var envManifest = manifest
    
    # Add multiple environment variables
    for i in 1..10:
      envManifest.environment["VAR_" & $i] = "value_" & $i
    
    let config = createContainerConfig(envManifest, "/tmp/cas")
    
    check config.environment.len == 10
    for i in 1..10:
      check config.environment["VAR_" & $i] == "value_" & $i

  test "Property: Config always includes CAS mount":
    ## Verify CAS mount is always present
    
    for i in 1..5:
      let manifest = createTestManifest("prop-cas-" & $i, "1.0.0")
      let config = createContainerConfig(manifest, "/tmp/cas-" & $i)
      
      var casMountFound = false
      for mount in config.mounts:
        if mount.target == "/cas":
          casMountFound = true
          break
      
      check casMountFound

  test "Property: Runtime ID format is consistent":
    ## Verify runtime IDs follow consistent format
    
    let manifest = createTestManifest("prop-id", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    
    for i in 1..10:
      let runtime = createContainerRuntime("container-" & $i, manifest, config)
      
      check runtime.id.startsWith("container-")
      check runtime.id.len > 10  # Should have timestamp

  test "Property: Config mounts are read-only for CAS":
    ## Verify CAS mounts are always read-only
    
    let manifest = createTestManifest("prop-ro", "1.0.0")
    let config = createContainerConfig(manifest, "/tmp/cas")
    
    for mount in config.mounts:
      if mount.target == "/cas":
        check mount.readOnly == true
        break
