diff --git a/central/complianceoperator/v2/report/manager/format/formatter.go b/central/complianceoperator/v2/report/manager/format/formatter.go index f5f850b13edaf..59a65e583a5be 100644 --- a/central/complianceoperator/v2/report/manager/format/formatter.go +++ b/central/complianceoperator/v2/report/manager/format/formatter.go @@ -5,14 +5,18 @@ import ( "bytes" "fmt" "io" + "strings" "github.com/pkg/errors" "github.com/stackrox/rox/central/complianceoperator/v2/report" + "github.com/stackrox/rox/generated/storage" "github.com/stackrox/rox/pkg/csv" ) const ( - EmptyValue = "Data not found for the cluster" + emptyValue = "Data not found for the cluster" + successfulClusterFmt = "cluster_%s.csv" + failedClusterFmt = "failed_cluster_%s.csv" ) var ( @@ -27,6 +31,12 @@ var ( "Rationale", "Instructions", } + failedClusterCSVHeader = []string{ + "Cluster ID", + "Cluster Name", + "Reason", + "Compliance Operator Version", + } ) //go:generate mockgen-wrapper @@ -53,7 +63,11 @@ func NewFormatter() *FormatterImpl { } } -func (f *FormatterImpl) FormatCSVReport(results map[string][]*report.ResultRow) (buffRet *bytes.Buffer, errRet error) { +// FormatCSVReport generates zip data containing CSV files (one per cluster). +// If a cluster fails, the generated CSV file will contain the reason for the reason but (no check results). +// If a cluster success, the generated CSV file will contain all the check results with enhanced information (e.g. remediation, associated profile, etc) +// The results parameter is expected to contain the clusters that succeed (no failed clusters should be passed in results). +func (f *FormatterImpl) FormatCSVReport(results map[string][]*report.ResultRow, failedClusters map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) (buffRet *bytes.Buffer, errRet error) { var buf bytes.Buffer zipWriter := f.newZipWriter(&buf) defer func() { @@ -62,8 +76,18 @@ func (f *FormatterImpl) FormatCSVReport(results map[string][]*report.ResultRow) errRet = errors.Wrap(err, "unable to create a zip file of the compliance report") } }() + for clusterID, failedCluster := range failedClusters { + fileName := fmt.Sprintf(failedClusterFmt, clusterID) + if err := f.createFailedClusterFileInZip(zipWriter, fileName, failedCluster); err != nil { + return nil, errors.Wrap(err, "error creating failed cluster report") + } + } for clusterID, res := range results { - fileName := fmt.Sprintf("cluster_%s.csv", clusterID) + // We should not receive results from a failed cluster + if _, ok := failedClusters[clusterID]; ok { + continue + } + fileName := fmt.Sprintf(successfulClusterFmt, clusterID) err := f.createCSVInZip(zipWriter, fileName, res) if err != nil { return nil, errors.Wrap(err, "error creating csv report") @@ -83,12 +107,13 @@ func (f *FormatterImpl) createCSVInZip(zipWriter ZipWriter, filename string, clu csvWriter.AddValue(generateRecord(checkRes)) } } else { - csvWriter.AddValue([]string{EmptyValue}) + csvWriter.AddValue([]string{emptyValue}) } return csvWriter.WriteCSV(w) } func generateRecord(row *report.ResultRow) []string { + // The order in the slice needs to match the order defined in `csvHeader` return []string{ row.ControlRef, row.CheckName, @@ -102,6 +127,26 @@ func generateRecord(row *report.ResultRow) []string { } } +func (f *FormatterImpl) createFailedClusterFileInZip(zipWriter ZipWriter, filename string, failedCluster *storage.ComplianceOperatorReportSnapshotV2_FailedCluster) error { + w, err := zipWriter.Create(filename) + if err != nil { + return err + } + csvWriter := f.newCSVWriter(failedClusterCSVHeader, true) + csvWriter.AddValue(generateFailRecord(failedCluster)) + return csvWriter.WriteCSV(w) +} + +func generateFailRecord(failedCluster *storage.ComplianceOperatorReportSnapshotV2_FailedCluster) []string { + // The order in the slice needs to match the order defined in `failedClusterCSVHeader` + return []string{ + failedCluster.GetClusterId(), + failedCluster.GetClusterName(), + strings.Join(failedCluster.GetReasons(), ", "), + failedCluster.GetOperatorVersion(), + } +} + func createNewZipWriter(buf *bytes.Buffer) ZipWriter { return zip.NewWriter(buf) } diff --git a/central/complianceoperator/v2/report/manager/format/formatter_test.go b/central/complianceoperator/v2/report/manager/format/formatter_test.go index 84f17e6b46d1e..b88ce7cd7b3da 100644 --- a/central/complianceoperator/v2/report/manager/format/formatter_test.go +++ b/central/complianceoperator/v2/report/manager/format/formatter_test.go @@ -8,11 +8,18 @@ import ( "github.com/pkg/errors" "github.com/stackrox/rox/central/complianceoperator/v2/report" "github.com/stackrox/rox/central/complianceoperator/v2/report/manager/format/mocks" + "github.com/stackrox/rox/generated/storage" "github.com/stackrox/rox/pkg/csv" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" ) +const ( + clusterID1 = "cluster-1" + clusterID2 = "cluster-2" +) + func TestComplianceReportingFormatter(t *testing.T) { suite.Run(t, new(ComplianceReportingFormatterSuite)) } @@ -27,62 +34,170 @@ type ComplianceReportingFormatterSuite struct { } func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportNoError() { - matcher := &valueMatcher{ - data: getFakeReportData(), - } - s.zipWriter.EXPECT().Create(gomock.Any()).Times(1).Return(nil, nil) - s.csvWriter.EXPECT().AddValue(matcher).Times(2).Do(func(_ any) { - matcher.recordNumber++ + s.Run("with empty failed clusters", func() { + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil) + gomock.InOrder( + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + data := getFakeReportData() + return compareStringSlice(s.T(), target, generateRecord(data[clusterID1][0])) + })).Times(1), + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + data := getFakeReportData() + return compareStringSlice(s.T(), target, generateRecord(data[clusterID1][1])) + })).Times(1), + ) + s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(nil) + s.zipWriter.EXPECT().Close().Times(1).Return(nil) + + buf, err := s.formatter.FormatCSVReport(getFakeReportData(), getFakeEmptyFailedClusters()) + s.Require().NoError(err) + s.Require().NotNil(buf) }) - s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(nil) + s.Run("with nil failed clusters", func() { + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil) + gomock.InOrder( + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + data := getFakeReportData() + return compareStringSlice(s.T(), target, generateRecord(data[clusterID1][0])) + })).Times(1), + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + data := getFakeReportData() + return compareStringSlice(s.T(), target, generateRecord(data[clusterID1][1])) + })).Times(1), + ) + s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(nil) + s.zipWriter.EXPECT().Close().Times(1).Return(nil) + + buf, err := s.formatter.FormatCSVReport(getFakeReportData(), nil) + s.Require().NoError(err) + s.Require().NotNil(buf) + }) +} + +func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportWithFailedClusterNoError() { + gomock.InOrder( + s.zipWriter.EXPECT().Create(fmt.Sprintf(failedClusterFmt, clusterID2)).Times(1).Return(nil, nil), + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil), + ) + gomock.InOrder( + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + _, failed := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateFailRecord(failed[clusterID2])) + })).Times(1), + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + successful, _ := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateRecord(successful[clusterID1][0])) + })).Times(1), + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + successful, _ := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateRecord(successful[clusterID1][1])) + })).Times(1), + ) + s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(2).Return(nil) s.zipWriter.EXPECT().Close().Times(1).Return(nil) - buf, err := s.formatter.FormatCSVReport(getFakeReportData()) + buf, err := s.formatter.FormatCSVReport(getFakeReportDataWithFailedCluster()) s.Require().NoError(err) s.Require().NotNil(buf) } -func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportCreateError() { - s.zipWriter.EXPECT().Create(gomock.Any()).Times(1).Return(nil, errors.New("error")) +func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportWithFailedClusterInResultsParameterNoError() { + gomock.InOrder( + s.zipWriter.EXPECT().Create(fmt.Sprintf(failedClusterFmt, clusterID2)).Times(1).Return(nil, nil), + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil), + ) + gomock.InOrder( + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + _, failed := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateFailRecord(failed[clusterID2])) + })).Times(1), + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + successful, _ := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateRecord(successful[clusterID1][0])) + })).Times(1), + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + successful, _ := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateRecord(successful[clusterID1][1])) + })).Times(1), + ) + s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(2).Return(nil) s.zipWriter.EXPECT().Close().Times(1).Return(nil) - buf, err := s.formatter.FormatCSVReport(getFakeReportData()) - s.Require().Error(err) - s.Require().Nil(buf) + results, failedCluster := getFakeReportDataWithFailedCluster() + // Add empty results to the failed cluster + results[clusterID2] = []*report.ResultRow{} + buf, err := s.formatter.FormatCSVReport(results, failedCluster) + s.Require().NoError(err) + s.Require().NotNil(buf) +} + +func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportCreateError() { + s.Run("zip writer failing to create a file (with no failed clusters) should yield an error", func() { + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, errors.New("error")) + s.zipWriter.EXPECT().Close().Times(1).Return(nil) + + buf, err := s.formatter.FormatCSVReport(getFakeReportData(), getFakeEmptyFailedClusters()) + s.Require().Error(err) + s.Require().Nil(buf) + }) + s.Run("zip writer failing to create a file (containing failed clusters) should yield an error", func() { + s.zipWriter.EXPECT().Create(fmt.Sprintf(failedClusterFmt, clusterID2)).Times(1).Return(nil, errors.New("error")) + s.zipWriter.EXPECT().Close().Times(1).Return(nil) + + buf, err := s.formatter.FormatCSVReport(getFakeReportDataOnlyFailedCluster()) + s.Require().Error(err) + s.Require().Nil(buf) + }) } func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportWriteError() { - s.zipWriter.EXPECT().Create(gomock.Any()).Times(1).Return(nil, nil) - s.csvWriter.EXPECT().AddValue(gomock.Any()).Times(2) - s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(errors.New("error")) - s.zipWriter.EXPECT().Close().Times(1).Return(nil) + s.Run("csv writer failing to create a file (with no failed clusters) should yield an error", func() { + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil) + s.csvWriter.EXPECT().AddValue(gomock.Any()).Times(2) + s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(errors.New("error")) + s.zipWriter.EXPECT().Close().Times(1).Return(nil) - buf, err := s.formatter.FormatCSVReport(getFakeReportData()) - s.Require().Error(err) - s.Require().Nil(buf) + buf, err := s.formatter.FormatCSVReport(getFakeReportData(), getFakeEmptyFailedClusters()) + s.Require().Error(err) + s.Require().Nil(buf) + }) + s.Run("csv writer failing to create a file (containing failed clusters) should yield an error", func() { + s.zipWriter.EXPECT().Create(fmt.Sprintf(failedClusterFmt, clusterID2)).Times(1).Return(nil, nil) + s.csvWriter.EXPECT().AddValue(gomock.Cond[csv.Value](func(target csv.Value) bool { + _, failed := getFakeReportDataWithFailedCluster() + return compareStringSlice(s.T(), target, generateFailRecord(failed[clusterID2])) + })).Times(1) + s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(errors.New("error")) + s.zipWriter.EXPECT().Close().Times(1).Return(nil) + + buf, err := s.formatter.FormatCSVReport(getFakeReportDataOnlyFailedCluster()) + s.Require().Error(err) + s.Require().Nil(buf) + }) } func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportCloseError() { - s.zipWriter.EXPECT().Create(gomock.Any()).Times(1).Return(nil, nil) + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil) s.csvWriter.EXPECT().AddValue(gomock.Any()).Times(2) s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(nil) s.zipWriter.EXPECT().Close().Times(1).Return(errors.New("error")) - buf, err := s.formatter.FormatCSVReport(getFakeReportData()) + buf, err := s.formatter.FormatCSVReport(getFakeReportData(), getFakeEmptyFailedClusters()) s.Require().Error(err) s.Require().Nil(buf) } func (s *ComplianceReportingFormatterSuite) Test_FormatCSVReportEmptyReportNoError() { - s.zipWriter.EXPECT().Create(gomock.Any()).Times(1).Return(nil, nil) + s.zipWriter.EXPECT().Create(fmt.Sprintf(successfulClusterFmt, clusterID1)).Times(1).Return(nil, nil) s.csvWriter.EXPECT().AddValue(&emptyValueMatcher{ - value: EmptyValue, + t: s.T(), + value: emptyValue, data: getFakeEmptyReportData(), }).Times(1) s.csvWriter.EXPECT().WriteCSV(gomock.Any()).Times(1).Return(nil) s.zipWriter.EXPECT().Close().Times(1).Return(nil) - buf, err := s.formatter.FormatCSVReport(getFakeEmptyReportData()) + buf, err := s.formatter.FormatCSVReport(getFakeEmptyReportData(), getFakeEmptyFailedClusters()) s.Require().NoError(err) s.Require().NotNil(buf) } @@ -118,7 +233,7 @@ func getFakeEmptyReportData() map[string][]*report.ResultRow { func getFakeReportData() map[string][]*report.ResultRow { results := make(map[string][]*report.ResultRow) - results["cluster-1"] = []*report.ResultRow{ + results[clusterID1] = []*report.ResultRow{ { ClusterName: "test_cluster-1", CheckName: "test_check-1", @@ -141,7 +256,36 @@ func getFakeReportData() map[string][]*report.ResultRow { return results } +func getFakeReportDataWithFailedCluster() (map[string][]*report.ResultRow, map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) { + failedClusters := make(map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) + failedClusters[clusterID2] = &storage.ComplianceOperatorReportSnapshotV2_FailedCluster{ + ClusterName: "test_cluster-2", + ClusterId: "test_cluster-2-id", + Reasons: []string{"timeout"}, + OperatorVersion: "v1.6.0", + } + results := getFakeReportData() + return results, failedClusters +} + +func getFakeReportDataOnlyFailedCluster() (map[string][]*report.ResultRow, map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) { + failedClusters := make(map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) + failedClusters[clusterID2] = &storage.ComplianceOperatorReportSnapshotV2_FailedCluster{ + ClusterName: "test_cluster-2", + ClusterId: "test_cluster-2-id", + Reasons: []string{"timeout"}, + OperatorVersion: "v1.6.0", + } + results := make(map[string][]*report.ResultRow) + return results, failedClusters +} + +func getFakeEmptyFailedClusters() map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster { + return make(map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) +} + type emptyValueMatcher struct { + t *testing.T value string data map[string][]*report.ResultRow error string @@ -155,7 +299,7 @@ func (m *emptyValueMatcher) Matches(target interface{}) bool { } for range m.data { m.error = fmt.Sprintf("expected record: %s", m.value) - return compareStringSlice(record, []string{m.value}) + return compareStringSlice(m.t, record, []string{m.value}) } return false } @@ -164,43 +308,6 @@ func (m *emptyValueMatcher) String() string { return m.error } -type valueMatcher struct { - recordNumber int - data map[string][]*report.ResultRow - error string -} - -func (m *valueMatcher) Matches(target interface{}) bool { - record, ok := target.(csv.Value) - if !ok { - m.error = "target is not of type csv.Value" - return false - } - recordIt := 0 - for _, clusterData := range m.data { - for _, check := range clusterData { - if recordIt == m.recordNumber { - m.error = fmt.Sprintf("expected record: %v", generateRecord(check)) - return compareStringSlice(record, generateRecord(check)) - } - recordIt++ - } - } - return false -} - -func compareStringSlice(a []string, b []string) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} - -func (m *valueMatcher) String() string { - return m.error +func compareStringSlice(t *testing.T, actual []string, expected []string) bool { + return assert.Equal(t, expected, actual) } diff --git a/central/complianceoperator/v2/report/manager/generator/mocks/report_gen.go b/central/complianceoperator/v2/report/manager/generator/mocks/report_gen.go index 5d0bbc625256f..d98f1d8ee0899 100644 --- a/central/complianceoperator/v2/report/manager/generator/mocks/report_gen.go +++ b/central/complianceoperator/v2/report/manager/generator/mocks/report_gen.go @@ -94,18 +94,18 @@ func (m *MockFormatter) EXPECT() *MockFormatterMockRecorder { } // FormatCSVReport mocks base method. -func (m *MockFormatter) FormatCSVReport(arg0 map[string][]*report.ResultRow) (*bytes.Buffer, error) { +func (m *MockFormatter) FormatCSVReport(arg0 map[string][]*report.ResultRow, arg1 map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) (*bytes.Buffer, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FormatCSVReport", arg0) + ret := m.ctrl.Call(m, "FormatCSVReport", arg0, arg1) ret0, _ := ret[0].(*bytes.Buffer) ret1, _ := ret[1].(error) return ret0, ret1 } // FormatCSVReport indicates an expected call of FormatCSVReport. -func (mr *MockFormatterMockRecorder) FormatCSVReport(arg0 any) *gomock.Call { +func (mr *MockFormatterMockRecorder) FormatCSVReport(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatCSVReport", reflect.TypeOf((*MockFormatter)(nil).FormatCSVReport), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatCSVReport", reflect.TypeOf((*MockFormatter)(nil).FormatCSVReport), arg0, arg1) } // MockResultsAggregator is a mock of ResultsAggregator interface. diff --git a/central/complianceoperator/v2/report/manager/generator/report_gen.go b/central/complianceoperator/v2/report/manager/generator/report_gen.go index d5d7f53cf717a..91725ca0cd9fc 100644 --- a/central/complianceoperator/v2/report/manager/generator/report_gen.go +++ b/central/complianceoperator/v2/report/manager/generator/report_gen.go @@ -34,7 +34,7 @@ type ComplianceReportGenerator interface { // //go:generate mockgen-wrapper type Formatter interface { - FormatCSVReport(map[string][]*report.ResultRow) (*bytes.Buffer, error) + FormatCSVReport(map[string][]*report.ResultRow, map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) (*bytes.Buffer, error) } // ResultsAggregator interface is used to generate the report data diff --git a/central/complianceoperator/v2/report/manager/generator/report_gen_impl.go b/central/complianceoperator/v2/report/manager/generator/report_gen_impl.go index 3f216c2ba0501..4c7ca45aaa0e2 100644 --- a/central/complianceoperator/v2/report/manager/generator/report_gen_impl.go +++ b/central/complianceoperator/v2/report/manager/generator/report_gen_impl.go @@ -82,7 +82,7 @@ func (rg *complianceReportGeneratorImpl) ProcessReportRequest(req *report.Reques reportData := rg.resultsAggregator.GetReportData(req) - zipData, err := rg.formatter.FormatCSVReport(reportData.ResultCSVs) + zipData, err := rg.formatter.FormatCSVReport(reportData.ResultCSVs, nil) if err != nil { if dbErr := reportUtils.UpdateSnapshotOnError(req.Ctx, snapshot, report.ErrReportGeneration, rg.snapshotDS); dbErr != nil { return errors.Wrap(dbErr, "unable to update the snapshot on report generation failure") diff --git a/central/complianceoperator/v2/report/manager/generator/report_gen_impl_test.go b/central/complianceoperator/v2/report/manager/generator/report_gen_impl_test.go index 60c1ed670c648..32dbca720a21a 100644 --- a/central/complianceoperator/v2/report/manager/generator/report_gen_impl_test.go +++ b/central/complianceoperator/v2/report/manager/generator/report_gen_impl_test.go @@ -111,7 +111,7 @@ func (s *ComplainceReportingTestSuite) TestProcessReportRequest() { ReportStatus: &storage.ComplianceOperatorReportStatus{}, }, true, nil) s.resultsAggregator.EXPECT().GetReportData(gomock.Any()).Times(1).Return(&report.Results{}) - s.formatter.EXPECT().FormatCSVReport(gomock.Any()).Times(1) + s.formatter.EXPECT().FormatCSVReport(gomock.Any(), gomock.Any()).Times(1) s.snapshotDS.EXPECT().UpsertSnapshot(gomock.Any(), gomock.Any()).Times(1). DoAndReturn(func(_ any, snapshot *storage.ComplianceOperatorReportSnapshotV2) error { s.Require().Equal(storage.ComplianceOperatorReportStatus_GENERATED, snapshot.GetReportStatus().GetRunState()) @@ -128,7 +128,7 @@ func (s *ComplainceReportingTestSuite) TestProcessReportRequest() { ReportStatus: &storage.ComplianceOperatorReportStatus{}, }, true, nil) s.resultsAggregator.EXPECT().GetReportData(gomock.Any()).Times(1).Return(&report.Results{}) - s.formatter.EXPECT().FormatCSVReport(gomock.Any()).Times(1) + s.formatter.EXPECT().FormatCSVReport(gomock.Any(), gomock.Any()).Times(1) gomock.InOrder( s.snapshotDS.EXPECT().UpsertSnapshot(gomock.Any(), gomock.Any()).Times(1). DoAndReturn(func(_ any, snapshot *storage.ComplianceOperatorReportSnapshotV2) error { @@ -170,7 +170,7 @@ func (s *ComplainceReportingTestSuite) TestProcessReportRequest() { ReportStatus: &storage.ComplianceOperatorReportStatus{}, }, true, nil) s.resultsAggregator.EXPECT().GetReportData(gomock.Any()).Times(1).Return(&report.Results{}) - s.formatter.EXPECT().FormatCSVReport(gomock.Any()).Times(1) + s.formatter.EXPECT().FormatCSVReport(gomock.Any(), gomock.Any()).Times(1) gomock.InOrder( s.snapshotDS.EXPECT().UpsertSnapshot(gomock.Any(), gomock.Any()).Times(1). DoAndReturn(func(_ any, snapshot *storage.ComplianceOperatorReportSnapshotV2) error {