cli/pkg/container/state_test.go
Snider 7741360bd5 Migrate pkg/container to io.Medium abstraction (#292)
* chore(io): migrate pkg/container to Medium abstraction

Migrated State, Templates, and LinuxKitManager in pkg/container to use
the io.Medium abstraction for storage operations.

- Introduced TemplateManager struct to handle template logic with injected medium.
- Updated State struct to use injected medium for persistence.
- Updated LinuxKitManager to hold and use an io.Medium instance.
- Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs.
- Adapted and maintained comprehensive test coverage in linuxkit_test.go.
- Fixed naming collision with standard io package by aliasing it as goio.

* chore(io): migrate pkg/container to Medium abstraction (v2)

- Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium.
- Introduced TemplateManager struct for dependency injection.
- Updated all call sites in internal/cmd/vm and pkg/devops.
- Restored and adapted comprehensive test suite in linuxkit_test.go.
- Fixed naming collisions and followed project test naming conventions.

* chore(io): address PR feedback for container Medium migration

- Added Open method to io.Medium interface to support log streaming.
- Implemented Open in local.Medium and MockMedium.
- Fixed extension inconsistency in GetTemplate (.yml vs .yaml).
- Refactored TemplateManager to use configurable WorkingDir and HomeDir.
- Reused TemplateManager instance in cmd_templates.go.
- Updated LinuxKitManager to use medium.Open for log access.
- Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00

223 lines
5 KiB
Go

package container
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/host-uk/core/pkg/io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewState_Good(t *testing.T) {
state := NewState(io.Local, "/tmp/test-state.json")
assert.NotNil(t, state)
assert.NotNil(t, state.Containers)
assert.Equal(t, "/tmp/test-state.json", state.FilePath())
}
func TestLoadState_Good_NewFile(t *testing.T) {
// Test loading from non-existent file
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
state, err := LoadState(io.Local, statePath)
require.NoError(t, err)
assert.NotNil(t, state)
assert.Empty(t, state.Containers)
}
func TestLoadState_Good_ExistingFile(t *testing.T) {
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
// Create a state file with data
content := `{
"containers": {
"abc12345": {
"id": "abc12345",
"name": "test-container",
"image": "/path/to/image.iso",
"status": "running",
"pid": 12345,
"started_at": "2024-01-01T00:00:00Z"
}
}
}`
err := os.WriteFile(statePath, []byte(content), 0644)
require.NoError(t, err)
state, err := LoadState(io.Local, statePath)
require.NoError(t, err)
assert.Len(t, state.Containers, 1)
c, ok := state.Get("abc12345")
assert.True(t, ok)
assert.Equal(t, "test-container", c.Name)
assert.Equal(t, StatusRunning, c.Status)
}
func TestLoadState_Bad_InvalidJSON(t *testing.T) {
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
// Create invalid JSON
err := os.WriteFile(statePath, []byte("invalid json{"), 0644)
require.NoError(t, err)
_, err = LoadState(io.Local, statePath)
assert.Error(t, err)
}
func TestState_Add_Good(t *testing.T) {
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
state := NewState(io.Local, statePath)
container := &Container{
ID: "abc12345",
Name: "test",
Image: "/path/to/image.iso",
Status: StatusRunning,
PID: 12345,
StartedAt: time.Now(),
}
err := state.Add(container)
require.NoError(t, err)
// Verify it's in memory
c, ok := state.Get("abc12345")
assert.True(t, ok)
assert.Equal(t, container.Name, c.Name)
// Verify file was created
_, err = os.Stat(statePath)
assert.NoError(t, err)
}
func TestState_Update_Good(t *testing.T) {
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
state := NewState(io.Local, statePath)
container := &Container{
ID: "abc12345",
Status: StatusRunning,
}
_ = state.Add(container)
// Update status
container.Status = StatusStopped
err := state.Update(container)
require.NoError(t, err)
// Verify update
c, ok := state.Get("abc12345")
assert.True(t, ok)
assert.Equal(t, StatusStopped, c.Status)
}
func TestState_Remove_Good(t *testing.T) {
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
state := NewState(io.Local, statePath)
container := &Container{
ID: "abc12345",
}
_ = state.Add(container)
err := state.Remove("abc12345")
require.NoError(t, err)
_, ok := state.Get("abc12345")
assert.False(t, ok)
}
func TestState_Get_Bad_NotFound(t *testing.T) {
state := NewState(io.Local, "/tmp/test-state.json")
_, ok := state.Get("nonexistent")
assert.False(t, ok)
}
func TestState_All_Good(t *testing.T) {
tmpDir := t.TempDir()
statePath := filepath.Join(tmpDir, "containers.json")
state := NewState(io.Local, statePath)
_ = state.Add(&Container{ID: "aaa11111"})
_ = state.Add(&Container{ID: "bbb22222"})
_ = state.Add(&Container{ID: "ccc33333"})
all := state.All()
assert.Len(t, all, 3)
}
func TestState_SaveState_Good_CreatesDirectory(t *testing.T) {
tmpDir := t.TempDir()
nestedPath := filepath.Join(tmpDir, "nested", "dir", "containers.json")
state := NewState(io.Local, nestedPath)
_ = state.Add(&Container{ID: "abc12345"})
err := state.SaveState()
require.NoError(t, err)
// Verify directory was created
_, err = os.Stat(filepath.Dir(nestedPath))
assert.NoError(t, err)
}
func TestDefaultStateDir_Good(t *testing.T) {
dir, err := DefaultStateDir()
require.NoError(t, err)
assert.Contains(t, dir, ".core")
}
func TestDefaultStatePath_Good(t *testing.T) {
path, err := DefaultStatePath()
require.NoError(t, err)
assert.Contains(t, path, "containers.json")
}
func TestDefaultLogsDir_Good(t *testing.T) {
dir, err := DefaultLogsDir()
require.NoError(t, err)
assert.Contains(t, dir, "logs")
}
func TestLogPath_Good(t *testing.T) {
path, err := LogPath("abc12345")
require.NoError(t, err)
assert.Contains(t, path, "abc12345.log")
}
func TestEnsureLogsDir_Good(t *testing.T) {
// This test creates real directories - skip in CI if needed
err := EnsureLogsDir(io.Local)
assert.NoError(t, err)
logsDir, _ := DefaultLogsDir()
_, err = os.Stat(logsDir)
assert.NoError(t, err)
}
func TestGenerateID_Good(t *testing.T) {
id1, err := GenerateID()
require.NoError(t, err)
assert.Len(t, id1, 8)
id2, err := GenerateID()
require.NoError(t, err)
assert.Len(t, id2, 8)
// IDs should be different
assert.NotEqual(t, id1, id2)
}