@@ -3,12 +3,15 @@ package database
33import (
44 "context"
55 "fmt"
6+ "slices"
67 "testing"
78 "time"
89
910 "github.com/go-openapi/strfmt"
1011 "github.com/stretchr/testify/require"
1112
13+ "github.com/crowdsecurity/crowdsec/pkg/database/ent"
14+ "github.com/crowdsecurity/crowdsec/pkg/database/ent/meta"
1215 "github.com/crowdsecurity/crowdsec/pkg/models"
1316 "github.com/crowdsecurity/crowdsec/pkg/types"
1417)
@@ -156,3 +159,91 @@ func TestFlushAlerts_MaxItemsKeepsActiveDecisions(t *testing.T) {
156159 require .NoError (t , err )
157160 require .Equal (t , 1 , decCount , "active decision must not be cascade-deleted" )
158161}
162+
163+ func countMetas (t * testing.T , ctx context.Context , c * Client ) (total int , orphans int ) {
164+ t .Helper ()
165+
166+ total , err := c .Ent .Meta .Query ().Count (ctx )
167+ require .NoError (t , err )
168+
169+ orphans , err = c .Ent .Meta .Query ().Where (meta .Not (meta .HasOwner ())).Count (ctx )
170+ require .NoError (t , err )
171+
172+ return total , orphans
173+ }
174+
175+ // A dropped alert must not leave its metas behind: they are inserted before the
176+ // alert row, and only the alert insert links them.
177+ func TestCreateAlert_DroppedAlertLeavesNoMetas (t * testing.T ) {
178+ ctx := t .Context ()
179+ c := getDBClient (t , ctx )
180+
181+ machineID := "test-dropped-alert-metas"
182+ registerFlushTestMachine (t , ctx , c , machineID )
183+
184+ // the decision value is not a valid address, so the decision is discarded and
185+ // the alert with it
186+ dropped := makeFlushAlert ("not-an-ip" , true )
187+ dropped .Meta = models.Meta {{Key : "k1" , Value : "v1" }, {Key : "k2" , Value : "v2" }}
188+
189+ kept := makeFlushAlert ("1.2.3.4" , true )
190+ kept .Meta = models.Meta {{Key : "k1" , Value : "v1" }}
191+
192+ _ , err := c .CreateAlert (ctx , machineID , []* models.Alert {dropped , kept })
193+ require .NoError (t , err )
194+
195+ alerts , err := c .Ent .Alert .Query ().Count (ctx )
196+ require .NoError (t , err )
197+ require .Equal (t , 1 , alerts )
198+
199+ total , orphans := countMetas (t , ctx , c )
200+ require .Equal (t , 1 , total )
201+ require .Zero (t , orphans )
202+ }
203+
204+ // Metas orphaned by an older version are reaped, linked ones are not.
205+ func TestFlushOrphans_DeletesOrphanMetas (t * testing.T ) {
206+ tests := []struct {
207+ name string
208+ orphans int
209+ }{
210+ {name : "single batch" , orphans : 3 },
211+ {name : "several batches" , orphans : orphanMetaBatchSize + 1 },
212+ }
213+
214+ for _ , tc := range tests {
215+ t .Run (tc .name , func (t * testing.T ) {
216+ ctx := t .Context ()
217+ c := getDBClient (t , ctx )
218+
219+ machineID := "test-orphan-metas"
220+ registerFlushTestMachine (t , ctx , c , machineID )
221+
222+ alert := makeFlushAlert ("1.2.3.4" , true )
223+ alert .Meta = models.Meta {{Key : "k1" , Value : "v1" }}
224+
225+ _ , err := c .CreateAlert (ctx , machineID , []* models.Alert {alert })
226+ require .NoError (t , err )
227+
228+ for chunk := range slices .Chunk (make ([]struct {}, tc .orphans ), 500 ) {
229+ builders := make ([]* ent.MetaCreate , len (chunk ))
230+ for i := range chunk {
231+ builders [i ] = c .Ent .Meta .Create ().SetKey ("orphan" ).SetValue ("v" )
232+ }
233+
234+ _ , err := c .Ent .Meta .CreateBulk (builders ... ).Save (ctx )
235+ require .NoError (t , err )
236+ }
237+
238+ total , orphans := countMetas (t , ctx , c )
239+ require .Equal (t , tc .orphans + 1 , total )
240+ require .Equal (t , tc .orphans , orphans )
241+
242+ c .FlushOrphans (ctx )
243+
244+ total , orphans = countMetas (t , ctx , c )
245+ require .Equal (t , 1 , total )
246+ require .Zero (t , orphans )
247+ })
248+ }
249+ }
0 commit comments