@@ -3,7 +3,6 @@ package tests
33import (
44 "net/http/httptest"
55 "testing"
6- "time"
76
87 "github.com/l3montree-dev/devguard/database/models"
98 "github.com/l3montree-dev/devguard/dtos"
@@ -124,9 +123,6 @@ func TestArtifactControllerDeleteArtifact(t *testing.T) {
124123 err := f .App .ArtifactController .DeleteArtifact (ctx )
125124 assert .NoError (t , err )
126125
127- // Wait for Fire and Forget to complete
128- time .Sleep (500 * time .Millisecond )
129-
130126 // Verify artifact was deleted
131127 var deletedArtifact models.Artifact
132128 err = f .DB .First (& deletedArtifact , "artifact_name = ? AND asset_id = ?" , artifact1 .ArtifactName , asset .ID ).Error
@@ -216,5 +212,153 @@ func TestArtifactControllerDeleteArtifact(t *testing.T) {
216212 err = f .DB .First (& deletedArtifact , "artifact_name = ? AND asset_id = ?" , artifact3 .ArtifactName , asset .ID ).Error
217213 assert .Error (t , err , "Artifact should be deleted" )
218214 })
215+
216+ t .Run ("should delete dependency vuln when it only belongs to the deleted artifact" , func (t * testing.T ) {
217+ // Create a new artifact for this test
218+ artifactSingle := models.Artifact {
219+ ArtifactName : "artifact-single-vuln" ,
220+ AssetVersionName : assetVersion .Name ,
221+ AssetID : asset .ID ,
222+ }
223+ assert .NoError (t , f .DB .Create (& artifactSingle ).Error )
224+
225+ // Create a component for this test
226+ componentSingle := models.Component {
227+ ID : "pkg:npm/single-artifact-vuln-package@1.0.0" ,
228+ }
229+ assert .NoError (t , f .DB .Create (& componentSingle ).Error )
230+
231+ // Create a vulnerability that only belongs to this artifact
232+ vulnSingle := models.DependencyVuln {
233+ Vulnerability : models.Vulnerability {
234+ AssetID : asset .ID ,
235+ AssetVersionName : assetVersion .Name ,
236+ State : dtos .VulnStateOpen ,
237+ },
238+ CVEID : cve .CVE ,
239+ ComponentPurl : componentSingle .ID ,
240+ VulnerabilityPath : []string {"root" , "artifact:artifact-single-vuln" , componentSingle .ID },
241+ Artifacts : []models.Artifact {
242+ artifactSingle ,
243+ },
244+ }
245+ assert .NoError (t , f .DB .Create (& vulnSingle ).Error )
246+
247+ // Verify vulnerability exists before deletion
248+ var vulnBefore models.DependencyVuln
249+ assert .NoError (t , f .DB .First (& vulnBefore , "id = ?" , vulnSingle .ID ).Error )
250+
251+ // Create echo context for delete request
252+ e := echo .New ()
253+ req := httptest .NewRequest ("DELETE" , "/" , nil )
254+ rec := httptest .NewRecorder ()
255+ ctx := e .NewContext (req , rec )
256+
257+ // Set context values
258+ ctx .Set ("organization" , org )
259+ ctx .Set ("project" , project )
260+ ctx .Set ("asset" , asset )
261+ ctx .Set ("assetVersion" , assetVersion )
262+ ctx .Set ("artifact" , artifactSingle )
263+ ctx .Set ("session" , NewSessionMock ("test-user" ))
264+
265+ // Execute delete
266+ err := f .App .ArtifactController .DeleteArtifact (ctx )
267+ assert .NoError (t , err )
268+ assert .Equal (t , 200 , rec .Code )
269+
270+ // Verify artifact was deleted
271+ var deletedArtifact models.Artifact
272+ err = f .DB .First (& deletedArtifact , "artifact_name = ? AND asset_id = ?" , artifactSingle .ArtifactName , asset .ID ).Error
273+ assert .Error (t , err , "Artifact should be deleted" )
274+
275+ // Verify the dependency vulnerability was also deleted
276+ var vulnAfter models.DependencyVuln
277+ err = f .DB .First (& vulnAfter , "id = ?" , vulnSingle .ID ).Error
278+ assert .Error (t , err , "Dependency vulnerability should be deleted when its only artifact is deleted" )
279+ })
280+
281+ t .Run ("should keep dependency vuln but remove artifact relation when vuln belongs to multiple artifacts" , func (t * testing.T ) {
282+ // Create two artifacts for this test
283+ artifactMulti1 := models.Artifact {
284+ ArtifactName : "artifact-multi-1" ,
285+ AssetVersionName : assetVersion .Name ,
286+ AssetID : asset .ID ,
287+ }
288+ assert .NoError (t , f .DB .Create (& artifactMulti1 ).Error )
289+
290+ artifactMulti2 := models.Artifact {
291+ ArtifactName : "artifact-multi-2" ,
292+ AssetVersionName : assetVersion .Name ,
293+ AssetID : asset .ID ,
294+ }
295+ assert .NoError (t , f .DB .Create (& artifactMulti2 ).Error )
296+
297+ // Create a component for this test
298+ componentMulti := models.Component {
299+ ID : "pkg:npm/multi-artifact-vuln-package@2.0.0" ,
300+ }
301+ assert .NoError (t , f .DB .Create (& componentMulti ).Error )
302+
303+ // Create a vulnerability that belongs to both artifacts
304+ vulnMulti := models.DependencyVuln {
305+ Vulnerability : models.Vulnerability {
306+ AssetID : asset .ID ,
307+ AssetVersionName : assetVersion .Name ,
308+ State : dtos .VulnStateOpen ,
309+ },
310+ CVEID : cve .CVE ,
311+ ComponentPurl : componentMulti .ID ,
312+ VulnerabilityPath : []string {"root" , "artifact:artifact-multi-1" , componentMulti .ID },
313+ Artifacts : []models.Artifact {
314+ artifactMulti1 ,
315+ artifactMulti2 ,
316+ },
317+ }
318+ assert .NoError (t , f .DB .Create (& vulnMulti ).Error )
319+
320+ // Verify vulnerability exists with two artifacts before deletion
321+ var vulnBefore models.DependencyVuln
322+ assert .NoError (t , f .DB .Preload ("Artifacts" ).First (& vulnBefore , "id = ?" , vulnMulti .ID ).Error )
323+ assert .Len (t , vulnBefore .Artifacts , 2 , "Vulnerability should have 2 artifacts before deletion" )
324+
325+ // Create echo context for delete request - delete artifact 1
326+ e := echo .New ()
327+ req := httptest .NewRequest ("DELETE" , "/" , nil )
328+ rec := httptest .NewRecorder ()
329+ ctx := e .NewContext (req , rec )
330+
331+ // Set context values
332+ ctx .Set ("organization" , org )
333+ ctx .Set ("project" , project )
334+ ctx .Set ("asset" , asset )
335+ ctx .Set ("assetVersion" , assetVersion )
336+ ctx .Set ("artifact" , artifactMulti1 )
337+ ctx .Set ("session" , NewSessionMock ("test-user" ))
338+
339+ // Execute delete
340+ err := f .App .ArtifactController .DeleteArtifact (ctx )
341+ assert .NoError (t , err )
342+ assert .Equal (t , 200 , rec .Code )
343+
344+ // Verify artifact1 was deleted
345+ var deletedArtifact models.Artifact
346+ err = f .DB .First (& deletedArtifact , "artifact_name = ? AND asset_id = ?" , artifactMulti1 .ArtifactName , asset .ID ).Error
347+ assert .Error (t , err , "Artifact1 should be deleted" )
348+
349+ // Verify artifact2 still exists
350+ var remainingArtifact models.Artifact
351+ err = f .DB .First (& remainingArtifact , "artifact_name = ? AND asset_id = ?" , artifactMulti2 .ArtifactName , asset .ID ).Error
352+ assert .NoError (t , err , "Artifact2 should still exist" )
353+
354+ // Verify the dependency vulnerability still exists
355+ var vulnAfter models.DependencyVuln
356+ err = f .DB .Preload ("Artifacts" ).First (& vulnAfter , "id = ?" , vulnMulti .ID ).Error
357+ assert .NoError (t , err , "Dependency vulnerability should still exist when it has remaining artifacts" )
358+
359+ // Verify the vulnerability now only has one artifact (the remaining one)
360+ assert .Len (t , vulnAfter .Artifacts , 1 , "Vulnerability should have only 1 artifact after deletion" )
361+ assert .Equal (t , artifactMulti2 .ArtifactName , vulnAfter .Artifacts [0 ].ArtifactName , "Remaining artifact should be artifact-multi-2" )
362+ })
219363 })
220364}
0 commit comments