From c560858adb37a68a587f8d9f55271d539122ac7c Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Jan 2025 07:17:20 +0100 Subject: [PATCH 1/5] metadata_test Signed-off-by: Angelo De Caro --- token/core/common/meta/metadata_test.go | 74 +++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 token/core/common/meta/metadata_test.go diff --git a/token/core/common/meta/metadata_test.go b/token/core/common/meta/metadata_test.go new file mode 100644 index 000000000..45ca3db38 --- /dev/null +++ b/token/core/common/meta/metadata_test.go @@ -0,0 +1,74 @@ +package meta + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTransferActionMetadata(t *testing.T) { + tests := []struct { + name string + attrs map[interface{}]interface{} + expected map[string][]byte + }{ + { + name: "Basic metadata extraction", + attrs: map[interface{}]interface{}{ + "TransferMetadataPrefixKey1": []byte("value1"), + "TransferMetadataPrefixKey2": []byte("value2"), + }, + expected: map[string][]byte{ + "Key1": []byte("value1"), + "Key2": []byte("value2"), + }, + }, + { + name: "Empty attrs", + attrs: map[interface{}]interface{}{}, + expected: map[string][]byte{}, + }, + { + name: "Non-string keys", + attrs: map[interface{}]interface{}{ + 123: []byte("value"), + }, + expected: map[string][]byte{}, + }, + { + name: "Invalid value types", + attrs: map[interface{}]interface{}{ + "TransferMetadataPrefixKey": 123, + }, + expected: map[string][]byte{}, + }, + { + name: "Exact prefix match", + attrs: map[interface{}]interface{}{ + "TransferMetadataPrefix": []byte("value"), + }, + expected: map[string][]byte{ + "": []byte("value"), + }, + }, + { + name: "No prefix match", + attrs: map[interface{}]interface{}{ + "WrongPrefixKey": []byte("value"), + }, + expected: map[string][]byte{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := TransferActionMetadata(tt.attrs) + assert.Equal(t, len(result), len(tt.expected), "Expected %d items, got %d", len(tt.expected), len(result)) + for key, value := range tt.expected { + gotValue, ok := result[key] + assert.True(t, ok, "Expected key %s to exist in result", key) + assert.Equal(t, value, gotValue, "Expected value for %s: %v, got: %v", key, value, gotValue) + } + }) + } +} From 736cda961b07ec5d62c3ef7ec27535cbb3223614 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Jan 2025 07:24:10 +0100 Subject: [PATCH 2/5] add licence and remove an unused fiedl Signed-off-by: Angelo De Caro --- token/core/common/meta/metadata_test.go | 6 ++++++ token/core/common/metrics/metrics.go | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/token/core/common/meta/metadata_test.go b/token/core/common/meta/metadata_test.go index 45ca3db38..16d5b9a34 100644 --- a/token/core/common/meta/metadata_test.go +++ b/token/core/common/meta/metadata_test.go @@ -1,3 +1,9 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + package meta import ( diff --git a/token/core/common/metrics/metrics.go b/token/core/common/metrics/metrics.go index e7aa964bd..ad8da3cad 100644 --- a/token/core/common/metrics/metrics.go +++ b/token/core/common/metrics/metrics.go @@ -20,5 +20,3 @@ type ( Provider = metrics.Provider MetricLabel = string ) - -var GetProvider = metrics.GetProvider From 6be827dcbf579ff32378a12c2fcf5a76e0681711 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Jan 2025 08:52:14 +0100 Subject: [PATCH 3/5] metrics Signed-off-by: Angelo De Caro --- token/core/common/metrics/metrics.go | 2 + token/core/common/metrics/mock/provider.go | 260 +++++++++++++++++++ token/core/common/metrics/provider.go | 1 + token/core/common/metrics/provider_test.go | 277 +++++++++++++++++++++ 4 files changed, 540 insertions(+) create mode 100644 token/core/common/metrics/mock/provider.go create mode 100644 token/core/common/metrics/provider_test.go diff --git a/token/core/common/metrics/metrics.go b/token/core/common/metrics/metrics.go index ad8da3cad..bc5319641 100644 --- a/token/core/common/metrics/metrics.go +++ b/token/core/common/metrics/metrics.go @@ -10,6 +10,8 @@ import ( "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/metrics" ) +//go:generate counterfeiter -o mock/provider.go -fake-name Provider . Provider + type ( CounterOpts = metrics.CounterOpts Counter = metrics.Counter diff --git a/token/core/common/metrics/mock/provider.go b/token/core/common/metrics/mock/provider.go new file mode 100644 index 000000000..1c018f87a --- /dev/null +++ b/token/core/common/metrics/mock/provider.go @@ -0,0 +1,260 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package mock + +import ( + "sync" + + metricsa "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/metrics" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common/metrics" +) + +type Provider struct { + NewCounterStub func(metricsa.CounterOpts) metricsa.Counter + newCounterMutex sync.RWMutex + newCounterArgsForCall []struct { + arg1 metricsa.CounterOpts + } + newCounterReturns struct { + result1 metricsa.Counter + } + newCounterReturnsOnCall map[int]struct { + result1 metricsa.Counter + } + NewGaugeStub func(metricsa.GaugeOpts) metricsa.Gauge + newGaugeMutex sync.RWMutex + newGaugeArgsForCall []struct { + arg1 metricsa.GaugeOpts + } + newGaugeReturns struct { + result1 metricsa.Gauge + } + newGaugeReturnsOnCall map[int]struct { + result1 metricsa.Gauge + } + NewHistogramStub func(metricsa.HistogramOpts) metricsa.Histogram + newHistogramMutex sync.RWMutex + newHistogramArgsForCall []struct { + arg1 metricsa.HistogramOpts + } + newHistogramReturns struct { + result1 metricsa.Histogram + } + newHistogramReturnsOnCall map[int]struct { + result1 metricsa.Histogram + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *Provider) NewCounter(arg1 metricsa.CounterOpts) metricsa.Counter { + fake.newCounterMutex.Lock() + ret, specificReturn := fake.newCounterReturnsOnCall[len(fake.newCounterArgsForCall)] + fake.newCounterArgsForCall = append(fake.newCounterArgsForCall, struct { + arg1 metricsa.CounterOpts + }{arg1}) + stub := fake.NewCounterStub + fakeReturns := fake.newCounterReturns + fake.recordInvocation("NewCounter", []interface{}{arg1}) + fake.newCounterMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *Provider) NewCounterCallCount() int { + fake.newCounterMutex.RLock() + defer fake.newCounterMutex.RUnlock() + return len(fake.newCounterArgsForCall) +} + +func (fake *Provider) NewCounterCalls(stub func(metricsa.CounterOpts) metricsa.Counter) { + fake.newCounterMutex.Lock() + defer fake.newCounterMutex.Unlock() + fake.NewCounterStub = stub +} + +func (fake *Provider) NewCounterArgsForCall(i int) metricsa.CounterOpts { + fake.newCounterMutex.RLock() + defer fake.newCounterMutex.RUnlock() + argsForCall := fake.newCounterArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Provider) NewCounterReturns(result1 metricsa.Counter) { + fake.newCounterMutex.Lock() + defer fake.newCounterMutex.Unlock() + fake.NewCounterStub = nil + fake.newCounterReturns = struct { + result1 metricsa.Counter + }{result1} +} + +func (fake *Provider) NewCounterReturnsOnCall(i int, result1 metricsa.Counter) { + fake.newCounterMutex.Lock() + defer fake.newCounterMutex.Unlock() + fake.NewCounterStub = nil + if fake.newCounterReturnsOnCall == nil { + fake.newCounterReturnsOnCall = make(map[int]struct { + result1 metricsa.Counter + }) + } + fake.newCounterReturnsOnCall[i] = struct { + result1 metricsa.Counter + }{result1} +} + +func (fake *Provider) NewGauge(arg1 metricsa.GaugeOpts) metricsa.Gauge { + fake.newGaugeMutex.Lock() + ret, specificReturn := fake.newGaugeReturnsOnCall[len(fake.newGaugeArgsForCall)] + fake.newGaugeArgsForCall = append(fake.newGaugeArgsForCall, struct { + arg1 metricsa.GaugeOpts + }{arg1}) + stub := fake.NewGaugeStub + fakeReturns := fake.newGaugeReturns + fake.recordInvocation("NewGauge", []interface{}{arg1}) + fake.newGaugeMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *Provider) NewGaugeCallCount() int { + fake.newGaugeMutex.RLock() + defer fake.newGaugeMutex.RUnlock() + return len(fake.newGaugeArgsForCall) +} + +func (fake *Provider) NewGaugeCalls(stub func(metricsa.GaugeOpts) metricsa.Gauge) { + fake.newGaugeMutex.Lock() + defer fake.newGaugeMutex.Unlock() + fake.NewGaugeStub = stub +} + +func (fake *Provider) NewGaugeArgsForCall(i int) metricsa.GaugeOpts { + fake.newGaugeMutex.RLock() + defer fake.newGaugeMutex.RUnlock() + argsForCall := fake.newGaugeArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Provider) NewGaugeReturns(result1 metricsa.Gauge) { + fake.newGaugeMutex.Lock() + defer fake.newGaugeMutex.Unlock() + fake.NewGaugeStub = nil + fake.newGaugeReturns = struct { + result1 metricsa.Gauge + }{result1} +} + +func (fake *Provider) NewGaugeReturnsOnCall(i int, result1 metricsa.Gauge) { + fake.newGaugeMutex.Lock() + defer fake.newGaugeMutex.Unlock() + fake.NewGaugeStub = nil + if fake.newGaugeReturnsOnCall == nil { + fake.newGaugeReturnsOnCall = make(map[int]struct { + result1 metricsa.Gauge + }) + } + fake.newGaugeReturnsOnCall[i] = struct { + result1 metricsa.Gauge + }{result1} +} + +func (fake *Provider) NewHistogram(arg1 metricsa.HistogramOpts) metricsa.Histogram { + fake.newHistogramMutex.Lock() + ret, specificReturn := fake.newHistogramReturnsOnCall[len(fake.newHistogramArgsForCall)] + fake.newHistogramArgsForCall = append(fake.newHistogramArgsForCall, struct { + arg1 metricsa.HistogramOpts + }{arg1}) + stub := fake.NewHistogramStub + fakeReturns := fake.newHistogramReturns + fake.recordInvocation("NewHistogram", []interface{}{arg1}) + fake.newHistogramMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *Provider) NewHistogramCallCount() int { + fake.newHistogramMutex.RLock() + defer fake.newHistogramMutex.RUnlock() + return len(fake.newHistogramArgsForCall) +} + +func (fake *Provider) NewHistogramCalls(stub func(metricsa.HistogramOpts) metricsa.Histogram) { + fake.newHistogramMutex.Lock() + defer fake.newHistogramMutex.Unlock() + fake.NewHistogramStub = stub +} + +func (fake *Provider) NewHistogramArgsForCall(i int) metricsa.HistogramOpts { + fake.newHistogramMutex.RLock() + defer fake.newHistogramMutex.RUnlock() + argsForCall := fake.newHistogramArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *Provider) NewHistogramReturns(result1 metricsa.Histogram) { + fake.newHistogramMutex.Lock() + defer fake.newHistogramMutex.Unlock() + fake.NewHistogramStub = nil + fake.newHistogramReturns = struct { + result1 metricsa.Histogram + }{result1} +} + +func (fake *Provider) NewHistogramReturnsOnCall(i int, result1 metricsa.Histogram) { + fake.newHistogramMutex.Lock() + defer fake.newHistogramMutex.Unlock() + fake.NewHistogramStub = nil + if fake.newHistogramReturnsOnCall == nil { + fake.newHistogramReturnsOnCall = make(map[int]struct { + result1 metricsa.Histogram + }) + } + fake.newHistogramReturnsOnCall[i] = struct { + result1 metricsa.Histogram + }{result1} +} + +func (fake *Provider) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.newCounterMutex.RLock() + defer fake.newCounterMutex.RUnlock() + fake.newGaugeMutex.RLock() + defer fake.newGaugeMutex.RUnlock() + fake.newHistogramMutex.RLock() + defer fake.newHistogramMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *Provider) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ metrics.Provider = new(Provider) diff --git a/token/core/common/metrics/provider.go b/token/core/common/metrics/provider.go index 702e9ecaf..c3d448f9d 100644 --- a/token/core/common/metrics/provider.go +++ b/token/core/common/metrics/provider.go @@ -71,6 +71,7 @@ func recoverFromDuplicate(recovered any) { func AllLabelNames(extraLabels ...MetricLabel) []MetricLabel { return append([]string{NetworkLabel, ChannelLabel, NamespaceLabel}, extraLabels...) } + func StatsdFormat(extraLabels ...MetricLabel) string { return "%{#fqname}.%{" + strings.Join(AllLabelNames(extraLabels...), "}.%{") + "}" } diff --git a/token/core/common/metrics/provider_test.go b/token/core/common/metrics/provider_test.go new file mode 100644 index 000000000..adec14a2e --- /dev/null +++ b/token/core/common/metrics/provider_test.go @@ -0,0 +1,277 @@ +package metrics_test + +import ( + "testing" + + "github.com/hyperledger-labs/fabric-token-sdk/token" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common/metrics" + "github.com/hyperledger-labs/fabric-token-sdk/token/core/common/metrics/mock" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/assert" +) + +func TestTMSProvider_NewCounter(t *testing.T) { + tmsID := token.TMSID{ + Network: "network", + Channel: "channel", + Namespace: "namespace", + } + tmsLabels := []string{ + metrics.NetworkLabel, tmsID.Network, + metrics.ChannelLabel, tmsID.Channel, + metrics.NamespaceLabel, tmsID.Namespace, + } + tests := []struct { + name string + setup func(*mock.Provider) + wantPanic bool + }{ + { + name: "successful counter creation", + setup: func(mp *mock.Provider) { + mp.NewCounterStub = func(opts metrics.CounterOpts) metrics.Counter { + return &mockCounter{} + } + }, + wantPanic: false, + }, + { + name: "counter creation with error", + setup: func(mp *mock.Provider) { + mp.NewCounterStub = func(opts metrics.CounterOpts) metrics.Counter { + panic(prometheus.AlreadyRegisteredError{}) + } + }, + wantPanic: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mp := &mock.Provider{} + if tt.setup != nil { + tt.setup(mp) + } + p := metrics.NewTMSProvider(tmsID, mp) + if tt.wantPanic { + assert.Panics(t, func() { + counter := p.NewCounter(metrics.CounterOpts{}) + assert.Equal(t, tmsLabels, counter.(*mockCounter).labelValues) + }) + } else { + assert.NotPanics(t, func() { p.NewCounter(metrics.CounterOpts{}) }) + } + }) + } +} + +func TestTMSProvider_NewGauge(t *testing.T) { + tmsID := token.TMSID{ + Network: "network", + Channel: "channel", + Namespace: "namespace", + } + tmsLabels := []string{ + metrics.NetworkLabel, tmsID.Network, + metrics.ChannelLabel, tmsID.Channel, + metrics.NamespaceLabel, tmsID.Namespace, + } + tests := []struct { + name string + setup func(*mock.Provider) + wantPanic bool + }{ + { + name: "successful gauge creation", + setup: func(mp *mock.Provider) { + mp.NewGaugeStub = func(opts metrics.GaugeOpts) metrics.Gauge { + return &mockGauge{} + } + }, + wantPanic: false, + }, + { + name: "gauge creation with error", + setup: func(mp *mock.Provider) { + mp.NewGaugeStub = func(opts metrics.GaugeOpts) metrics.Gauge { + panic(prometheus.AlreadyRegisteredError{}) + } + }, + wantPanic: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mp := &mock.Provider{} + if tt.setup != nil { + tt.setup(mp) + } + p := metrics.NewTMSProvider(tmsID, mp) + if tt.wantPanic { + assert.Panics(t, func() { + gauge := p.NewGauge(metrics.GaugeOpts{}) + assert.Equal(t, tmsLabels, gauge.(*mockGauge).labelValues) + }) + } else { + assert.NotPanics(t, func() { p.NewGauge(metrics.GaugeOpts{}) }) + } + }) + } +} + +func TestTMSProvider_NewHistogram(t *testing.T) { + tmsID := token.TMSID{ + Network: "network", + Channel: "channel", + Namespace: "namespace", + } + tmsLabels := []string{ + metrics.NetworkLabel, tmsID.Network, + metrics.ChannelLabel, tmsID.Channel, + metrics.NamespaceLabel, tmsID.Namespace, + } + tests := []struct { + name string + setup func(*mock.Provider) + wantPanic bool + }{ + { + name: "successful histogram creation", + setup: func(mp *mock.Provider) { + mp.NewHistogramStub = func(opts metrics.HistogramOpts) metrics.Histogram { + return &mockHistogram{} + } + }, + wantPanic: false, + }, + { + name: "histogram creation with error", + setup: func(mp *mock.Provider) { + mp.NewHistogramStub = func(opts metrics.HistogramOpts) metrics.Histogram { + panic(prometheus.AlreadyRegisteredError{}) + } + }, + wantPanic: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mp := &mock.Provider{} + if tt.setup != nil { + tt.setup(mp) + } + p := metrics.NewTMSProvider(tmsID, mp) + if tt.wantPanic { + assert.Panics(t, func() { + histogram := p.NewHistogram(metrics.HistogramOpts{}) + assert.Equal(t, tmsLabels, histogram.(*mockHistogram).labelValues) + }) + } else { + assert.NotPanics(t, func() { p.NewHistogram(metrics.HistogramOpts{}) }) + } + }) + } +} + +func TestAllLabelNames(t *testing.T) { + tests := []struct { + name string + extras []string + want []string + }{ + { + name: "no extras", + extras: nil, + want: []string{"network", "channel", "namespace"}, + }, + { + name: "with extras", + extras: []string{"extra1", "extra2"}, + want: []string{"network", "channel", "namespace", "extra1", "extra2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := metrics.AllLabelNames(tt.extras...) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestStatsdFormat(t *testing.T) { + tests := []struct { + name string + labels []string + values []string + want string + }{ + { + name: "empty labels", + labels: nil, + want: "%{#fqname}.%{network}.%{channel}.%{namespace}", + }, + { + name: "single label", + labels: []string{"label1"}, + want: "%{#fqname}.%{network}.%{channel}.%{namespace}.%{label1}", + }, + { + name: "multiple labels", + labels: []string{"l1", "l2"}, + want: "%{#fqname}.%{network}.%{channel}.%{namespace}.%{l1}.%{l2}", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := metrics.StatsdFormat(tt.labels...) + assert.Equal(t, tt.want, got) + }) + } +} + +type mockCounter struct { + labelValues []string +} + +func (m *mockCounter) With(labelValues ...string) metrics.Counter { + m.labelValues = labelValues + return m +} + +func (m *mockCounter) Add(delta float64) { + return +} + +type mockGauge struct { + labelValues []string +} + +func (m *mockGauge) Set(value float64) { + return +} + +func (m *mockGauge) With(labelValues ...string) metrics.Gauge { + m.labelValues = labelValues + return m +} + +func (m *mockGauge) Add(delta float64) { + return +} + +type mockHistogram struct { + labelValues []string +} + +func (m *mockHistogram) With(labelValues ...string) metrics.Histogram { + m.labelValues = labelValues + return m +} + +func (m *mockHistogram) Observe(value float64) { + return +} From 15237355b530b762c8335c9b399666c6ffce2273 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Jan 2025 08:55:22 +0100 Subject: [PATCH 4/5] missing licence Signed-off-by: Angelo De Caro --- token/core/common/metrics/provider_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/token/core/common/metrics/provider_test.go b/token/core/common/metrics/provider_test.go index adec14a2e..d22469aa1 100644 --- a/token/core/common/metrics/provider_test.go +++ b/token/core/common/metrics/provider_test.go @@ -1,3 +1,9 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + package metrics_test import ( From 08a3f654b706da8dfcb6f7b670e36b5034f2d40a Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 29 Jan 2025 09:05:08 +0100 Subject: [PATCH 5/5] linting fix Signed-off-by: Angelo De Caro --- token/core/common/metrics/provider_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/token/core/common/metrics/provider_test.go b/token/core/common/metrics/provider_test.go index d22469aa1..b0c735289 100644 --- a/token/core/common/metrics/provider_test.go +++ b/token/core/common/metrics/provider_test.go @@ -249,7 +249,6 @@ func (m *mockCounter) With(labelValues ...string) metrics.Counter { } func (m *mockCounter) Add(delta float64) { - return } type mockGauge struct { @@ -257,7 +256,6 @@ type mockGauge struct { } func (m *mockGauge) Set(value float64) { - return } func (m *mockGauge) With(labelValues ...string) metrics.Gauge { @@ -266,7 +264,6 @@ func (m *mockGauge) With(labelValues ...string) metrics.Gauge { } func (m *mockGauge) Add(delta float64) { - return } type mockHistogram struct { @@ -279,5 +276,4 @@ func (m *mockHistogram) With(labelValues ...string) metrics.Histogram { } func (m *mockHistogram) Observe(value float64) { - return }