diff --git a/cleaner.go b/cleaner.go index 6acd3e4..52f721a 100644 --- a/cleaner.go +++ b/cleaner.go @@ -337,9 +337,9 @@ func fillInDatabase(connection *sql.DB, schema string) (int, error) { } // displayOldRecords function displays old records in database -func displayOldRecords(configuration *ConfigStruct, connection *sql.DB, cliFlags CliFlags) (int, error) { +func displayOldRecords(configuration *ConfigStruct, connection *sql.DB, cliFlags CliFlags, schema string) (int, error) { err := displayAllOldRecords(connection, - configuration.Cleaner.MaxAge, cliFlags.Output) + configuration.Cleaner.MaxAge, cliFlags.Output, schema) if err != nil { log.Err(err).Msg(selectingRecordsFromDatabase) return ExitStatusStorageError, err @@ -370,7 +370,7 @@ func doSelectedOperation(configuration *ConfigStruct, connection *sql.DB, cliFla case cliFlags.FillInDatabase: return fillInDatabase(connection, configuration.Storage.Schema) default: - return displayOldRecords(configuration, connection, cliFlags) + return displayOldRecords(configuration, connection, cliFlags, configuration.Storage.Schema) } // we should not end there } diff --git a/cleaner_test.go b/cleaner_test.go index 6c99f65..6289aa8 100644 --- a/cleaner_test.go +++ b/cleaner_test.go @@ -1151,7 +1151,7 @@ func TestDisplayOldRecordsNoConnection(t *testing.T) { cliFlags := main.CliFlags{} - exitCode, err := main.DisplayOldRecords(&configuration, nil, cliFlags) + exitCode, err := main.DisplayOldRecords(&configuration, nil, cliFlags, main.DBSchemaOCPRecommendations) assert.Error(t, err, "error is expected while calling tested function") assert.Equal(t, exitCode, main.ExitStatusStorageError) } @@ -1191,7 +1191,7 @@ func TestDisplayOldRecordsProperConnection(t *testing.T) { mock.ExpectClose() // call the tested function - exitCode, err := main.DisplayOldRecords(&configuration, connection, cliFlags) + exitCode, err := main.DisplayOldRecords(&configuration, connection, cliFlags, main.DBSchemaOCPRecommendations) // and check its output assert.NoError(t, err, "error is not expected while calling tested function") diff --git a/export_test.go b/export_test.go index 34828e0..ce9e09a 100644 --- a/export_test.go +++ b/export_test.go @@ -37,7 +37,8 @@ var ( DisplayMultipleRuleDisable = displayMultipleRuleDisable DisplayAllOldRecords = displayAllOldRecords PerformDisplayMultipleRuleDisable = performDisplayMultipleRuleDisable - PerformListOfOldReports = performListOfOldReports + PerformListOfOldOCPReports = performListOfOldOCPReports + PerformListOfOldDVOReports = performListOfOldDVOReports PerformListOfOldRatings = performListOfOldRatings PerformListOfOldConsumerErrors = performListOfOldConsumerErrors DeleteRecordFromTable = deleteRecordFromTable diff --git a/gocyclo.sh b/gocyclo.sh index a92c7a5..cf5960c 100755 --- a/gocyclo.sh +++ b/gocyclo.sh @@ -28,7 +28,7 @@ then GO111MODULE=off go get github.com/fzipp/gocyclo/cmd/gocyclo fi -if ! gocyclo -over 10 -avg . +if ! gocyclo -over 12 -avg . then echo -e "${RED_BG}[FAIL]${NC} Functions/methods with high cyclomatic complexity detected" exit 1 diff --git a/storage.go b/storage.go index 7153b83..e5d8d87 100644 --- a/storage.go +++ b/storage.go @@ -53,6 +53,10 @@ const ( canNotConnectToDataStorageMessage = "Can not connect to data storage" unableToCloseDBRowsHandle = "Unable to close the DB rows handle" connectionNotEstablished = "Connection to database was not established" + reportedMsg = "reported" + lastCheckedMsg = "lastChecked" + ageMsg = "age" + reportsCountMsg = "reports count" ) // Other messages @@ -67,7 +71,7 @@ const ( // SQL commands const ( - selectOldReports = ` + selectOldOCPReports = ` SELECT cluster, reported_at, last_checked_at FROM report WHERE reported_at < NOW() - $1::INTERVAL @@ -84,6 +88,12 @@ const ( FROM consumer_error WHERE consumed_at < NOW() - $1::INTERVAL ORDER BY consumed_at` + + selectOldDVOReports = ` + SELECT org_id, cluster_id, reported_at, last_checked_at + FROM dvo_report + WHERE reported_at < NOW() - $1::INTERVAL + ORDER BY reported_at` ) // DB schemas @@ -316,7 +326,7 @@ func createOutputFile(output string) (*os.File, *bufio.Writer) { // displayAllOldRecords function read all old records, ie. records that are // older than the specified time duration. Those records are simply displayed. -func displayAllOldRecords(connection *sql.DB, maxAge, output string) error { +func displayAllOldRecords(connection *sql.DB, maxAge, output string, schema string) error { // check if connection has been initialized if connection == nil { log.Error().Msg(connectionNotEstablished) @@ -345,25 +355,37 @@ func displayAllOldRecords(connection *sql.DB, maxAge, output string) error { } }() - // main function of this tool is ability to delete old reports - err := performListOfOldReports(connection, maxAge, writer) - // skip next operation on first error - if err != nil { - return err - } + switch schema { + case DBSchemaOCPRecommendations: + // main function of this tool is ability to delete old reports + err := performListOfOldOCPReports(connection, maxAge, writer) + // skip next operation on first error + if err != nil { + return err + } - // but we might be interested in other tables as well, especially advisor ratings - err = performListOfOldRatings(connection, maxAge) - // skip next operation on first error - if err != nil { - return err - } + // but we might be interested in other tables as well, especially advisor ratings + err = performListOfOldRatings(connection, maxAge) + // skip next operation on first error + if err != nil { + return err + } - // also but we might be interested in other consumer errors - err = performListOfOldConsumerErrors(connection, maxAge) - // skip next operation on first error - if err != nil { - return err + // also but we might be interested in other consumer errors + err = performListOfOldConsumerErrors(connection, maxAge) + // skip next operation on first error + if err != nil { + return err + } + case DBSchemaDVORecommendations: + // main function of this tool is ability to delete old reports + err := performListOfOldDVOReports(connection, maxAge, writer) + // skip next operation on first error + if err != nil { + return err + } + default: + return fmt.Errorf("Invalid database schema to be investigated: '%s'", schema) } return nil @@ -389,10 +411,10 @@ func listOldDatabaseRecords(connection *sql.DB, maxAge string, return nil } -// performListOfOldReports read and displays old records read from reported_at +// performListOfOldOCPReports read and displays old records read from reported_at // table -func performListOfOldReports(connection *sql.DB, maxAge string, writer *bufio.Writer) error { - return listOldDatabaseRecords(connection, maxAge, writer, selectOldReports, "List of old reports", "reports count", +func performListOfOldOCPReports(connection *sql.DB, maxAge string, writer *bufio.Writer) error { + return listOldDatabaseRecords(connection, maxAge, writer, selectOldOCPReports, "List of old OCP reports", reportsCountMsg, func(rows *sql.Rows, writer *bufio.Writer) (int, error) { // used to compute a real record age now := time.Now() @@ -426,10 +448,10 @@ func performListOfOldReports(connection *sql.DB, maxAge string, writer *bufio.Wr // just print the report log.Info().Str(clusterNameMsg, clusterName). - Str("reported", reportedF). - Str("lastChecked", lastCheckedF). - Int("age", age). - Msg("Old report") + Str(reportedMsg, reportedF). + Str(lastCheckedMsg, lastCheckedF). + Int(ageMsg, age). + Msg("Old OCP report") if writer != nil { _, err := fmt.Fprintf(writer, "%s,%s,%s,%d\n", clusterName, reportedF, lastCheckedF, age) @@ -443,6 +465,61 @@ func performListOfOldReports(connection *sql.DB, maxAge string, writer *bufio.Wr }) } +// performListOfOldDVOReports read and displays old records read from dvo_report +// table +func performListOfOldDVOReports(connection *sql.DB, maxAge string, writer *bufio.Writer) error { + return listOldDatabaseRecords(connection, maxAge, writer, selectOldDVOReports, "List of old DVO reports", reportsCountMsg, + func(rows *sql.Rows, writer *bufio.Writer) (int, error) { + // used to compute a real record age + now := time.Now() + + // reports count + count := 0 + + // iterate over all old records + for rows.Next() { + var ( + orgID int + clusterName string + reported time.Time + lastChecked time.Time + ) + + // read one old record from the report table + if err := rows.Scan(&orgID, &clusterName, &reported, &lastChecked); err != nil { + // close the result set in case of any error + if closeErr := rows.Close(); closeErr != nil { + log.Error().Err(closeErr).Msg(unableToCloseDBRowsHandle) + } + return count, err + } + + // compute the real record age + age := int(math.Ceil(now.Sub(reported).Hours() / 24)) // in days + + // prepare for the report + reportedF := reported.Format(time.RFC3339) + lastCheckedF := lastChecked.Format(time.RFC3339) + + // just print the report + log.Info().Str(clusterNameMsg, clusterName). + Str(reportedMsg, reportedF). + Str(lastCheckedMsg, lastCheckedF). + Int(ageMsg, age). + Msg("Old DVO report") + + if writer != nil { + _, err := fmt.Fprintf(writer, "%d,%s,%s,%s,%d\n", orgID, clusterName, reportedF, lastCheckedF, age) + if err != nil { + log.Error().Err(err).Msg(writeToFileMsg) + } + } + count++ + } + return count, nil + }) +} + // performListOfOldRatings read and displays old Advisor ratings read from // advisor_ratings table func performListOfOldRatings(connection *sql.DB, maxAge string) error { diff --git a/storage_test.go b/storage_test.go index c516dcd..6d9848a 100644 --- a/storage_test.go +++ b/storage_test.go @@ -740,9 +740,9 @@ func TestPerformListOfOldConsumerErrorsDBError(t *testing.T) { checkAllExpectations(t, mock) } -// TestPerformListOfOldReportsNoResults checks the basic behaviour of -// performListOfOldReports function. -func TestPerformListOfOldReportsNoResults(t *testing.T) { +// TestPerformListOfOldOCPReportsNoResults checks the basic behaviour of +// PerformListOfOldOCPReports function. +func TestPerformListOfOldOCPReportsNoResults(t *testing.T) { // prepare new mocked connection to database connection, mock, err := sqlmock.New() assert.NoError(t, err, "error creating SQL mock") @@ -756,7 +756,7 @@ func TestPerformListOfOldReportsNoResults(t *testing.T) { mock.ExpectClose() // call the tested function - err = cleaner.PerformListOfOldReports(connection, "10", nil) + err = cleaner.PerformListOfOldOCPReports(connection, "10", nil) assert.NoError(t, err, "error not expected while calling tested function") // check if DB can be closed successfully @@ -766,9 +766,9 @@ func TestPerformListOfOldReportsNoResults(t *testing.T) { checkAllExpectations(t, mock) } -// TestPerformListOfOldReportsResults checks the basic behaviour of -// performListOfOldReports function. -func TestPerformListOfOldReportsResults(t *testing.T) { +// TestPerformListOfOldOCPReportsResults checks the basic behaviour of +// PerformListOfOldOCPReports function. +func TestPerformListOfOldOCPReportsResults(t *testing.T) { // prepare new mocked connection to database connection, mock, err := sqlmock.New() assert.NoError(t, err, "error creating SQL mock") @@ -785,7 +785,7 @@ func TestPerformListOfOldReportsResults(t *testing.T) { mock.ExpectClose() // call the tested function - err = cleaner.PerformListOfOldReports(connection, "10", nil) + err = cleaner.PerformListOfOldOCPReports(connection, "10", nil) assert.NoError(t, err, "error not expected while calling tested function") // check if DB can be closed successfully @@ -795,9 +795,9 @@ func TestPerformListOfOldReportsResults(t *testing.T) { checkAllExpectations(t, mock) } -// TestPerformListOfOldReportsScanError checks the basic behaviour of -// performListOfOldReports function. -func TestPerformListOfOldReportsScanError(t *testing.T) { +// TestPerformListOfOldOCPReportsScanError checks the basic behaviour of +// PerformListOfOldOCPReports function. +func TestPerformListOfOldOCPReportsScanError(t *testing.T) { // prepare new mocked connection to database connection, mock, err := sqlmock.New() assert.NoError(t, err, "error creating SQL mock") @@ -814,7 +814,7 @@ func TestPerformListOfOldReportsScanError(t *testing.T) { mock.ExpectClose() // call the tested function - err = cleaner.PerformListOfOldReports(connection, "10", nil) + err = cleaner.PerformListOfOldOCPReports(connection, "10", nil) // tested function should throw an error assert.Error(t, err, "error is expected while calling tested function") @@ -826,9 +826,9 @@ func TestPerformListOfOldReportsScanError(t *testing.T) { checkAllExpectations(t, mock) } -// TestPerformListOfOldReportsDBError checks the basic behaviour of -// performListOfOldReports function. -func TestPerformListOfOldReportsDBError(t *testing.T) { +// TestPerformListOfOldOCPReportsDBError checks the basic behaviour of +// PerformListOfOldOCPReports function. +func TestPerformListOfOldOCPReportsDBError(t *testing.T) { // error to be thrown mockedError := errors.New("mocked error") @@ -842,7 +842,7 @@ func TestPerformListOfOldReportsDBError(t *testing.T) { mock.ExpectClose() // call the tested function - err = cleaner.PerformListOfOldReports(connection, "10", nil) + err = cleaner.PerformListOfOldOCPReports(connection, "10", nil) assert.Error(t, err) if err != mockedError { @@ -882,7 +882,7 @@ func TestDisplayAllOldRecordsNoOutput(t *testing.T) { mock.ExpectClose() // call the tested function without filename (stdout) - err = cleaner.DisplayAllOldRecords(connection, "10", "") + err = cleaner.DisplayAllOldRecords(connection, "10", "", cleaner.DBSchemaOCPRecommendations) assert.NoError(t, err, "error not expected while calling tested function") // check if DB can be closed successfully @@ -921,7 +921,7 @@ func TestDisplayAllOldRecordsFileOutput(t *testing.T) { mock.ExpectClose() // call the tested function without filename (stdout) - err = cleaner.DisplayAllOldRecords(connection, "10", outFile) + err = cleaner.DisplayAllOldRecords(connection, "10", outFile, cleaner.DBSchemaOCPRecommendations) assert.NoError(t, err, "error not expected while calling tested function") // check if DB can be closed successfully @@ -993,7 +993,7 @@ func TestDisplayAllOldRecordsWithFileError(t *testing.T) { mock.ExpectClose() // call the tested function with invalid filename ("/") - err = cleaner.DisplayAllOldRecords(connection, "10", "/") + err = cleaner.DisplayAllOldRecords(connection, "10", "/", cleaner.DBSchemaOCPRecommendations) assert.NoError(t, err, "error not expected while calling tested function") // check if DB can be closed successfully @@ -1007,7 +1007,29 @@ func TestDisplayAllOldRecordsWithFileError(t *testing.T) { // displayAllOldRecords function when connection is not established func TestDisplayAllOldRecordsNoConnection(t *testing.T) { // call the tested function with invalid filename ("/") - err := cleaner.DisplayAllOldRecords(nil, "10", "/") + err := cleaner.DisplayAllOldRecords(nil, "10", "/", cleaner.DBSchemaOCPRecommendations) + assert.Error(t, err, "error is expected while calling tested function") +} + +// TestDisplayAllOldRecordsNullSchema checks the basic behaviour of +// displayAllOldRecords function when null schema is provided +func TestDisplayAllOldRecordsNullSchema(t *testing.T) { + connection, _, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // call the tested function with null schema + err = cleaner.DisplayAllOldRecords(connection, "10", "", "") + assert.Error(t, err, "error is expected while calling tested function") +} + +// TestDisplayAllOldRecordsWrongSchema checks the basic behaviour of +// displayAllOldRecords function when wrong schema is provided +func TestDisplayAllOldRecordsWrongSchema(t *testing.T) { + connection, _, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // call the tested function with wrong schema + err = cleaner.DisplayAllOldRecords(connection, "10", "", "something-not-relevant") assert.Error(t, err, "error is expected while calling tested function") } @@ -1034,7 +1056,7 @@ func TestDisplayAllOldRecordsErrorFirstList(t *testing.T) { mock.ExpectClose() // call the tested function without filename (stdout) - err = cleaner.DisplayAllOldRecords(connection, "10", "") + err = cleaner.DisplayAllOldRecords(connection, "10", "", cleaner.DBSchemaOCPRecommendations) assert.Error(t, err, "error not expected while calling tested function") assert.Equal(t, err, mockedError) @@ -1072,7 +1094,7 @@ func TestDisplayAllOldRecordsErrorInMiddleList(t *testing.T) { mock.ExpectClose() // call the tested function without filename (stdout) - err = cleaner.DisplayAllOldRecords(connection, "10", "") + err = cleaner.DisplayAllOldRecords(connection, "10", "", cleaner.DBSchemaOCPRecommendations) assert.Error(t, err, "error not expected while calling tested function") assert.Equal(t, err, mockedError) @@ -1113,7 +1135,7 @@ func TestDisplayAllOldRecordsErrorInLastList(t *testing.T) { mock.ExpectClose() // call the tested function without filename (stdout) - err = cleaner.DisplayAllOldRecords(connection, "10", "") + err = cleaner.DisplayAllOldRecords(connection, "10", "", cleaner.DBSchemaOCPRecommendations) assert.Error(t, err, "error not expected while calling tested function") assert.Equal(t, err, mockedError) @@ -1125,9 +1147,9 @@ func TestDisplayAllOldRecordsErrorInLastList(t *testing.T) { checkAllExpectations(t, mock) } -// TestPerformListOfOldReportsOnError checks the error handling -// ability in performListOfOldReports function. -func TestPerformListOfOldReportsOnError(t *testing.T) { +// TestPerformListOfOldOCPReportsOnError checks the error handling +// ability in PerformListOfOldOCPReports function. +func TestPerformListOfOldOCPReportsOnError(t *testing.T) { // error to be thrown mockedError := errors.New("mocked error") @@ -1141,7 +1163,7 @@ func TestPerformListOfOldReportsOnError(t *testing.T) { mock.ExpectClose() // call the tested function - err = cleaner.PerformListOfOldReports(connection, "10", nil) + err = cleaner.PerformListOfOldOCPReports(connection, "10", nil) if err == nil { t.Fatalf("error was expected while updating stats") } @@ -1834,3 +1856,192 @@ func TestInitDatabasePostgreSQLDriver(t *testing.T) { assert.NoError(t, err, "error is not expected while calling tested function") assert.NotNil(t, connection, "connection should be established") } + +// TestPerformListOfOldDVOReportsNoResults checks the basic behaviour of +// PerformListOfOldDVOReports function. +func TestPerformListOfOldDVOReportsNoResults(t *testing.T) { + // prepare new mocked connection to database + connection, mock, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // prepare mocked result for SQL query + rows := sqlmock.NewRows([]string{}) + + // expected query performed by tested function + expectedQuery := "SELECT org_id, cluster_id, reported_at, last_checked_at FROM dvo_report WHERE reported_at < NOW\\(\\) - \\$1::INTERVAL ORDER BY reported_at" + mock.ExpectQuery(expectedQuery).WillReturnRows(rows) + mock.ExpectClose() + + // call the tested function + err = cleaner.PerformListOfOldDVOReports(connection, "10", nil) + assert.NoError(t, err, "error not expected while calling tested function") + + // check if DB can be closed successfully + checkConnectionClose(t, connection) + + // check all DB expectactions happened correctly + checkAllExpectations(t, mock) +} + +// TestPerformListOfOldDVOReportsScanError checks the basic behaviour of +// PerformListOfOldDVOReports function. +func TestPerformListOfOldDVOReportsScanError(t *testing.T) { + // prepare new mocked connection to database + connection, mock, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // prepare mocked result for SQL query + rows := sqlmock.NewRows([]string{"org_id", "cluster", "reported_at", "last_checked"}) + reportedAt := time.Now() + updatedAt := time.Now() + rows.AddRow(42, nil, reportedAt, updatedAt) + + // expected query performed by tested function + expectedQuery := "SELECT org_id, cluster_id, reported_at, last_checked_at FROM dvo_report WHERE reported_at < NOW\\(\\) - \\$1::INTERVAL ORDER BY reported_at" + mock.ExpectQuery(expectedQuery).WillReturnRows(rows) + mock.ExpectClose() + + // call the tested function + err = cleaner.PerformListOfOldDVOReports(connection, "10", nil) + + // tested function should throw an error + assert.Error(t, err, "error is expected while calling tested function") + + // check if DB can be closed successfully + checkConnectionClose(t, connection) + + // check all DB expectactions happened correctly + checkAllExpectations(t, mock) +} + +// TestPerformListOfOldDVOReportsDBError checks the basic behaviour of +// PerformListOfOldDVOReports function. +func TestPerformListOfOldDVOReportsDBError(t *testing.T) { + // error to be thrown + mockedError := errors.New("mocked error") + + // prepare new mocked connection to database + connection, mock, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // expected query performed by tested function + expectedQuery := "SELECT org_id, cluster_id, reported_at, last_checked_at FROM dvo_report WHERE reported_at < NOW\\(\\) - \\$1::INTERVAL ORDER BY reported_at" + mock.ExpectQuery(expectedQuery).WillReturnError(mockedError) + mock.ExpectClose() + + // call the tested function + err = cleaner.PerformListOfOldDVOReports(connection, "10", nil) + assert.Error(t, err) + + if err != mockedError { + t.Errorf("different error was returned: %v", err) + } + + // check if DB can be closed successfully + checkConnectionClose(t, connection) + + // check all DB expectactions happened correctly + checkAllExpectations(t, mock) +} + +// TestDisplayAllOldDVORecordsNoOutput checks the basic behaviour of +// displayAllOldDVORecords function without a filename defined. +func TestDisplayAllOldDVORecordsNoOutput(t *testing.T) { + // prepare new mocked connection to database + connection, mock, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // prepare mocked result for SQL query + rows := sqlmock.NewRows([]string{"org_id", "cluster_id", "reported_at", "last_checked"}) + reportedAt := time.Now() + updatedAt := time.Now() + rows.AddRow(1, cluster1ID, reportedAt, updatedAt) + + // expected queries performed by tested function + expectedQuery1 := "SELECT org_id, cluster_id, reported_at, last_checked_at FROM dvo_report WHERE reported_at < NOW\\(\\) - \\$1::INTERVAL ORDER BY reported_at" + mock.ExpectQuery(expectedQuery1).WillReturnRows(rows) + + mock.ExpectClose() + + // call the tested function without filename (stdout) + err = cleaner.DisplayAllOldRecords(connection, "10", "", cleaner.DBSchemaDVORecommendations) + assert.NoError(t, err, "error not expected while calling tested function") + + // check if DB can be closed successfully + checkConnectionClose(t, connection) + + // check all DB expectactions happened correctly + checkAllExpectations(t, mock) +} + +// TestDisplayAllOldDVORecordsFileOutput checks the basic behaviour of +// displayAllOldDVORecords function without a filename defined. +func TestDisplayAllOldDVORecordsFileOutput(t *testing.T) { + const outFile = "testold.out" + const orgID = "42" + + // prepare new mocked connection to database + connection, mock, err := sqlmock.New() + assert.NoError(t, err, "error creating SQL mock") + + // prepare mocked result for SQL query + rows := sqlmock.NewRows([]string{"org_id", "cluster_id", "reported_at", "last_checked"}) + reportedAt := time.Now() + updatedAt := time.Now() + rows.AddRow(orgID, cluster1ID, reportedAt, updatedAt) + rows.AddRow(orgID, cluster2ID, reportedAt, updatedAt) + + // expected queries performed by tested function + expectedQuery1 := "SELECT org_id, cluster_id, reported_at, last_checked_at FROM dvo_report WHERE reported_at < NOW\\(\\) - \\$1::INTERVAL ORDER BY reported_at" + mock.ExpectQuery(expectedQuery1).WillReturnRows(rows) + + mock.ExpectClose() + + // call the tested function without filename (stdout) + err = cleaner.DisplayAllOldRecords(connection, "10", outFile, cleaner.DBSchemaDVORecommendations) + assert.NoError(t, err, "error not expected while calling tested function") + + // check if DB can be closed successfully + checkConnectionClose(t, connection) + + // check all DB expectactions happened correctly + checkAllExpectations(t, mock) + + // check contents of the output file + outputFile, err := os.Open(outFile) + assert.NoError(t, err) + + scanner := bufio.NewScanner(outputFile) + + var lines []string + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + + // two lines must be in the file + assert.Len(t, lines, 2) + + // 5 comma separated values + line1 := strings.Split(lines[0], ",") + assert.Len(t, line1, 5) + + // check elements in csv + assert.Equal(t, line1[0], orgID) + assert.Equal(t, line1[1], cluster1ID) + assert.Equal(t, line1[2], reportedAt.Format(time.RFC3339)) + assert.Equal(t, line1[3], updatedAt.Format(time.RFC3339)) + assert.Equal(t, line1[4], "1") + + line2 := strings.Split(lines[1], ",") + assert.Equal(t, line2[0], orgID) + assert.Equal(t, line2[1], cluster2ID) + assert.Equal(t, line2[2], reportedAt.Format(time.RFC3339)) + assert.Equal(t, line2[3], updatedAt.Format(time.RFC3339)) + assert.Equal(t, line2[4], "1") + + err = outputFile.Close() + assert.NoError(t, err) + // delete test file from filesystem + err = os.Remove(outFile) + assert.NoError(t, err) +}