11//
2- // Copyright 2023 The Chainloop Authors.
2+ // Copyright 2023-2026 The Chainloop Authors.
33//
44// Licensed under the Apache License, Version 2.0 (the "License");
55// you may not use this file except in compliance with the License.
@@ -21,12 +21,12 @@ import (
2121 "testing"
2222 "time"
2323
24+ api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
2425 "github.com/go-git/go-git/v5"
26+ "github.com/go-git/go-git/v5/config"
2527 "github.com/go-git/go-git/v5/plumbing/object"
2628 "github.com/stretchr/testify/assert"
2729 "github.com/stretchr/testify/require"
28-
29- "github.com/go-git/go-git/v5/config"
3030 "github.com/stretchr/testify/suite"
3131)
3232
@@ -214,6 +214,106 @@ func (s *crafterUnitSuite) TestGitRepoHead() {
214214 }
215215}
216216
217+ func (s * crafterUnitSuite ) TestPolicyEvaluationDedup () {
218+ // Simulate the protojson round-trip issue:
219+ // - Init phase sets With = map[string]string{} (empty map)
220+ // - protojson.Marshal omits empty maps
221+ // - protojson.Unmarshal sets With = nil (absent field)
222+ // - Push phase produces With = map[string]string{} again
223+ // The dedup comparison must treat nil and empty map as equal.
224+
225+ policyRef := & api.PolicyEvaluation_Reference {
226+ Name : "source-commit" ,
227+ Digest : "sha256:abc123" ,
228+ }
229+
230+ testCases := []struct {
231+ name string
232+ existing []* api.PolicyEvaluation
233+ newEvals []* api.PolicyEvaluation
234+ wantCount int
235+ description string
236+ }{
237+ {
238+ name : "nil vs empty map With are deduplicated" ,
239+ existing : []* api.PolicyEvaluation {
240+ {Name : "source-commit" , PolicyReference : policyRef , With : nil },
241+ },
242+ newEvals : []* api.PolicyEvaluation {
243+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {}},
244+ },
245+ wantCount : 1 ,
246+ description : "after protojson round-trip, nil With should match empty map With" ,
247+ },
248+ {
249+ name : "empty map vs empty map With are deduplicated" ,
250+ existing : []* api.PolicyEvaluation {
251+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {}},
252+ },
253+ newEvals : []* api.PolicyEvaluation {
254+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {}},
255+ },
256+ wantCount : 1 ,
257+ description : "identical empty maps should deduplicate" ,
258+ },
259+ {
260+ name : "nil vs nil With are deduplicated" ,
261+ existing : []* api.PolicyEvaluation {
262+ {Name : "source-commit" , PolicyReference : policyRef , With : nil },
263+ },
264+ newEvals : []* api.PolicyEvaluation {
265+ {Name : "source-commit" , PolicyReference : policyRef , With : nil },
266+ },
267+ wantCount : 1 ,
268+ description : "both nil should deduplicate" ,
269+ },
270+ {
271+ name : "different With args are not deduplicated" ,
272+ existing : []* api.PolicyEvaluation {
273+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {"key" : "val1" }},
274+ },
275+ newEvals : []* api.PolicyEvaluation {
276+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {"key" : "val2" }},
277+ },
278+ wantCount : 2 ,
279+ description : "different With values should not deduplicate" ,
280+ },
281+ {
282+ name : "different policy references are not deduplicated" ,
283+ existing : []* api.PolicyEvaluation {
284+ {Name : "policy-a" , PolicyReference : & api.PolicyEvaluation_Reference {Name : "policy-a" }, With : nil },
285+ },
286+ newEvals : []* api.PolicyEvaluation {
287+ {Name : "policy-b" , PolicyReference : & api.PolicyEvaluation_Reference {Name : "policy-b" }, With : nil },
288+ },
289+ wantCount : 2 ,
290+ description : "different policies should both be kept" ,
291+ },
292+ }
293+
294+ for _ , tc := range testCases {
295+ s .Run (tc .name , func () {
296+ all := append (tc .existing , tc .newEvals ... )
297+
298+ var filtered []* api.PolicyEvaluation
299+ for _ , ev := range all {
300+ var duplicated bool
301+ for _ , existing := range filtered {
302+ if policyEvalMatches (existing , ev ) {
303+ duplicated = true
304+ break
305+ }
306+ }
307+ if ! duplicated {
308+ filtered = append (filtered , ev )
309+ }
310+ }
311+
312+ s .Len (filtered , tc .wantCount , tc .description )
313+ })
314+ }
315+ }
316+
217317func TestSuite (t * testing.T ) {
218318 suite .Run (t , new (crafterUnitSuite ))
219319}
0 commit comments