diff options
Diffstat (limited to 'tests/regression_394_test.go')
-rw-r--r-- | tests/regression_394_test.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/tests/regression_394_test.go b/tests/regression_394_test.go new file mode 100644 index 0000000..7306293 --- /dev/null +++ b/tests/regression_394_test.go @@ -0,0 +1,126 @@ +package icingadb_test + +import ( + "github.com/icinga/icinga-testing/utils" + "github.com/icinga/icinga-testing/utils/eventually" + "github.com/jmoiron/sqlx" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "strconv" + "testing" + "time" +) + +// Regression test for https://github.com/Icinga/icingadb/pull/394 +// +// Upsert and delete runtime objects for the same object must be executed in-order. Otherwise, an old update might +// replace the object in the database with an older version or delete an object even if it still exists. +func TestRegression394(t *testing.T) { + numObjects := 200 + + logger := it.Logger(t) + + r := it.RedisServerT(t) + rdb := getDatabase(t) + i := it.Icinga2NodeT(t, "master") + i.EnableIcingaDb(r) + err := i.Reload() + require.NoError(t, err, "icinga2 reload") + + // Wait for Icinga 2 to signal a successful dump before starting + // Icinga DB to ensure that we actually test the initial sync. + logger.Debug("waiting for icinga2 dump done signal") + waitForDumpDoneSignal(t, r, 20*time.Second, 100*time.Millisecond) + + // Only after that, start Icinga DB. + logger.Debug("starting icingadb") + it.IcingaDbInstanceT(t, r, rdb) + + client := i.ApiClient() + + db, err := sqlx.Open(rdb.Driver(), rdb.DSN()) + require.NoError(t, err, "connecting to SQL database shouldn't fail") + t.Cleanup(func() { _ = db.Close() }) + + waitForPendingRuntimeUpdates := func(t *testing.T) { + // To verify that all host runtime updates up to now have been processed, create a marker host, wait for it to + // appear in the database, delete it, and wait for it to disappear again. + markerName := utils.UniqueName(t, "marker") + + client.CreateHost(t, markerName, nil) + eventually.Require(t, func(t require.TestingT) { + var count int + err = db.Get(&count, db.Rebind("SELECT COUNT(*) FROM host WHERE name = ?"), markerName) + require.NoError(t, err, "select host count from database") + assert.Equalf(t, 1, count, "marker host %q should appear in database", markerName) + }, 10*time.Second, 100*time.Millisecond) + + client.DeleteHost(t, markerName, true) + eventually.Require(t, func(t require.TestingT) { + var count int + err = db.Get(&count, db.Rebind("SELECT COUNT(*) FROM host WHERE name = ?"), markerName) + require.NoError(t, err, "select host count from database") + assert.Zerof(t, count, "marker host %q should disappear from database", markerName) + }, 10*time.Second, 100*time.Millisecond) + } + + t.Run("CreateAndDelete", func(t *testing.T) { + // This test creates a number of hosts and deletes them immediately afterwards. If the delete operation for a + // host is executed before the upsert operation, it would still exist in the database even though the object + // is gone from Icinga 2. + + t.Parallel() + + namePrefix := utils.UniqueName(t, "host") + "-" + + for i := 0; i < numObjects; i++ { + name := namePrefix + strconv.Itoa(i) + client.CreateHost(t, name, nil) + client.DeleteHost(t, name, true) + } + + waitForPendingRuntimeUpdates(t) + + var countAfter int + err = db.Get(&countAfter, db.Rebind("SELECT COUNT(*) FROM host WHERE name LIKE ?"), namePrefix+"%") + require.NoError(t, err, "select host count from database") + assert.Zero(t, countAfter, "no hosts should be left in database") + + }) + + t.Run("DeleteAndRecreate", func(t *testing.T) { + // This test performs an operation that is probably more useful: delete an existing object and recreate it + // immediately afterwards. If the delete operation is delayed, the host will be missing from the database. + + t.Parallel() + + namePrefix := utils.UniqueName(t, "host") + "-" + hosts := make([]string, numObjects) + + for i := range hosts { + name := namePrefix + strconv.Itoa(i) + client.CreateHost(t, name, nil) + hosts[i] = name + } + + waitForPendingRuntimeUpdates(t) + + var countBefore int + err = db.Get(&countBefore, db.Rebind("SELECT COUNT(*) FROM host WHERE name LIKE ?"), namePrefix+"%") + require.NoError(t, err, "select host count from database") + assert.Equal(t, numObjects, countBefore, "all hosts should exist in database before recreation") + + for _, name := range hosts { + client.DeleteHost(t, name, true) + client.CreateHost(t, name, nil) + } + + waitForPendingRuntimeUpdates(t) + + var countAfter int + err = db.Get(&countAfter, db.Rebind("SELECT COUNT(*) FROM host WHERE name LIKE ?"), namePrefix+"%") + require.NoError(t, err, "select host count from database") + assert.Equal(t, numObjects, countAfter, "all hosts should exist in database after recreation") + }) + +} |