From 51fc8827f54436864924ea8d4dc96111da81e5bf Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 20 Nov 2017 15:43:56 -0500 Subject: Add tests for SQL-backed state impl Minor changes to container.go and sql_state.go to fix issues identified by the tests Signed-off-by: Matthew Heon --- libpod/sql_state_test.go | 520 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 libpod/sql_state_test.go (limited to 'libpod/sql_state_test.go') diff --git a/libpod/sql_state_test.go b/libpod/sql_state_test.go new file mode 100644 index 000000000..b50f3aced --- /dev/null +++ b/libpod/sql_state_test.go @@ -0,0 +1,520 @@ +package libpod + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "testing" + "time" + + "github.com/opencontainers/runtime-tools/generate" + "github.com/stretchr/testify/assert" +) + +func getTestContainer(id, name string) *Container { + ctr := &Container{ + config: &containerConfig{ + ID: id, + Name: name, + RootfsImageID: id, + RootfsImageName: "testimg", + UseImageConfig: true, + StaticDir: "/does/not/exist/", + Stdin: true, + Labels: make(map[string]string), + StopSignal: 0, + CreatedTime: time.Now(), + }, + state: &containerRuntimeInfo{ + State: ContainerStateRunning, + ConfigPath: "/does/not/exist/specs/" + id, + RunDir: "/does/not/exist/tmp/", + Mounted: true, + Mountpoint: "/does/not/exist/tmp/" + id, + }, + valid: true, + } + + g := generate.New() + ctr.config.Spec = g.Spec() + + ctr.config.Labels["test"] = "testing" + + return ctr +} + +// This horrible hack tests if containers are equal in a way that should handle +// empty arrays being dropped to nil pointers in the spec JSON +func testContainersEqual(a, b *Container) bool { + if a == nil && b == nil { + return true + } else if a == nil || b == nil { + return false + } + + if a.valid != b.valid { + return false + } + + aConfigJSON, err := json.Marshal(a.config) + if err != nil { + return false + } + + bConfigJSON, err := json.Marshal(b.config) + if err != nil { + return false + } + + if !reflect.DeepEqual(aConfigJSON, bConfigJSON) { + return false + } + + aStateJSON, err := json.Marshal(a.state) + if err != nil { + return false + } + + bStateJSON, err := json.Marshal(b.state) + if err != nil { + return false + } + + return reflect.DeepEqual(aStateJSON, bStateJSON) +} + +// Get an empty state for use in tests +// An empty Runtime is provided +func getEmptyState() (s State, p string, err error) { + tmpDir, err := ioutil.TempDir("", "libpod_state_test_") + if err != nil { + return nil, "", err + } + defer func() { + if err != nil { + os.RemoveAll(tmpDir) + } + }() + + dbPath := filepath.Join(tmpDir, "db.sql") + lockPath := filepath.Join(tmpDir, "db.lck") + + state, err := NewSQLState(dbPath, lockPath, tmpDir, nil) + if err != nil { + return nil, "", err + } + + return state, tmpDir, nil +} + +func TestAddAndGetContainer(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr, retrievedCtr) { + assert.EqualValues(t, testCtr, retrievedCtr) + } +} + +func TestAddAndGetContainerFromMultiple(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr1 := getTestContainer("11111111111111111111111111111111", "test1") + testCtr2 := getTestContainer("22222222222222222222222222222222", "test2") + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.NoError(t, err) + + retrievedCtr, err := state.Container(testCtr1.ID()) + assert.NoError(t, err) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr1, retrievedCtr) { + assert.EqualValues(t, testCtr1, retrievedCtr) + } +} + +func TestAddInvalidContainerFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + err = state.AddContainer(&Container{}) + assert.Error(t, err) +} + +func TestAddDuplicateIDFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr1 := getTestContainer("11111111111111111111111111111111", "test1") + testCtr2 := getTestContainer(testCtr1.ID(), "test2") + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.Error(t, err) +} + +func TestAddDuplicateNameFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr1 := getTestContainer("11111111111111111111111111111111", "test1") + testCtr2 := getTestContainer("22222222222222222222222222222222", testCtr1.Name()) + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.Error(t, err) +} + +func TestGetNonexistantContainerFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + _, err = state.Container("does not exist") + assert.Error(t, err) +} + +func TestGetContainerWithEmptyIDFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + _, err = state.Container("") + assert.Error(t, err) +} + +func TestLookupContainerWithEmptyIDFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + _, err = state.LookupContainer("") + assert.Error(t, err) +} + +func TestLookupNonexistantContainerFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + + _, err = state.LookupContainer("does not exist") + assert.Error(t, err) +} + +func TestLookupContainerByFullID(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.LookupContainer(testCtr.ID()) + assert.NoError(t, err) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr, retrievedCtr) { + assert.EqualValues(t, testCtr, retrievedCtr) + } +} + +func TestLookupContainerByUniquePartialID(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.LookupContainer(testCtr.ID()[0:8]) + assert.NoError(t, err) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr, retrievedCtr) { + assert.EqualValues(t, testCtr, retrievedCtr) + } +} + +func TestLookupContainerByNonUniquePartialIDFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr1 := getTestContainer("00000000000000000000000000000000", "test1") + testCtr2 := getTestContainer("00000000000000000000000000000001", "test2") + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.NoError(t, err) + + _, err = state.LookupContainer(testCtr1.ID()[0:8]) + assert.Error(t, err) +} + +func TestLookupContainerByName(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.LookupContainer(testCtr.Name()) + assert.NoError(t, err) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr, retrievedCtr) { + assert.EqualValues(t, testCtr, retrievedCtr) + } +} + +func TestHasContainerEmptyIDFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + _, err = state.HasContainer("") + assert.Error(t, err) +} + +func TestHasContainerNoSuchContainerReturnsFalse(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + exists, err := state.HasContainer("does not exist") + assert.NoError(t, err) + assert.False(t, exists) +} + +func TestHasContainerFindsContainer(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + exists, err := state.HasContainer(testCtr.ID()) + assert.NoError(t, err) + assert.True(t, exists) +} + +func TestSaveAndUpdateContainer(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + retrievedCtr.state.State = ContainerStateStopped + retrievedCtr.state.ExitCode = 127 + retrievedCtr.state.FinishedTime = time.Now() + + err = state.SaveContainer(retrievedCtr) + assert.NoError(t, err) + + err = state.UpdateContainer(testCtr) + assert.NoError(t, err) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr, retrievedCtr) { + assert.EqualValues(t, testCtr, retrievedCtr) + } +} + +func TestUpdateContainerNotInDatabaseReturnsError(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.UpdateContainer(testCtr) + assert.Error(t, err) + assert.False(t, testCtr.valid) +} + +func TestUpdateInvalidContainerReturnsError(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + err = state.UpdateContainer(&Container{}) + assert.Error(t, err) +} + +func TestSaveInvalidContainerReturnsError(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + err = state.SaveContainer(&Container{}) + assert.Error(t, err) +} + +func TestSaveContainerNotInStateReturnsError(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.SaveContainer(testCtr) + assert.Error(t, err) +} + +func TestRemoveContainer(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 1, len(ctrs)) + + err = state.RemoveContainer(testCtr) + assert.NoError(t, err) + + ctrs2, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(ctrs2)) +} + +func TestRemoveNonexistantContainerFails(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.RemoveContainer(testCtr) + assert.Error(t, err) +} + +func TestGetAllContainersOnNewStateIsEmpty(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(ctrs)) +} + +func TestGetAllContainersWithOneContainer(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 1, len(ctrs)) + + // Use assert.EqualValues if the test fails to pretty print diff + // between actual and expected + if !testContainersEqual(testCtr, ctrs[0]) { + assert.EqualValues(t, testCtr, ctrs[0]) + } +} + +func TestGetAllContainersTwoContainers(t *testing.T) { + state, path, err := getEmptyState() + assert.NoError(t, err) + defer os.RemoveAll(path) + defer state.Close() + + testCtr1 := getTestContainer("11111111111111111111111111111111", "test1") + testCtr2 := getTestContainer("22222222222222222222222222222222", "test2") + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.NoError(t, err) + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 2, len(ctrs)) +} -- cgit v1.2.3-54-g00ecf