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.
@@ -18,15 +18,16 @@ package crafter
1818import (
1919 "os"
2020 "path/filepath"
21+ "slices"
2122 "testing"
2223 "time"
2324
25+ api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
2426 "github.com/go-git/go-git/v5"
27+ "github.com/go-git/go-git/v5/config"
2528 "github.com/go-git/go-git/v5/plumbing/object"
2629 "github.com/stretchr/testify/assert"
2730 "github.com/stretchr/testify/require"
28-
29- "github.com/go-git/go-git/v5/config"
3031 "github.com/stretchr/testify/suite"
3132)
3233
@@ -214,6 +215,106 @@ func (s *crafterUnitSuite) TestGitRepoHead() {
214215 }
215216}
216217
218+ func (s * crafterUnitSuite ) TestPolicyEvaluationDedup () {
219+ // Simulate the protojson round-trip issue:
220+ // - Init phase sets With = map[string]string{} (empty map)
221+ // - protojson.Marshal omits empty maps
222+ // - protojson.Unmarshal sets With = nil (absent field)
223+ // - Push phase produces With = map[string]string{} again
224+ // The dedup comparison must treat nil and empty map as equal.
225+
226+ policyRef := & api.PolicyEvaluation_Reference {
227+ Name : "source-commit" ,
228+ Digest : "sha256:abc123" ,
229+ }
230+
231+ testCases := []struct {
232+ name string
233+ existing []* api.PolicyEvaluation
234+ newEvals []* api.PolicyEvaluation
235+ wantCount int
236+ description string
237+ }{
238+ {
239+ name : "nil vs empty map With are deduplicated" ,
240+ existing : []* api.PolicyEvaluation {
241+ {Name : "source-commit" , PolicyReference : policyRef , With : nil },
242+ },
243+ newEvals : []* api.PolicyEvaluation {
244+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {}},
245+ },
246+ wantCount : 1 ,
247+ description : "after protojson round-trip, nil With should match empty map With" ,
248+ },
249+ {
250+ name : "empty map vs empty map With are deduplicated" ,
251+ existing : []* api.PolicyEvaluation {
252+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {}},
253+ },
254+ newEvals : []* api.PolicyEvaluation {
255+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {}},
256+ },
257+ wantCount : 1 ,
258+ description : "identical empty maps should deduplicate" ,
259+ },
260+ {
261+ name : "nil vs nil With are deduplicated" ,
262+ existing : []* api.PolicyEvaluation {
263+ {Name : "source-commit" , PolicyReference : policyRef , With : nil },
264+ },
265+ newEvals : []* api.PolicyEvaluation {
266+ {Name : "source-commit" , PolicyReference : policyRef , With : nil },
267+ },
268+ wantCount : 1 ,
269+ description : "both nil should deduplicate" ,
270+ },
271+ {
272+ name : "different With args are not deduplicated" ,
273+ existing : []* api.PolicyEvaluation {
274+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {"key" : "val1" }},
275+ },
276+ newEvals : []* api.PolicyEvaluation {
277+ {Name : "source-commit" , PolicyReference : policyRef , With : map [string ]string {"key" : "val2" }},
278+ },
279+ wantCount : 2 ,
280+ description : "different With values should not deduplicate" ,
281+ },
282+ {
283+ name : "different policy references are not deduplicated" ,
284+ existing : []* api.PolicyEvaluation {
285+ {Name : "policy-a" , PolicyReference : & api.PolicyEvaluation_Reference {Name : "policy-a" }, With : nil },
286+ },
287+ newEvals : []* api.PolicyEvaluation {
288+ {Name : "policy-b" , PolicyReference : & api.PolicyEvaluation_Reference {Name : "policy-b" }, With : nil },
289+ },
290+ wantCount : 2 ,
291+ description : "different policies should both be kept" ,
292+ },
293+ }
294+
295+ for _ , tc := range testCases {
296+ s .Run (tc .name , func () {
297+ all := slices .Concat (tc .existing , tc .newEvals )
298+
299+ var filtered []* api.PolicyEvaluation
300+ for _ , ev := range all {
301+ var duplicated bool
302+ for _ , existing := range filtered {
303+ if policyEvalMatches (existing , ev ) {
304+ duplicated = true
305+ break
306+ }
307+ }
308+ if ! duplicated {
309+ filtered = append (filtered , ev )
310+ }
311+ }
312+
313+ s .Len (filtered , tc .wantCount , tc .description )
314+ })
315+ }
316+ }
317+
217318func TestSuite (t * testing.T ) {
218319 suite .Run (t , new (crafterUnitSuite ))
219320}
0 commit comments