diff --git a/pkg/network/network_test.go b/pkg/network/network_test.go new file mode 100644 index 00000000..5676711e --- /dev/null +++ b/pkg/network/network_test.go @@ -0,0 +1,70 @@ +// Copyright (c) 2023-2025, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetTapIndex(t *testing.T) { + // This test just verifies the function doesn't panic + // The actual count depends on the system's network interfaces + index, err := getTapIndex() + assert.NoError(t, err, "getTapIndex() should not return an error") + assert.GreaterOrEqual(t, index, 0, "Index should be non-negative") + assert.LessOrEqual(t, index, 255, "Index should not exceed 255") +} + +func TestNewNetworkManager(t *testing.T) { + tests := []struct { + name string + networkType string + expectedErr bool + expectedType string + }{ + { + name: "static network manager", + networkType: "static", + expectedErr: false, + expectedType: "*network.StaticNetwork", + }, + { + name: "dynamic network manager", + networkType: "dynamic", + expectedErr: false, + expectedType: "*network.DynamicNetwork", + }, + { + name: "invalid network type", + networkType: "invalid", + expectedErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := NewNetworkManager(tt.networkType) + if tt.expectedErr { + assert.Error(t, err, "NewNetworkManager() should return an error") + } else { + assert.NoError(t, err, "NewNetworkManager() should not return an error") + assert.NotNil(t, got, "NewNetworkManager() should return a non-nil manager") + } + }) + } +} diff --git a/pkg/unikontainers/rootfs_test.go b/pkg/unikontainers/rootfs_test.go new file mode 100644 index 00000000..278394b1 --- /dev/null +++ b/pkg/unikontainers/rootfs_test.go @@ -0,0 +1,157 @@ +// Copyright (c) 2023-2025, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unikontainers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urunc-dev/urunc/pkg/unikontainers/types" +) + +func TestNewRootfsResult(t *testing.T) { + expected := types.RootfsParams{ + Type: "initrd", + Path: "/path/to/initrd", + MountedPath: "/mnt/rootfs", + MonRootfs: "/run/urunc/mon", + } + + got := newRootfsResult("initrd", "/path/to/initrd", "/mnt/rootfs", "/run/urunc/mon") + + assert.Equal(t, expected.Type, got.Type, "Type should match") + assert.Equal(t, expected.Path, got.Path, "Path should match") + assert.Equal(t, expected.MountedPath, got.MountedPath, "MountedPath should match") + assert.Equal(t, expected.MonRootfs, got.MonRootfs, "MonRootfs should match") +} + +func TestRootfsSelector_TryInitrd(t *testing.T) { + tests := []struct { + name string + annot map[string]string + expectedFound bool + expectedType string + expectedPath string + }{ + { + name: "initrd present", + annot: map[string]string{ + annotInitrd: "/path/to/initrd.img", + }, + expectedFound: true, + expectedType: "initrd", + expectedPath: "/path/to/initrd.img", + }, + { + name: "initrd missing", + annot: map[string]string{}, + expectedFound: false, + }, + { + name: "initrd empty", + annot: map[string]string{ + annotInitrd: "", + }, + expectedFound: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + rs := &rootfsSelector{ + annot: tt.annot, + cntrRootfs: "/container/rootfs", + } + + got, found := rs.tryInitrd() + + assert.Equal(t, tt.expectedFound, found, "tryInitrd() found mismatch") + + if found { + assert.Equal(t, tt.expectedType, got.Type, "tryInitrd() Type mismatch") + assert.Equal(t, tt.expectedPath, got.Path, "tryInitrd() Path mismatch") + } + }) + } +} + +func TestRootfsSelector_ShouldMountContainerRootfs(t *testing.T) { + tests := []struct { + name string + annot map[string]string + expected bool + }{ + { + name: "mount rootfs true", + annot: map[string]string{ + annotMountRootfs: "true", + }, + expected: true, + }, + { + name: "mount rootfs 1", + annot: map[string]string{ + annotMountRootfs: "1", + }, + expected: true, + }, + { + name: "mount rootfs false", + annot: map[string]string{ + annotMountRootfs: "false", + }, + expected: false, + }, + { + name: "mount rootfs 0", + annot: map[string]string{ + annotMountRootfs: "0", + }, + expected: false, + }, + { + name: "mount rootfs missing", + annot: map[string]string{}, + expected: false, + }, + { + name: "mount rootfs empty", + annot: map[string]string{ + annotMountRootfs: "", + }, + expected: false, + }, + { + name: "mount rootfs invalid", + annot: map[string]string{ + annotMountRootfs: "invalid", + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + rs := &rootfsSelector{ + annot: tt.annot, + } + + got := rs.shouldMountContainerRootfs() + assert.Equal(t, tt.expected, got, "shouldMountContainerRootfs() mismatch") + }) + } +} diff --git a/pkg/unikontainers/vaccel_test.go b/pkg/unikontainers/vaccel_test.go new file mode 100644 index 00000000..f887851d --- /dev/null +++ b/pkg/unikontainers/vaccel_test.go @@ -0,0 +1,171 @@ +// Copyright (c) 2023-2025, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unikontainers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + minCID = 3 + maxCID = 99 +) + +func TestIdToGuestCID(t *testing.T) { + tests := []struct { + name string + id string + expectedCID int + }{ + { + name: "empty string", + id: "", + expectedCID: idToGuestCID(""), + }, + { + name: "simple id", + id: "container123", + expectedCID: idToGuestCID("container123"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := idToGuestCID(tt.id) + + // Check if CID is within valid range + assert.GreaterOrEqual(t, got, minCID, "CID should be >= %d", minCID) + assert.LessOrEqual(t, got, maxCID, "CID should be <= %d", maxCID) + + // Verify the expected result matches + assert.Equal(t, tt.expectedCID, got, "CID should match expected value") + }) + } +} + +func TestIsValidVSockAddress(t *testing.T) { + tests := []struct { + name string + rpcAddress string + monitor string + expectedValid bool + expectedErr bool + expectedPath string + }{ + { + name: "valid qemu vsock address", + rpcAddress: "vsock://2:1234", + monitor: "qemu", + expectedValid: true, + expectedErr: false, + expectedPath: "", + }, + { + name: "invalid qemu vsock - wrong CID", + rpcAddress: "vsock://3:1234", + monitor: "qemu", + expectedValid: false, + expectedErr: true, + }, + { + name: "invalid qemu vsock - no port", + rpcAddress: "vsock://2:", + monitor: "qemu", + expectedValid: false, + expectedErr: true, + }, + { + name: "invalid qemu vsock - malformed", + rpcAddress: "vsock://invalid", + monitor: "qemu", + expectedValid: false, + expectedErr: true, + }, + { + name: "not a vsock address", + rpcAddress: "http://localhost:1234", + monitor: "qemu", + expectedValid: false, + expectedErr: true, + }, + { + name: "empty address", + rpcAddress: "", + monitor: "qemu", + expectedValid: false, + expectedErr: true, + }, + { + name: "valid firecracker unix socket", + rpcAddress: "unix:///tmp/vaccel.sock_1234", + monitor: "firecracker", + expectedValid: true, + expectedErr: false, + expectedPath: "/tmp", + }, + { + name: "valid firecracker nested path", + rpcAddress: "unix:///var/run/urunc/vaccel.sock_5678", + monitor: "firecracker", + expectedValid: true, + expectedErr: false, + expectedPath: "/var/run/urunc", + }, + { + name: "firecracker invalid - wrong socket name", + rpcAddress: "unix:///tmp/test.sock", + monitor: "firecracker", + expectedValid: false, + expectedErr: true, + }, + { + name: "firecracker invalid - no unix prefix", + rpcAddress: "/tmp/vaccel.sock_1234", + monitor: "firecracker", + expectedValid: false, + expectedErr: true, + }, + { + name: "unsupported monitor", + rpcAddress: "vsock://2:1234", + monitor: "kvm", + expectedValid: false, + expectedErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + addr := tt.rpcAddress + gotValid, gotPath, err := isValidVSockAddress(&addr, tt.monitor) + + if tt.expectedErr { + assert.Error(t, err, "isValidVSockAddress() should return an error") + } else { + assert.NoError(t, err, "isValidVSockAddress() should not return an error") + } + + assert.Equal(t, tt.expectedValid, gotValid, "isValidVSockAddress() valid mismatch") + + if tt.expectedPath != "" { + assert.Equal(t, tt.expectedPath, gotPath, "isValidVSockAddress() path mismatch") + } + }) + } +}