This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch feat/no-duplicates in repository https://gitbox.apache.org/repos/asf/skywalking-infra-e2e.git
commit e2c67fb9cf7d9af2a031d6332d86ec767b13dfdf Author: Wu Sheng <[email protected]> AuthorDate: Tue Apr 14 06:45:25 2026 +0800 feat: add noDuplicates pipe function for duplicate data verification Add a noDuplicates template pipe function that checks a list for duplicate entries. Works with contains, containsOnce, and range. Resolves apache/skywalking#12253 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --- docs/en/setup/Configuration-File.md | 13 +++++ internal/components/verifier/funcs.go | 18 +++++++ internal/components/verifier/verifier_test.go | 77 +++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/docs/en/setup/Configuration-File.md b/docs/en/setup/Configuration-File.md index fd4c77a..fa5104d 100644 --- a/docs/en/setup/Configuration-File.md +++ b/docs/en/setup/Configuration-File.md @@ -248,6 +248,19 @@ For example, if a generic expected entry (e.g., `notEmpty`) appears before a spe {{- end }} ``` +##### Duplicate Detection + +`noDuplicates` is a pipe function that verifies a list contains no duplicate items. Two items are considered duplicates when all their fields are equal. It can be combined with `contains`, `containsOnce`, or `range`. + +```yaml +{{- contains (.metrics | noDuplicates) }} +- name: {{ notEmpty .name }} + value: {{ notEmpty .value }} +{{- end }} +``` + +When duplicates exist in the actual data, verification will fail with an error indicating the duplicated item. + ##### Encoding In order to make the program easier for users to read and use, some code conversions are provided. diff --git a/internal/components/verifier/funcs.go b/internal/components/verifier/funcs.go index f0ca3d4..caffc0a 100644 --- a/internal/components/verifier/funcs.go +++ b/internal/components/verifier/funcs.go @@ -54,6 +54,9 @@ var customFuncMap = map[string]any{ // Calculation: "subtractor": subtractor, + + // List: + "noDuplicates": noDuplicates, } func base64encode(s string) string { @@ -103,3 +106,18 @@ func subtractor(total int, nums ...int) int { } return total } + +func noDuplicates(list any) any { + items, ok := list.([]any) + if !ok { + return list + } + for i := 0; i < len(items); i++ { + for j := i + 1; j < len(items); j++ { + if fmt.Sprint(items[i]) == fmt.Sprint(items[j]) { + return fmt.Sprintf("<duplicate found: %v>", items[i]) + } + } + } + return list +} diff --git a/internal/components/verifier/verifier_test.go b/internal/components/verifier/verifier_test.go index dfa5634..01aa3b5 100644 --- a/internal/components/verifier/verifier_test.go +++ b/internal/components/verifier/verifier_test.go @@ -501,6 +501,83 @@ metrics: }, wantErr: true, // service-A does not exist in actual }, + { + name: "noDuplicates should pass when list has no duplicates", + args: args{ + actualData: ` +metrics: + - name: service-A + value: "100" + - name: service-B + value: "200" +`, + expectedTemplate: ` +metrics: +{{- contains (.metrics | noDuplicates) }} + - name: {{ notEmpty .name }} + value: {{ notEmpty .value }} +{{- end }} +`, + }, + wantErr: false, + }, + { + name: "noDuplicates should fail when list has duplicate items", + args: args{ + actualData: ` +metrics: + - name: service-A + value: "100" + - name: service-A + value: "100" +`, + expectedTemplate: ` +metrics: +{{- contains (.metrics | noDuplicates) }} + - name: {{ notEmpty .name }} + value: {{ notEmpty .value }} +{{- end }} +`, + }, + wantErr: true, // duplicate entry exists + }, + { + name: "noDuplicates should work with containsOnce", + args: args{ + actualData: ` +- name: service-A + value: "100" +- name: service-B + value: "200" +- name: service-A + value: "100" +`, + expectedTemplate: ` +{{- containsOnce (. | noDuplicates) }} +- name: {{ notEmpty .name }} + value: {{ notEmpty .value }} +{{- end }} +`, + }, + wantErr: true, // duplicate entry exists even though containsOnce would pass + }, + { + name: "noDuplicates should pass with range and no duplicates", + args: args{ + actualData: ` +metrics: + - name: service-A + - name: service-B +`, + expectedTemplate: ` +metrics: +{{- range (.metrics | noDuplicates) }} + - name: {{ notEmpty .name }} +{{- end }} +`, + }, + wantErr: false, + }, { name: "notEmpty with nil", args: args{
