blob: 5724b72e09f555689d6e0f57d4d4cb6b9c1c50b6 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package metadata
import (
"errors"
"testing"
)
import (
gxset "github.com/dubbogo/gost/container/set"
"github.com/dubbogo/gost/gof/observer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/metadata/info"
"dubbo.apache.org/dubbo-go/v3/metadata/mapping"
"dubbo.apache.org/dubbo-go/v3/metadata/report"
"dubbo.apache.org/dubbo-go/v3/metrics"
metricsMetadata "dubbo.apache.org/dubbo-go/v3/metrics/metadata"
)
func TestDelegateMetadataReportGetAppMetadata(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
metadataInfo := info.NewAppMetadataInfo("dubbo")
var ch = make(chan metrics.MetricsEvent, 10)
metrics.Subscribe(constant.MetricsMetadata, ch)
defer close(ch)
t.Run("normal", func(t *testing.T) {
mockReport.On("GetAppMetadata").Return(metadataInfo, nil).Once()
got, err := delegate.GetAppMetadata("dubbo", "1")
require.NoError(t, err)
assert.Equal(t, metadataInfo, got)
assert.Len(t, ch, 1)
metricEvent := <-ch
assert.Equal(t, constant.MetricsMetadata, metricEvent.Type())
event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
assert.True(t, ok)
assert.Equal(t, metricsMetadata.MetadataSub, event.Name)
assert.NotNil(t, event.Start)
assert.NotNil(t, event.End)
assert.True(t, event.Succ)
})
t.Run("error", func(t *testing.T) {
sourceErr := errors.New("mock error")
mockReport.On("GetAppMetadata").Return(info.NewAppMetadataInfo("dubbo"), sourceErr).Once()
_, err := delegate.GetAppMetadata("dubbo", "1111")
require.Error(t, err)
assert.Contains(t, err.Error(), "metadata_report failed:")
assert.Contains(t, err.Error(), "operation=get")
assert.Contains(t, err.Error(), "app=dubbo")
assert.Contains(t, err.Error(), "revision=1111")
assert.Contains(t, err.Error(), "storage_type=remote")
require.ErrorIs(t, err, sourceErr)
assert.Len(t, ch, 1)
metricEvent := <-ch
assert.Equal(t, constant.MetricsMetadata, metricEvent.Type())
event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
assert.True(t, ok)
assert.Equal(t, metricsMetadata.MetadataSub, event.Name)
assert.NotNil(t, event.Start)
assert.NotNil(t, event.End)
assert.False(t, event.Succ)
})
}
func TestDelegateMetadataReportPublishAppMetadata(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
metadataInfo := info.NewAppMetadataInfo("dubbo")
var ch = make(chan metrics.MetricsEvent, 10)
metrics.Subscribe(constant.MetricsMetadata, ch)
defer close(ch)
t.Run("normal", func(t *testing.T) {
mockReport.On("PublishAppMetadata").Return(nil).Once()
err := delegate.PublishAppMetadata("application", "revision", metadataInfo)
require.NoError(t, err)
assert.Len(t, ch, 1)
metricEvent := <-ch
assert.Equal(t, constant.MetricsMetadata, metricEvent.Type())
event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
assert.True(t, ok)
assert.Equal(t, metricsMetadata.MetadataPush, event.Name)
assert.NotNil(t, event.Start)
assert.NotNil(t, event.End)
assert.True(t, event.Succ)
})
t.Run("error", func(t *testing.T) {
sourceErr := errors.New("mock error")
mockReport.On("PublishAppMetadata").Return(sourceErr).Once()
err := delegate.PublishAppMetadata("application", "revision", metadataInfo)
require.Error(t, err)
assert.Contains(t, err.Error(), "metadata_report failed:")
assert.Contains(t, err.Error(), "operation=publish")
assert.Contains(t, err.Error(), "app=application")
assert.Contains(t, err.Error(), "revision=revision")
assert.Contains(t, err.Error(), "storage_type=remote")
require.ErrorIs(t, err, sourceErr)
assert.Len(t, ch, 1)
metricEvent := <-ch
assert.Equal(t, constant.MetricsMetadata, metricEvent.Type())
event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
assert.True(t, ok)
assert.Equal(t, metricsMetadata.MetadataPush, event.Name)
assert.NotNil(t, event.Start)
assert.NotNil(t, event.End)
assert.False(t, event.Succ)
})
}
func TestDelegateMetadataReportGetServiceAppMapping(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
t.Run("get normal", func(t *testing.T) {
mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), nil).Once()
got, err := delegate.GetServiceAppMapping("dubbo", "dev", nil)
require.NoError(t, err)
assert.True(t, got.Empty())
})
t.Run("get error", func(t *testing.T) {
mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), errors.New("mock error")).Once()
_, err := delegate.GetServiceAppMapping("dubbo", "dev", nil)
require.Error(t, err)
})
t.Run("listen normal", func(t *testing.T) {
mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), nil).Once()
got, err := delegate.GetServiceAppMapping("dubbo", "dev", &listener{})
require.NoError(t, err)
assert.True(t, got.Empty())
})
t.Run("listen error", func(t *testing.T) {
mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), errors.New("mock error")).Once()
_, err := delegate.GetServiceAppMapping("dubbo", "dev", &listener{})
require.Error(t, err)
})
}
func TestDelegateMetadataReportRegisterServiceAppMapping(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
t.Run("normal", func(t *testing.T) {
mockReport.On("RegisterServiceAppMapping").Return(nil).Once()
err := delegate.RegisterServiceAppMapping("interfaceName", "group", "application")
require.NoError(t, err)
})
t.Run("error", func(t *testing.T) {
mockReport.On("RegisterServiceAppMapping").Return(errors.New("mock error")).Once()
err := delegate.RegisterServiceAppMapping("interfaceName", "group", "application")
require.Error(t, err)
})
}
func TestDelegateMetadataReportRemoveServiceAppMappingListener(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
t.Run("normal", func(t *testing.T) {
mockReport.On("RemoveServiceAppMappingListener").Return(nil).Once()
err := delegate.RemoveServiceAppMappingListener("interfaceName", "group")
require.NoError(t, err)
})
t.Run("error", func(t *testing.T) {
mockReport.On("RemoveServiceAppMappingListener").Return(errors.New("mock error")).Once()
err := delegate.RemoveServiceAppMappingListener("interfaceName", "group")
require.Error(t, err)
})
}
func TestDelegateMetadataReportUnPublishAppMetadata(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
t.Run("normal", func(t *testing.T) {
mockReport.On("UnPublishAppMetadata").Return(nil).Once()
err := delegate.UnPublishAppMetadata("application", "revision")
require.NoError(t, err)
})
t.Run("error", func(t *testing.T) {
sourceErr := errors.New("mock error")
mockReport.On("UnPublishAppMetadata").Return(sourceErr).Once()
err := delegate.UnPublishAppMetadata("application", "revision")
require.Error(t, err)
assert.Contains(t, err.Error(), "metadata_report failed:")
assert.Contains(t, err.Error(), "operation=unpublish")
assert.Contains(t, err.Error(), "app=application")
assert.Contains(t, err.Error(), "revision=revision")
assert.Contains(t, err.Error(), "storage_type=remote")
require.ErrorIs(t, err, sourceErr)
})
}
func TestDelegateMetadataReportListAppRevisions(t *testing.T) {
mockReport := new(mockMetadataReport)
defer mockReport.AssertExpectations(t)
delegate := &DelegateMetadataReport{instance: mockReport}
t.Run("normal", func(t *testing.T) {
expected := []report.AppRevision{
{Revision: "rev1", ModifyTime: 3000},
{Revision: "rev2", ModifyTime: 1000},
}
mockReport.On("ListAppRevisions").Return(expected, nil).Once()
got, err := delegate.ListAppRevisions("application")
require.NoError(t, err)
assert.Equal(t, expected, got)
})
t.Run("error", func(t *testing.T) {
sourceErr := errors.New("mock error")
mockReport.On("ListAppRevisions").Return([]report.AppRevision(nil), sourceErr).Once()
_, err := delegate.ListAppRevisions("application")
require.Error(t, err)
assert.Contains(t, err.Error(), "metadata_report failed:")
assert.Contains(t, err.Error(), "operation=list_revisions")
assert.Contains(t, err.Error(), "app=application")
assert.Contains(t, err.Error(), "storage_type=remote")
require.ErrorIs(t, err, sourceErr)
})
}
func TestGetMetadataReport(t *testing.T) {
instances = make(map[string]report.MetadataReport)
assert.Nil(t, GetMetadataReport())
instances["default"] = new(mockMetadataReport)
assert.NotNil(t, GetMetadataReport())
}
func TestGetMetadataReportIsDeterministic(t *testing.T) {
instances = make(map[string]report.MetadataReport)
r1 := new(mockMetadataReport)
r2 := new(mockMetadataReport)
// "aaa" sorts before "zzz" alphabetically, neither is "default"
instances["zzz"] = r1
instances["aaa"] = r2
// without a "default" key, must always return the alphabetically first entry (r2)
for range 20 {
got := GetMetadataReport()
assert.Equal(t, r2, got, "expected the report for 'aaa'")
}
// when the "default" key exists, must always return it (r1), taking priority over alphabetical order
instances[constant.DefaultKey] = r1
for range 20 {
got := GetMetadataReport()
assert.Equal(t, r1, got, "expected the report for 'default'")
}
}
func TestGetMetadataReportByRegistry(t *testing.T) {
instances = make(map[string]report.MetadataReport)
// nothing registered: all paths return nil
assert.Nil(t, GetMetadataReportByRegistry(""))
assert.Nil(t, GetMetadataReportByRegistry("reg"))
defaultReport := new(mockMetadataReport)
instances["default"] = defaultReport
// exact hit
assert.Equal(t, defaultReport, GetMetadataReportByRegistry("default"))
// empty string → no registry context → falls through to GetMetadataReport() → "default"
assert.Equal(t, defaultReport, GetMetadataReportByRegistry(""))
// specific but unknown id → falls back to "default"
assert.Equal(t, defaultReport, GetMetadataReportByRegistry("reg"))
}
func TestGetMetadataReportByRegistryFallsBackDeterministically(t *testing.T) {
instances = make(map[string]report.MetadataReport)
rA := new(mockMetadataReport)
rB := new(mockMetadataReport)
instances["aaa"] = rA // lex-first
instances["zzz"] = rB
// known key → exact report
assert.Equal(t, rA, GetMetadataReportByRegistry("aaa"))
assert.Equal(t, rB, GetMetadataReportByRegistry("zzz"))
// unknown specific id → nil when no "default" is registered
assert.Nil(t, GetMetadataReportByRegistry("unknown-registry"))
// empty string → falls through to GetMetadataReport() → lex-first ("aaa" → rA)
assert.Equal(t, rA, GetMetadataReportByRegistry(""))
}
func TestGetMetadataReports(t *testing.T) {
instances = make(map[string]report.MetadataReport)
assert.Empty(t, GetMetadataReports())
instances["default"] = new(mockMetadataReport)
assert.Len(t, GetMetadataReports(), 1)
}
func TestGetMetadataType(t *testing.T) {
assert.Equal(t, constant.DefaultMetadataStorageType, GetMetadataType())
metadataOptions = &Options{}
assert.Equal(t, constant.DefaultMetadataStorageType, GetMetadataType())
metadataOptions = &Options{
metadataType: constant.RemoteMetadataStorageType,
}
assert.Equal(t, constant.RemoteMetadataStorageType, GetMetadataType())
}
func TestAddMetadataReport(t *testing.T) {
url := common.NewURLWithOptions(
common.WithProtocol("registryId"),
)
err := addMetadataReport("registryId", url)
require.NoError(t, err)
assert.Nil(t, instances["registryId"])
mockReport := new(mockMetadataReport)
extension.SetMetadataReportFactory("registryId", func() report.MetadataReportFactory {
return mockReport
})
err = addMetadataReport("registryId", url)
require.NoError(t, err)
assert.NotNil(t, instances["registryId"])
}
type mockMetadataReport struct {
mock.Mock
}
func (m *mockMetadataReport) CreateMetadataReport(*common.URL) report.MetadataReport {
return m
}
func (m *mockMetadataReport) GetAppMetadata(string, string) (*info.MetadataInfo, error) {
args := m.Called()
return args.Get(0).(*info.MetadataInfo), args.Error(1)
}
func (m *mockMetadataReport) PublishAppMetadata(string, string, *info.MetadataInfo) error {
args := m.Called()
return args.Error(0)
}
func (m *mockMetadataReport) RegisterServiceAppMapping(string, string, string) error {
args := m.Called()
return args.Error(0)
}
func (m *mockMetadataReport) GetServiceAppMapping(string, string, mapping.MappingListener) (*gxset.HashSet, error) {
args := m.Called()
return args.Get(0).(*gxset.HashSet), args.Error(1)
}
func (m *mockMetadataReport) RemoveServiceAppMappingListener(string, string) error {
args := m.Called()
return args.Error(0)
}
func (m *mockMetadataReport) UnPublishAppMetadata(string, string) error {
args := m.Called()
return args.Error(0)
}
func (m *mockMetadataReport) ListAppRevisions(string) ([]report.AppRevision, error) {
args := m.Called()
return args.Get(0).([]report.AppRevision), args.Error(1)
}
func (m *mockMetadataReport) URL() *common.URL {
u, _ := common.NewURL("mock://127.0.0.1:8848")
return u
}
type listener struct {
}
func (l *listener) OnEvent(e observer.Event) error {
return nil
}
func (l *listener) Stop() {
}