package microsandbox

import (
	"context"
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestLifecycleConvergenceOptions(t *testing.T) {
	connect := connectOrStartOptions{}
	WithConnectOrStartDetached()(&connect)
	if !connect.detached {
		t.Fatal("WithConnectOrStartDetached did not enable detached mode")
	}

	restart := restartOptions{}
	WithRestartForce()(&restart)
	WithRestartTimeout(3 * time.Second)(&restart)
	WithRestartDetached()(&restart)
	if !restart.force || restart.timeout != 3*time.Second || !restart.detached {
		t.Fatalf("restart options = %#v", restart)
	}

	destroy := destroyOptions{}
	WithDestroyForce()(&destroy)
	WithDestroyTimeout(4 * time.Second)(&destroy)
	if !destroy.force || destroy.timeout != 4*time.Second {
		t.Fatalf("destroy options = %#v", destroy)
	}
}

func marshalCreateOptions(t *testing.T, opts ...SandboxOption) map[string]any {
	t.Helper()
	cfg := SandboxConfig{}
	for _, o := range opts {
		o(&cfg)
	}
	raw, err := json.Marshal(buildFFICreateOptions(cfg))
	if err != nil {
		t.Fatalf("marshal: %v", err)
	}
	var out map[string]any
	if err := json.Unmarshal(raw, &out); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	return out
}

func mustField(t *testing.T, m map[string]any, key string) any {
	t.Helper()
	v, ok := m[key]
	if !ok {
		t.Fatalf("expected JSON field %q in payload; got %v", key, m)
	}
	return v
}

func marshalRestoreOptions[T SnapshotSeed](t *testing.T, snapshot T) map[string]any {
	t.Helper()
	raw, err := json.Marshal(buildFFIRestoreOptions(snapshot, RestoreConfig{}))
	if err != nil {
		t.Fatal(err)
	}
	var payload map[string]any
	if err := json.Unmarshal(raw, &payload); err != nil {
		t.Fatal(err)
	}
	return payload
}

func TestRestorePreservesTypedReference(t *testing.T) {
	snapshot := &SnapshotArtifact{
		reference:     "baseline",
		referenceKind: "path",
	}
	payload := marshalRestoreOptions(t, snapshot)

	if got := mustField(t, payload, "snapshot"); got != "baseline" {
		t.Fatalf("snapshot = %v, want baseline", got)
	}
	if got := mustField(t, payload, "snapshot_reference_kind"); got != "path" {
		t.Fatalf("snapshot_reference_kind = %v, want path", got)
	}
}

func TestRestoreStringRemainsUnresolved(t *testing.T) {
	payload := marshalRestoreOptions(t, "baseline")

	if got := mustField(t, payload, "snapshot"); got != "baseline" {
		t.Fatalf("snapshot = %v, want baseline", got)
	}
	if _, ok := payload["snapshot_reference_kind"]; ok {
		t.Fatalf("snapshot_reference_kind should be omitted for string references: %v", payload)
	}
}

func TestSandboxConfigUnmarshalPersistedRootfsSource(t *testing.T) {
	raw := []byte(`{
		"name": "go-sdk-example-main",
		"image": {
			"Oci": {
				"reference": "mirror.gcr.io/library/alpine",
				"root_disk": {"kind": "managed", "size_mib": 4096}
			}
		},
		"cpus": 1,
		"memory_mib": 512,
		"workdir": "/",
		"labels": {"suite": "sdk"},
		"scripts": {"hello": "echo hi"}
	}`)

	var cfg SandboxConfig
	if err := json.Unmarshal(raw, &cfg); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}

	if cfg.Name != "go-sdk-example-main" {
		t.Fatalf("Name = %q", cfg.Name)
	}
	if cfg.Image != "mirror.gcr.io/library/alpine" {
		t.Fatalf("Image = %q", cfg.Image)
	}
	if cfg.RootDisk == nil || cfg.RootDisk.Kind() != RootDiskKindManaged || cfg.RootDisk.SizeMiB != 4096 {
		t.Fatalf("RootDisk = %#v, want managed 4096", cfg.RootDisk)
	}
	if cfg.OCIUpperSizeMiB != 4096 || !cfg.ociUpperSizeSet {
		t.Fatalf("legacy OCI upper mirror = %d, set = %v", cfg.OCIUpperSizeMiB, cfg.ociUpperSizeSet)
	}
	if cfg.CPUs != 1 || cfg.MemoryMiB != 512 || cfg.Workdir != "/" {
		t.Fatalf("scalar config mismatch: %#v", cfg)
	}
	if cfg.Labels["suite"] != "sdk" || cfg.Scripts["hello"] != "echo hi" {
		t.Fatalf("map config mismatch: labels=%v scripts=%v", cfg.Labels, cfg.Scripts)
	}
}

func TestSandboxConfigUnmarshalPersistedTmpfsRootDisk(t *testing.T) {
	raw := []byte(`{
		"name": "go-sdk-example-main",
		"image": {
			"Oci": {
				"reference": "mirror.gcr.io/library/alpine",
				"root_disk": {"kind": "tmpfs"}
			}
		}
	}`)

	var cfg SandboxConfig
	if err := json.Unmarshal(raw, &cfg); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}

	if cfg.RootDisk == nil || cfg.RootDisk.Kind() != RootDiskKindTmpfs {
		t.Fatalf("RootDisk = %#v, want tmpfs", cfg.RootDisk)
	}
	if cfg.ociUpperSizeSet {
		t.Fatal("legacy upper mirror must stay unset for tmpfs root disks")
	}
}

func TestSandboxConfigUnmarshalPersistedDiskImageRootDisk(t *testing.T) {
	raw := []byte(`{
		"name": "go-sdk-example-main",
		"image": {
			"Oci": {
				"reference": "mirror.gcr.io/library/alpine",
				"root_disk": {"kind": "disk-image", "path": "/imgs/scratch.img", "format": "Raw", "fstype": "ext4"}
			}
		}
	}`)

	var cfg SandboxConfig
	if err := json.Unmarshal(raw, &cfg); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}

	rd := cfg.RootDisk
	if rd == nil || rd.Kind() != RootDiskKindDiskImage {
		t.Fatalf("RootDisk = %#v, want disk-image", rd)
	}
	if rd.Path != "/imgs/scratch.img" || rd.Format != "raw" || rd.Fstype != "ext4" {
		t.Fatalf("disk-image fields = %#v", rd)
	}
}

func TestSandboxConfigUnmarshalLegacyFlatUpperSize(t *testing.T) {
	raw := []byte(`{
		"name": "go-sdk-example-main",
		"image": {
			"Oci": {
				"reference": "mirror.gcr.io/library/alpine",
				"upper_size_mib": 4096
			}
		}
	}`)

	var cfg SandboxConfig
	if err := json.Unmarshal(raw, &cfg); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}

	if cfg.RootDisk == nil || cfg.RootDisk.Kind() != RootDiskKindManaged || cfg.RootDisk.SizeMiB != 4096 {
		t.Fatalf("RootDisk = %#v, want managed 4096 from legacy field", cfg.RootDisk)
	}
	if cfg.OCIUpperSizeMiB != 4096 || !cfg.ociUpperSizeSet {
		t.Fatalf("legacy OCI upper mirror = %d, set = %v", cfg.OCIUpperSizeMiB, cfg.ociUpperSizeSet)
	}
}

func TestSandboxConfigUnmarshalNestedResources(t *testing.T) {
	raw := []byte(`{
		"name": "go-sdk-example-main",
		"image": {
			"Oci": {
				"reference": "mirror.gcr.io/library/alpine",
				"upper_size_mib": 4096
			}
		},
		"resources": {
			"cpus": 2,
			"memory_mib": 1024,
			"max_cpus": 8,
			"max_memory_mib": 4096
		}
	}`)

	var cfg SandboxConfig
	if err := json.Unmarshal(raw, &cfg); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}

	if cfg.CPUs != 2 || cfg.MemoryMiB != 1024 {
		t.Fatalf("effective resources mismatch: %#v", cfg)
	}
	if cfg.MaxCPUs != 8 || cfg.MaxMemoryMiB != 4096 {
		t.Fatalf("max resources mismatch: %#v", cfg)
	}
}

func TestSandboxConfigUnmarshalLegacyNestedResourcesDefaultMax(t *testing.T) {
	raw := []byte(`{
		"name": "go-sdk-example-main",
		"resources": {
			"cpus": 4,
			"memory_mib": 2048
		}
	}`)

	var cfg SandboxConfig
	if err := json.Unmarshal(raw, &cfg); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}

	if cfg.MaxCPUs != 4 || cfg.MaxMemoryMiB != 2048 {
		t.Fatalf("legacy max resources mismatch: %#v", cfg)
	}
}

func TestFFIWireShape_WithImage(t *testing.T) {
	got := marshalCreateOptions(t, WithImage("python:3.12"))
	if v := mustField(t, got, "image"); v != "python:3.12" {
		t.Fatalf("image = %v, want %q", v, "python:3.12")
	}
	if _, present := got["snapshot"]; present {
		t.Fatal("snapshot must not appear in payload when only image is set")
	}
}

func TestFFIWireShape_WithBindRootfs(t *testing.T) {
	got := marshalCreateOptions(t, WithBindRootfs("/srv/rootfs"))
	if v := mustField(t, got, "image_bind"); v != "/srv/rootfs" {
		t.Fatalf("image_bind = %v, want %q", v, "/srv/rootfs")
	}
	if _, present := got["image"]; present {
		t.Fatal("image must not appear in payload when only image_bind is set")
	}
}

func TestFFIWireShape_WithRootDiskManaged(t *testing.T) {
	got := marshalCreateOptions(t, WithImage("python:3.12"), WithRootDisk(RootDisk.Managed(8192)))
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "managed" || rd["size_mib"] != float64(8192) {
		t.Fatalf("root_disk = %v, want managed 8192", rd)
	}
}

func TestFFIWireShape_WithRootDiskTmpfs(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("python:3.12"),
		WithRootDisk(RootDisk.Tmpfs(RootDiskTmpfsOptions{SizeMiB: 512})),
	)
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "tmpfs" || rd["size_mib"] != float64(512) {
		t.Fatalf("root_disk = %v, want tmpfs 512", rd)
	}
}

func TestFFIWireShape_WithRootDiskTmpfsDefaultSizeOmitsSize(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("python:3.12"),
		WithRootDisk(RootDisk.Tmpfs(RootDiskTmpfsOptions{})),
	)
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "tmpfs" {
		t.Fatalf("root_disk = %v, want tmpfs", rd)
	}
	if _, present := rd["size_mib"]; present {
		t.Fatalf("size_mib must be omitted for the runtime default; root_disk = %v", rd)
	}
}

func TestFFIWireShape_WithRootDiskImage(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("python:3.12"),
		WithRootDisk(RootDisk.Disk("./scratch.img", RootDiskImageOptions{Format: "raw", Fstype: "ext4"})),
	)
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "disk-image" || rd["path"] != "./scratch.img" || rd["format"] != "raw" || rd["fstype"] != "ext4" {
		t.Fatalf("root_disk = %v, want disk-image fields", rd)
	}
	if _, present := rd["size_mib"]; present {
		t.Fatalf("size_mib must be omitted for disk-image root disks; root_disk = %v", rd)
	}
}

func TestFFIWireShape_WithRootDiskFlat(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("python:3.12"),
		WithRootDisk(RootDisk.Flat(RootDiskFlatOptions{
			SizeMiB: 8192,
			Fstype:  "ext4",
			Clone:   FlatCloneReflink,
		})),
	)
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "flat" || rd["size_mib"] != float64(8192) || rd["fstype"] != "ext4" || rd["clone"] != "reflink" {
		t.Fatalf("root_disk = %v, want flat ext4 reflink fields", rd)
	}
}

func TestFFIWireShape_WithOCIUpperSize(t *testing.T) {
	got := marshalCreateOptions(t, WithImage("python:3.12"), WithOCIUpperSize(8192))
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "managed" || rd["size_mib"] != float64(8192) {
		t.Fatalf("root_disk = %v, want managed 8192 from deprecated alias", rd)
	}
	if _, present := got["oci_upper_size_mib"]; present {
		t.Fatal("deprecated flat field must not reach the wire")
	}
}

func TestFFIWireShape_WithOCIUpperSizeZero(t *testing.T) {
	got := marshalCreateOptions(t, WithImage("python:3.12"), WithOCIUpperSize(0))
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "managed" || rd["size_mib"] != float64(0) {
		t.Fatalf("root_disk = %v, want explicit managed 0", rd)
	}
}

func TestFFIWireShape_LegacyConfigFieldMapsToRootDisk(t *testing.T) {
	// WithConfig users may still set the deprecated public field directly.
	got := marshalCreateOptions(t, WithImage("python:3.12"), func(o *SandboxConfig) {
		o.OCIUpperSizeMiB = 2048
	})
	rd := mustField(t, got, "root_disk").(map[string]any)
	if rd["kind"] != "managed" || rd["size_mib"] != float64(2048) {
		t.Fatalf("root_disk = %v, want managed 2048 from legacy field", rd)
	}
}

func TestFFIWireShape_Restore(t *testing.T) {
	config := RestoreConfig{}
	WithSnapshotDiskOnly()(&config)
	raw, err := json.Marshal(buildFFIRestoreOptions("after-pip-install", config))
	if err != nil {
		t.Fatal(err)
	}
	var got map[string]any
	if err := json.Unmarshal(raw, &got); err != nil {
		t.Fatal(err)
	}
	if v := mustField(t, got, "snapshot"); v != "after-pip-install" {
		t.Fatalf("snapshot = %v, want %q", v, "after-pip-install")
	}
	if _, present := got["image"]; present {
		t.Fatal("image must not appear in payload when only snapshot is set")
	}
	if v := mustField(t, got, "disk_only"); v != true {
		t.Fatalf("snapshot_disk_only = %v, want true", v)
	}
}

func TestFFIWireShape_ScalarKnobs(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithMemory(512),
		WithCPUs(2),
		WithMaxMemory(4096),
		WithMaxCPUs(8),
		WithCPUPlacement(CPUPlacementSpread),
		WithPlacementProfile("latency"),
		WithWorkdir("/app"),
		WithShell("/bin/bash"),
		WithHostname("sb"),
		WithUser("nobody"),
		WithReplace(),
		WithDetached(),
		WithEphemeral(true),
		WithQuietLogs(),
		WithLogLevel(LogLevelDebug),
		WithPullPolicy(PullPolicyAlways),
		WithMaxDuration(45*time.Second),
		WithIdleTimeout(2*time.Minute),
	)
	checks := []struct {
		key  string
		want any
	}{
		{"image", "alpine"},
		{"cpu_placement", "spread"},
		{"placement_profile", "latency"},
		{"memory_mib", float64(512)},
		{"cpus", float64(2)},
		{"max_memory_mib", float64(4096)},
		{"max_cpus", float64(8)},
		{"workdir", "/app"},
		{"shell", "/bin/bash"},
		{"hostname", "sb"},
		{"user", "nobody"},
		{"replace", true},
		{"detached", true},
		{"ephemeral", true},
		{"quiet_logs", true},
		{"log_level", "debug"},
		{"pull_policy", "always"},
		{"max_duration_secs", float64(45)},
		{"idle_timeout_secs", float64(120)},
	}
	for _, c := range checks {
		if v := mustField(t, got, c.key); v != c.want {
			t.Errorf("%s = %v (%T), want %v", c.key, v, v, c.want)
		}
	}
}

func TestFFIWireShape_ReplaceWithTimeoutMs(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithReplaceWithTimeout(750*time.Millisecond),
	)
	if v := mustField(t, got, "replace_with_timeout_ms"); v != float64(750) {
		t.Fatalf("replace_with_timeout_ms = %v, want 750", v)
	}
	if v := mustField(t, got, "replace"); v != true {
		t.Fatalf("replace = %v, want true", v)
	}

	// Zero must round-trip (means "skip SIGTERM"), not be omitted.
	got = marshalCreateOptions(t,
		WithImage("alpine"),
		WithReplaceWithTimeout(0),
	)
	v, ok := got["replace_with_timeout_ms"]
	if !ok {
		t.Fatal("zero timeout was omitted")
	}
	if v != float64(0) {
		t.Fatalf("replace_with_timeout_ms = %v, want 0", v)
	}
}

func TestFFIWireShape_CommandOverridesPreserveExplicitClear(t *testing.T) {
	got := marshalCreateOptions(t, WithEntrypoint("python", "-u"), WithCmd("worker.py"))
	if entrypoint := mustField(t, got, "entrypoint").([]any); len(entrypoint) != 2 {
		t.Fatalf("entrypoint = %v", entrypoint)
	}
	if cmd := mustField(t, got, "cmd").([]any); len(cmd) != 1 || cmd[0] != "worker.py" {
		t.Fatalf("cmd = %v", cmd)
	}

	cleared := marshalCreateOptions(t, WithEntrypoint(), WithCmd())
	if entrypoint := mustField(t, cleared, "entrypoint").([]any); len(entrypoint) != 0 {
		t.Fatalf("cleared entrypoint = %v", entrypoint)
	}
	if cmd := mustField(t, cleared, "cmd").([]any); len(cmd) != 0 {
		t.Fatalf("cleared cmd = %v", cmd)
	}
}

func TestFFIWireShape_EnvAndScripts(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithEnv(map[string]string{"FOO": "1"}),
		WithEnv(map[string]string{"BAR": "2"}), // repeated -> merge
		WithScripts(map[string]string{"run": "echo hi"}),
	)
	env := mustField(t, got, "env").(map[string]any)
	if env["FOO"] != "1" || env["BAR"] != "2" {
		t.Fatalf("env merge failed: %v", env)
	}
	scripts := mustField(t, got, "scripts").(map[string]any)
	if scripts["run"] != "echo hi" {
		t.Fatalf("scripts = %v", scripts)
	}
}

func TestFFIWireShape_Labels(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithLabels(map[string]string{"user.id": "alice"}),
		WithLabel("tenant", "acme"),
	)
	labels := mustField(t, got, "labels").(map[string]any)
	if labels["user.id"] != "alice" || labels["tenant"] != "acme" {
		t.Fatalf("labels = %v", labels)
	}
}

func TestFFIWireShape_Ports(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithPorts(map[uint16]uint16{8080: 80}),
		WithPortsUDP(map[uint16]uint16{5353: 53}),
		WithPortBindings(PortBinding{Bind: "0.0.0.0", HostPort: 8081, GuestPort: 81}),
	)
	ports := mustField(t, got, "ports").(map[string]any)
	if ports["8080"] != float64(80) {
		t.Fatalf("ports = %v", ports)
	}
	portsUDP := mustField(t, got, "ports_udp").(map[string]any)
	if portsUDP["5353"] != float64(53) {
		t.Fatalf("ports_udp = %v", portsUDP)
	}
	bindings := mustField(t, got, "port_bindings").([]any)
	first := bindings[0].(map[string]any)
	if first["bind"] != "0.0.0.0" || first["host_port"] != float64(8081) || first["guest_port"] != float64(81) {
		t.Fatalf("port_bindings = %v", bindings)
	}
}

func TestFFIWireShape_Vsock(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithVsock(
			VsockRoute{HostSocket: "/run/host-api.sock", Port: 5000},
			VsockRoute{HostSocket: "/run/events.sock", Port: 5001, SocketType: VsockSocketTypeDgram},
		),
	)
	routes := mustField(t, got, "vsock").([]any)
	stream := routes[0].(map[string]any)
	dgram := routes[1].(map[string]any)
	if stream["host_socket"] != "/run/host-api.sock" || stream["port"] != float64(5000) {
		t.Fatalf("stream vsock route = %v", stream)
	}
	if _, present := stream["socket_type"]; present {
		t.Fatalf("default stream type should be omitted: %v", stream)
	}
	if dgram["socket_type"] != "dgram" || dgram["port"] != float64(5001) {
		t.Fatalf("datagram vsock route = %v", dgram)
	}
}

func TestFFIWireShape_RegistryAuth(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("private.example.com/img"),
		WithRegistryAuth(RegistryAuth{Username: "u", Password: "p"}),
	)
	ra := mustField(t, got, "registry_auth").(map[string]any)
	if ra["username"] != "u" || ra["password"] != "p" {
		t.Fatalf("registry_auth = %v", ra)
	}
}

// Anonymous auth travels as a top-level field: libraries that predate it reject
// unknown create options instead of falling back to ambient credentials.
func TestFFIWireShape_RegistryAnonymous(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("ghcr.io/org/public"),
		WithRegistryAuth(RegistryAuth{Username: "old", Password: "old"}),
		WithRegistryAnonymous(),
	)
	if got["registry_anonymous"] != true {
		t.Fatalf("registry_anonymous = %v", got["registry_anonymous"])
	}
	if _, present := got["registry_auth"]; present {
		t.Fatalf("anonymous auth also sent registry_auth: %v", got["registry_auth"])
	}
}

func TestFFIWireShape_RegistryOverrides(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("localhost:5050/img"),
		WithRegistryInsecure(),
		WithRegistryCACerts([]byte("pem-one")),
		WithRegistryCACerts([]byte("pem-two")),
	)
	if got["registry_insecure"] != true {
		t.Fatalf("registry_insecure = %v", got["registry_insecure"])
	}
	certs := mustField(t, got, "registry_ca_certs").([]any)
	if len(certs) != 2 || certs[0] != "pem-one" || certs[1] != "pem-two" {
		t.Fatalf("registry_ca_certs = %v", certs)
	}
}

func TestResolveRegistryCACertPaths(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "ca.pem")
	if err := os.WriteFile(path, []byte("pem-from-file"), 0o600); err != nil {
		t.Fatalf("write ca.pem: %v", err)
	}

	cfg := SandboxConfig{}
	WithRegistryCACerts([]byte("pem-inline"))(&cfg)
	WithRegistryCACertsPath(path)(&cfg)
	if err := resolveRegistryCACertPaths(&cfg); err != nil {
		t.Fatalf("resolveRegistryCACertPaths: %v", err)
	}
	if len(cfg.RegistryCACerts) != 2 ||
		string(cfg.RegistryCACerts[0]) != "pem-inline" ||
		string(cfg.RegistryCACerts[1]) != "pem-from-file" {
		t.Fatalf("RegistryCACerts = %q", cfg.RegistryCACerts)
	}
}

func TestResolveRegistryCACertPathsMissingFile(t *testing.T) {
	missing := filepath.Join(t.TempDir(), "nope.pem")
	cfg := SandboxConfig{}
	WithRegistryCACertsPath(missing)(&cfg)
	err := resolveRegistryCACertPaths(&cfg)
	if err == nil {
		t.Fatal("resolveRegistryCACertPaths: got nil error for a missing file")
	}
	if !strings.Contains(err.Error(), missing) {
		t.Errorf("error %q does not mention the path %q", err, missing)
	}
}

func TestFFIWireShape_Init(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithInit(Init.Cmd("/sbin/init", InitOptions{
			Args: []string{"--foo"},
			Env:  map[string]string{"X": "1"},
		})),
	)
	init := mustField(t, got, "init").(map[string]any)
	if init["cmd"] != "/sbin/init" {
		t.Fatalf("init.cmd = %v", init["cmd"])
	}
	args := init["args"].([]any)
	if len(args) != 1 || args[0] != "--foo" {
		t.Fatalf("init.args = %v", args)
	}
	envArr := init["env"].([]any)
	if len(envArr) != 1 {
		t.Fatalf("init.env = %v", envArr)
	}
	pair := envArr[0].([]any)
	if pair[0] != "X" || pair[1] != "1" {
		t.Fatalf("init.env[0] = %v", pair)
	}
}

func TestFFIWireShape_Patches(t *testing.T) {
	mode := uint32(0o755)
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithPatches(
			Patch.Text("/etc/x", "hello", PatchOptions{Mode: &mode, Replace: true}),
			Patch.Mkdir("/app", PatchOptions{Mode: &mode}),
			Patch.Symlink("/x", "/y", PatchOptions{Replace: true}),
		),
	)
	patches := mustField(t, got, "patches").([]any)
	if len(patches) != 3 {
		t.Fatalf("patches length = %d, want 3", len(patches))
	}
	first := patches[0].(map[string]any)
	if first["kind"] != "text" || first["path"] != "/etc/x" || first["content"] != "hello" {
		t.Fatalf("patches[0] = %v", first)
	}
	if first["replace"] != true {
		t.Fatalf("patches[0].replace = %v, want true", first["replace"])
	}
}

func TestFFIWireShape_Volumes(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithMounts(map[string]MountConfig{
			"/data":    Mount.Named("vol-a", MountOptions{}),
			"/host":    Mount.Bind("/var/lib", MountOptions{Readonly: true, Noexec: true, Nosuid: true, Nodev: true}),
			"/scratch": Mount.Tmpfs(TmpfsOptions{SizeMiB: 128, Noexec: true, Nosuid: true}),
			"/img":     Mount.Disk("/tmp/pool.img", DiskOptions{Format: "raw", Readonly: true}),
		}),
	)
	volumes := mustField(t, got, "volumes").(map[string]any)
	if v := volumes["/data"].(map[string]any); v["named"] != "vol-a" {
		t.Fatalf("/data named = %v", v)
	}
	if v := volumes["/host"].(map[string]any); v["bind"] != "/var/lib" || v["readonly"] != true || v["noexec"] != true || v["nosuid"] != true || v["nodev"] != true {
		t.Fatalf("/host = %v", v)
	}
	if v := volumes["/scratch"].(map[string]any); v["tmpfs"] != true || v["size_mib"] != float64(128) || v["noexec"] != true || v["nosuid"] != true {
		t.Fatalf("/scratch = %v", v)
	}
	if v := volumes["/img"].(map[string]any); v["disk"] != "/tmp/pool.img" || v["format"] != "raw" {
		t.Fatalf("/img = %v", v)
	}
}

func TestFFIWireShape_MountOwner(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithMounts(map[string]MountConfig{
			"/owned":   Mount.Bind("/host/owned", MountOptions{Owner: &MountOwner{UID: 1000, GID: 1000}}),
			"/root":    Mount.Bind("/host/root", MountOptions{Owner: &MountOwner{UID: 0, GID: 0}}),
			"/default": Mount.Bind("/host/default", MountOptions{}),
		}),
	)
	volumes := mustField(t, got, "volumes").(map[string]any)

	// An explicit owner rides the wire as override_uid/override_gid.
	if v := volumes["/owned"].(map[string]any); v["override_uid"] != float64(1000) || v["override_gid"] != float64(1000) {
		t.Fatalf("/owned = %v", v)
	}
	// uid 0 (root) is a real value and must be present, not omitted.
	if v := volumes["/root"].(map[string]any); v["override_uid"] != float64(0) || v["override_gid"] != float64(0) {
		t.Fatalf("/root = %v", v)
	}
	// No owner → the keys are omitted entirely (unset, not 0).
	if v := volumes["/default"].(map[string]any); v["override_uid"] != nil || v["override_gid"] != nil {
		t.Fatalf("/default should omit override_uid/override_gid, got %v", v)
	}
}

func TestFFIWireShape_SecurityProfile(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithSecurityProfile(SecurityProfileRestricted),
	)
	if got["security_profile"] != "restricted" {
		t.Fatalf("security_profile = %v", got["security_profile"])
	}
}

func TestFFIWireShape_DeploymentProfile(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithDeploymentProfile(DeploymentProfileMultiTenant),
	)
	if got["deployment_profile"] != "multi-tenant" {
		t.Fatalf("deployment_profile = %v", got["deployment_profile"])
	}
}

func TestFFIWireShape_Secrets(t *testing.T) {
	for _, test := range []struct {
		name                    string
		preferred, legacy, want []string
	}{
		{"preferred", []string{"api.anthropic.com"}, nil, []string{"api.anthropic.com"}},
		{"legacy", nil, []string{"api.anthropic.com"}, []string{"api.anthropic.com"}},
		{"combined", []string{"api.anthropic.com"}, []string{"*.anthropic.com"}, []string{"api.anthropic.com", "*.anthropic.com"}},
	} {
		t.Run(test.name, func(t *testing.T) {
			got := marshalCreateOptions(t,
				WithImage("alpine"),
				WithSecrets(Secret.Env("OPENAI_API_KEY", "sk-xxx", SecretEnvOptions{
					Allow:               []string{"api.openai.com", "*.openai.com"},
					AllowPlaceholderFor: test.preferred,
					Passthrough:         test.legacy,
					ViolationAction:     ViolationActionBlockAndTerminate,
				})),
			)
			secs := mustField(t, got, "secrets").([]any)
			if len(secs) != 1 {
				t.Fatalf("secrets length = %d", len(secs))
			}
			s := secs[0].(map[string]any)
			if s["env_var"] != "OPENAI_API_KEY" || s["value"] != "sk-xxx" {
				t.Fatalf("secret = %v", s)
			}
			if s["violation_action"] != "block-and-terminate" {
				t.Fatalf("violation_action = %v", s["violation_action"])
			}
			hosts := s["allow"].([]any)
			if len(hosts) != 2 || hosts[0] != "api.openai.com" || hosts[1] != "*.openai.com" {
				t.Fatalf("allow = %v", hosts)
			}
			placeholders := s["passthrough"].([]any)
			if len(placeholders) != len(test.want) {
				t.Fatalf("passthrough = %v, want %v", placeholders, test.want)
			}
			for i, host := range test.want {
				if placeholders[i] != host {
					t.Fatalf("passthrough = %v, want %v", placeholders, test.want)
				}
			}
			if _, ok := s["allow_placeholder_for"]; ok {
				t.Fatal("new API name leaked into wire format")
			}
		})
	}
}

func TestFFIWireShape_NetworkProfile(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithNetwork(NetworkPolicy.FromProfiles(NetworkProfilePublic)),
	)
	net := mustField(t, got, "network").(map[string]any)
	policy := mustField(t, net, "custom_policy").(map[string]any)
	rules := mustField(t, policy, "rules").([]any)
	if len(rules) != 2 {
		t.Fatalf("network.custom_policy.rules len = %d, want 2", len(rules))
	}
}

func TestFFIWireShape_NetworkCustomRules(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithNetwork(&NetworkConfig{
			DefaultEgress:  PolicyActionDeny,
			DefaultIngress: PolicyActionAllow,
			Rules: []PolicyRule{
				{
					Action:      PolicyActionAllow,
					Direction:   PolicyDirectionEgress,
					Destination: "api.openai.com",
					Protocol:    PolicyProtocolTCP,
					Port:        "443",
				},
			},
			DenyDomains: []string{"blocked.example.com"},
			DNS: &DNSConfig{
				Nameservers: []string{"1.1.1.1:53"},
			},
			IPv4Pool:      "172.31.240.0/24",
			IPv6Pool:      "fd7a:115c:a1e0:100::/56",
			NAT64Prefixes: []string{"2001:db8:64::/96"},
		}),
		WithProxy(SOCKS5Proxy("127.0.0.1:1080")),
	)
	net := mustField(t, got, "network").(map[string]any)

	// The custom policy is nested under custom_policy.
	cp := net["custom_policy"].(map[string]any)
	if cp["default_egress"] != "deny" || cp["default_ingress"] != "allow" {
		t.Fatalf("defaults = %v", cp)
	}
	rules := cp["rules"].([]any)
	r0 := rules[0].(map[string]any)
	if r0["action"] != "allow" || r0["direction"] != "egress" {
		t.Fatalf("rule[0] = %v", r0)
	}
	if r0["destination"] != "api.openai.com" || r0["protocol"] != "tcp" || r0["port"] != "443" {
		t.Fatalf("rule[0] details = %v", r0)
	}

	deny := net["deny_domains"].([]any)
	if len(deny) != 1 || deny[0] != "blocked.example.com" {
		t.Fatalf("deny_domains = %v", deny)
	}
	if net["strict"] != true {
		t.Fatalf("strict = %v", net["strict"])
	}
	if net["ipv4_pool"] != "172.31.240.0/24" {
		t.Fatalf("ipv4_pool = %v", net["ipv4_pool"])
	}
	if net["ipv6_pool"] != "fd7a:115c:a1e0:100::/56" {
		t.Fatalf("ipv6_pool = %v", net["ipv6_pool"])
	}
	nat64 := net["nat64_prefixes"].([]any)
	if len(nat64) != 1 || nat64[0] != "2001:db8:64::/96" {
		t.Fatalf("nat64_prefixes = %v", nat64)
	}
	dns := net["dns"].(map[string]any)
	ns := dns["nameservers"].([]any)
	if len(ns) != 1 || ns[0] != "1.1.1.1:53" {
		t.Fatalf("dns.nameservers = %v", ns)
	}
	proxy := mustField(t, got, "proxy").(map[string]any)
	if proxy["protocol"] != "socks5" || proxy["address"] != "127.0.0.1:1080" {
		t.Fatalf("proxy = %#v", proxy)
	}
}

func TestFFIWireShape_SOCKS4Proxy(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithProxy(SOCKS4Proxy("127.0.0.1:1080", SOCKS4ProxyOptions{UserID: "sandbox"})),
	)
	proxy := mustField(t, got, "proxy").(map[string]any)
	if proxy["protocol"] != "socks4" || proxy["address"] != "127.0.0.1:1080" || proxy["user_id"] != "sandbox" {
		t.Fatalf("proxy = %#v", proxy)
	}
}

func TestFFIWireShape_NetworkConnectionLimits(t *testing.T) {
	zero, finite := uint(0), uint(7)
	for _, test := range []struct {
		name    string
		network *NetworkConfig
		want    map[string]any
	}{
		{"omitted", &NetworkConfig{}, map[string]any{}},
		{"canonical", &NetworkConfig{MaxTCPConnections: &zero, MaxUDPConnections: &finite, SecretViolationAction: ViolationActionBlockAndLog}, map[string]any{"max_tcp_connections": float64(0), "max_udp_connections": float64(7)}},
		{"legacy", &NetworkConfig{MaxConnections: &finite, MaxUDPConnections: &zero}, map[string]any{"max_connections": float64(7), "max_udp_connections": float64(0)}},
	} {
		t.Run(test.name, func(t *testing.T) {
			got := marshalCreateOptions(t, WithNetwork(test.network))["network"].(map[string]any)
			if action := test.network.SecretViolationAction; action != "" && got["secret_violation_action"] != string(action) {
				t.Fatalf("connection limits discarded the secret violation action: %#v", got)
			}
			for _, field := range []string{"max_connections", "max_tcp_connections", "max_udp_connections"} {
				actual, present := got[field]
				want, expected := test.want[field]
				if present != expected || actual != want {
					t.Errorf("%s = %#v (present %v), want %#v (present %v)", field, actual, present, want, expected)
				}
			}
		})
	}
}

func TestFFIWireShape_PublishedTCPConfiguration(t *testing.T) {
	omitted := marshalCreateOptions(t, WithNetwork(&NetworkConfig{}))["network"].(map[string]any)
	if value, present := omitted["tcp_accept_queue_size"]; present {
		t.Fatalf("unset accept queue size reached the wire as %#v", value)
	}

	size := uint32(4096)
	unlimited := uint(0)
	got := marshalCreateOptions(t, WithNetwork(&NetworkConfig{
		Ports:                    map[uint16]uint16{8080: 80},
		TCPAcceptQueueSize:       &size,
		MaxInboundTCPConnections: &unlimited,
	}))["network"].(map[string]any)
	if got["max_inbound_tcp_connections"] != float64(0) {
		t.Fatalf("published TCP overrides lost: %#v", got)
	}
	if got["tcp_accept_queue_size"] != float64(4096) {
		t.Fatalf("tcp_accept_queue_size = %#v, want 4096", got["tcp_accept_queue_size"])
	}
}

func TestBuildFFINetworkRateLimiters(t *testing.T) {
	out := buildFFINetwork(&NetworkConfig{
		RateLimiter: &NetworkRateLimiterConfig{
			Egress: &RateLimiterConfig{
				Bandwidth: &TokenBucketConfig{
					Size:         1 << 20,
					RefillTime:   time.Second,
					OneTimeBurst: 512 << 10,
				},
				Ops: &TokenBucketConfig{Size: 1000, RefillTime: 100 * time.Millisecond},
			},
			Ingress: &RateLimiterConfig{
				Bandwidth: &TokenBucketConfig{Size: 2 << 20, RefillTime: 500 * time.Millisecond},
			},
		},
	})

	egress := out.RateLimiter.Egress
	if egress == nil || egress.Bandwidth == nil || egress.Ops == nil {
		t.Fatalf("egress rate limiter = %+v", egress)
	}
	if egress.Bandwidth.Size != 1<<20 || egress.Bandwidth.RefillTimeMs != 1000 {
		t.Fatalf("egress bandwidth = %+v", egress.Bandwidth)
	}
	if egress.Bandwidth.OneTimeBurst != 512<<10 {
		t.Fatalf("egress bandwidth burst = %d", egress.Bandwidth.OneTimeBurst)
	}
	if egress.Ops.Size != 1000 || egress.Ops.RefillTimeMs != 100 || egress.Ops.OneTimeBurst != 0 {
		t.Fatalf("egress ops = %+v", egress.Ops)
	}

	ingress := out.RateLimiter.Ingress
	if ingress == nil || ingress.Bandwidth == nil {
		t.Fatalf("ingress rate limiter = %+v", ingress)
	}
	if ingress.Ops != nil {
		t.Fatalf("ingress ops should stay nil, got %+v", ingress.Ops)
	}
	if ingress.Bandwidth.Size != 2<<20 || ingress.Bandwidth.RefillTimeMs != 500 {
		t.Fatalf("ingress bandwidth = %+v", ingress.Bandwidth)
	}
}

func TestBuildFFINetworkRateLimitersNil(t *testing.T) {
	out := buildFFINetwork(&NetworkConfig{})
	if out.RateLimiter != nil {
		t.Fatalf("nil rate limiters should stay nil: %+v", out)
	}
}

func TestBuildFFITokenBucketKeepsInvalidRefillTimesInvalid(t *testing.T) {
	for _, refillTime := range []time.Duration{
		-time.Second,
		0,
		time.Microsecond,
		1500 * time.Microsecond,
	} {
		out := buildFFITokenBucket(&TokenBucketConfig{Size: 1, RefillTime: refillTime})
		if out.RefillTimeMs != 0 {
			t.Fatalf("refill time %s became %dms", refillTime, out.RefillTimeMs)
		}
	}

	out := buildFFITokenBucket(&TokenBucketConfig{Size: 1, RefillTime: time.Millisecond})
	if out.RefillTimeMs != 1 {
		t.Fatalf("1ms refill time became %dms", out.RefillTimeMs)
	}
}

func TestFFIWireShape_NetworkRateLimiters(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithNetwork(&NetworkConfig{
			RateLimiter: &NetworkRateLimiterConfig{
				Egress: &RateLimiterConfig{
					Bandwidth: &TokenBucketConfig{
						Size:         1 << 20,
						RefillTime:   time.Second,
						OneTimeBurst: 512 << 10,
					},
				},
				Ingress: &RateLimiterConfig{
					Ops: &TokenBucketConfig{Size: 1000, RefillTime: 100 * time.Millisecond},
				},
			},
		}),
	)
	net := mustField(t, got, "network").(map[string]any)
	rateLimiter := net["rate_limiter"].(map[string]any)

	egress := rateLimiter["egress"].(map[string]any)
	bw := egress["bandwidth"].(map[string]any)
	if bw["size"] != float64(1<<20) || bw["refill_time_ms"] != float64(1000) {
		t.Fatalf("egress bandwidth = %v", bw)
	}
	if bw["one_time_burst"] != float64(512<<10) {
		t.Fatalf("egress bandwidth burst = %v", bw["one_time_burst"])
	}
	if _, present := egress["ops"]; present {
		t.Fatalf("nil ops bucket should be omitted: %v", egress)
	}

	ingress := rateLimiter["ingress"].(map[string]any)
	ops := ingress["ops"].(map[string]any)
	if ops["size"] != float64(1000) || ops["refill_time_ms"] != float64(100) {
		t.Fatalf("ingress ops = %v", ops)
	}
	// A zero burst must not reach the wire; the Rust side defaults it.
	if _, present := ops["one_time_burst"]; present {
		t.Fatalf("zero one_time_burst should be omitted: %v", ops)
	}
	if _, present := ingress["bandwidth"]; present {
		t.Fatalf("nil bandwidth bucket should be omitted: %v", ingress)
	}
}

func TestFFIWireShape_SOCKS5Credentials(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithProxy(SOCKS5Proxy("127.0.0.1:1080").Credentials("sandbox", SecretSourceEnv("SOCKS5_PASSWORD"))),
	)
	proxy := mustField(t, got, "proxy").(map[string]any)
	passwordSource, ok := proxy["password_source"].(map[string]any)
	if proxy["username"] != "sandbox" || !ok || passwordSource["kind"] != "env" || passwordSource["var"] != "SOCKS5_PASSWORD" {
		t.Fatalf("proxy credentials = %#v", proxy)
	}
}

func TestFFIWireShape_HTTPConnectCredentials(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("alpine"),
		WithProxy(HTTPConnectProxy("127.0.0.1:3128").Credentials("sandbox", SecretSourceEnv("HTTP_CONNECT_PASSWORD"))),
	)
	proxy := mustField(t, got, "proxy").(map[string]any)
	passwordSource, ok := proxy["password_source"].(map[string]any)
	if proxy["protocol"] != "http_connect" || proxy["username"] != "sandbox" || !ok || passwordSource["kind"] != "env" || passwordSource["var"] != "HTTP_CONNECT_PASSWORD" {
		t.Fatalf("HTTP CONNECT proxy credentials = %#v", proxy)
	}
}

// The Rust side relies on serde(default), so zero-valued Go scalar fields must
// not reach the wire. Explicit optional values use pointers when zero is valid
// on the wire for validation.
func TestFFIWireShape_EmptyConfigOmitsOptionalFields(t *testing.T) {
	got := marshalCreateOptions(t)

	for _, key := range []string{
		"image", "snapshot", "memory_mib", "cpus", "max_memory_mib", "max_cpus", "workdir", "shell",
		"thp", "nested_virt",
		"hostname", "user", "replace", "detached", "env", "scripts",
		"ports", "ports_udp", "vsock", "network", "secrets", "patches", "volumes",
		"proxy", "init", "registry_auth", "registry_anonymous", "registry_insecure", "registry_ca_certs", "root_disk",
	} {
		if _, present := got[key]; present {
			body, _ := json.Marshal(got)
			t.Errorf("empty config emitted key %q; payload = %s", key, body)
		}
	}
}

func TestFFIWireShape_NestedVirt(t *testing.T) {
	for _, enabled := range []bool{false, true} {
		got := marshalCreateOptions(t, WithNestedVirt(enabled))
		if value, ok := got["nested_virt"].(bool); !ok || value != enabled {
			t.Fatalf("nested_virt = %v, want %v", got["nested_virt"], enabled)
		}
	}
}

func TestFFIWireShape_THPPolicy(t *testing.T) {
	got := marshalCreateOptions(t, WithImage("python:3.12"), WithTHP(THPAlways))
	if got["thp"] != "always" {
		t.Fatalf("thp = %v, want always", got["thp"])
	}
}

func TestFFIWireShape_KitchenSinkDoesNotPanic(t *testing.T) {
	got := marshalCreateOptions(t,
		WithImage("python:3.12"),
		WithMemory(1024),
		WithCPUs(4),
		WithEnv(map[string]string{"A": "1"}),
		WithMounts(map[string]MountConfig{
			"/data": Mount.Named("vol", MountOptions{}),
		}),
		WithNetwork(&NetworkConfig{
			DefaultEgress: PolicyActionDeny,
			Rules: []PolicyRule{
				{Action: PolicyActionAllow, Destination: "*"},
			},
			TLS: &TLSConfig{Bypass: []string{"*.googleapis.com"}},
		}),
		WithSecrets(Secret.Env("K", "v", SecretEnvOptions{
			Allow: []string{"h"},
		})),
		WithPatches(Patch.Mkdir("/app", PatchOptions{})),
		WithPorts(map[uint16]uint16{8080: 80}),
		WithReplace(),
		WithDetached(),
		WithMaxDuration(30*time.Second),
	)
	body, _ := json.Marshal(got)
	if !strings.Contains(string(body), "python:3.12") {
		t.Fatalf("kitchen-sink payload missing image: %s", body)
	}
}

func TestNetworkStrictDefaultsAndOptOut(t *testing.T) {
	for _, tc := range []struct {
		name   string
		config NetworkConfig
		want   bool
	}{
		{"default", NetworkConfig{}, true},
		{"explicit default", NetworkConfig{DisableStrict: false}, true},
		{"opt out", NetworkConfig{DisableStrict: true}, false},
	} {
		t.Run(tc.name, func(t *testing.T) {
			got := marshalCreateOptions(t, WithNetwork(&tc.config))
			network := mustField(t, got, "network").(map[string]any)
			if network["strict"] != tc.want {
				t.Fatalf("strict = %v, want %v", network["strict"], tc.want)
			}
		})
	}
}

func TestHTTPNetworkDenyMessageSurvivesFFIConversion(t *testing.T) {
	implicit := buildFFINetwork(&NetworkConfig{})
	if implicit.HTTP != nil {
		t.Fatal("omitted HTTP config must inherit runtime defaults")
	}
	optOut := buildFFINetwork(&NetworkConfig{HTTP: &HTTPConfig{DenyResponse: false}})
	if optOut.HTTP.DenyResponse || optOut.HTTP.DenyResponseFormat != "json" {
		t.Fatal("explicit opt-out must preserve the JSON default format")
	}
	for _, enabled := range []bool{false, true} {
		config := buildFFINetwork(&NetworkConfig{HTTP: &HTTPConfig{DenyResponse: enabled, DenyResponseFormat: HTTPDenyResponseJSON, DenyMessage: "legacy {host}", NetworkDenyMessage: "blocked {host}", SecretDenyMessage: "check secret access"}})
		if config.HTTP.DenyResponse != enabled {
			t.Fatalf("HTTP denial response flag lost")
		}
		if config.HTTP.DenyResponseFormat != "json" || config.HTTP.DenyMessage != "legacy {host}" {
			t.Fatal("HTTP response format or legacy message lost")
		}
		if config.HTTP.NetworkDenyMessage != "blocked {host}" {
			t.Fatalf("HTTP denial message lost: %q", config.HTTP.NetworkDenyMessage)
		}
		if config.HTTP.SecretDenyMessage != "check secret access" {
			t.Fatalf("secret denial message lost: %q", config.HTTP.SecretDenyMessage)
		}

	}
}

func TestInvalidConfigRejectedBeforeCreate(t *testing.T) {
	missingCA := filepath.Join(t.TempDir(), "missing.pem")
	cases := []struct {
		name    string
		option  SandboxOption
		message string
		cause   error
	}{
		{"http", func(config *SandboxConfig) {
			config.Network = &NetworkConfig{HTTP: &HTTPConfig{DenyResponse: true, DenyResponseFormat: "JSON"}}
		}, "deny_response_format must be text or json", nil},
		{"owned_mount", func(config *SandboxConfig) {
			config.Volumes = map[string]MountConfig{"/data": {kind: MountKindOwned, Owned: "disk"}}
		}, "disk storage requires positive SizeMiB", nil},
		{"registry_ca", func(config *SandboxConfig) {
			config.RegistryCACertPaths = []string{missingCA}
		}, "reading registry CA certs", os.ErrNotExist},
	}
	for _, create := range []struct {
		name string
		call func(context.Context, string, ...SandboxOption) (*Sandbox, error)
	}{
		{"create", CreateSandbox},
		{"connect_or_create", ConnectOrCreateSandbox},
		{"progress", func(ctx context.Context, name string, opts ...SandboxOption) (*Sandbox, error) {
			events, results := CreateSandboxWithProgress(ctx, name, opts...)
			result := <-results
			if _, ok := <-results; ok {
				t.Error("expected exactly one creation result")
			}
			if _, ok := <-events; ok {
				t.Error("invalid config must not emit progress")
			}
			return result.Sandbox, result.Err
		}},
	} {
		for _, tc := range cases {
			t.Run(create.name+"/"+tc.name, func(t *testing.T) {
				ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
				defer cancel()
				_, err := create.call(ctx, "invalid-config", tc.option)
				if !IsKind(err, ErrInvalidConfig) || !strings.Contains(err.Error(), tc.message) {
					t.Fatalf("unexpected error: %v", err)
				}
				if tc.cause != nil && !errors.Is(err, tc.cause) {
					t.Fatalf("lost underlying error: %v", err)
				}
			})
		}
	}
}
