From 8d1acfbe4efdf6122a1c36e0c36d392244133140 Mon Sep 17 00:00:00 2001 From: Derek Ng Date: Thu, 21 Aug 2025 13:30:18 -0700 Subject: [PATCH 1/2] Release 5.3.1 --- .github/workflows/deploy-dotnet.yml | 13 ++++++++----- Directory.Build.props | 2 +- .../Resources/SharedResources.resx | 2 +- tests/Tableau.Migration.Tests/FixtureFactory.cs | 11 ++++++++--- tests/Tableau.Migration.Tests/TestLoggerFactory.cs | 12 ++++++++++-- .../Resources/ISharedResourcesLocalizerTests.cs | 2 -- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy-dotnet.yml b/.github/workflows/deploy-dotnet.yml index 6b5dfcd7..6758ff12 100644 --- a/.github/workflows/deploy-dotnet.yml +++ b/.github/workflows/deploy-dotnet.yml @@ -80,18 +80,21 @@ jobs: if ("${{ vars.NUGET_PUBLISH_USER }}" -ne "") { Write-Host "Using username and API key authentication for environment: $publishEnv" - # Remove existing source if it exists to avoid conflicts - dotnet nuget remove source $publishEnv 2>$null + # Use unique source name based on GitHub run ID + $uniqueSourceName = "$publishEnv-${{ github.run_id }}" # Add the NuGet source configuration dotnet nuget add source ${{ env.NUGET_PACKAGE_REPOSITORY }} ` - --name $publishEnv ` + --name $uniqueSourceName ` --username ${{ vars.NUGET_PUBLISH_USER }} ` --password ${{ secrets.NUGET_PUBLISH_API_KEY }} ` --store-password-in-clear-text - # Push the package using the configured source - dotnet nuget push Tableau.Migration.*.nupkg -s $publishEnv + # Push the package + dotnet nuget push Tableau.Migration.*.nupkg -s $uniqueSourceName + + # Clean up temporary source + dotnet nuget remove source $uniqueSourceName } else { Write-Host "Using API key only authentication for environment: $publishEnv" diff --git a/Directory.Build.props b/Directory.Build.props index 9ef89976..a808b66a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ enable true true - 5.3.0 + 5.3.1 Salesforce, Inc. Salesforce, Inc. Copyright (c) 2025, Salesforce, Inc. and its licensors diff --git a/src/Tableau.Migration/Resources/SharedResources.resx b/src/Tableau.Migration/Resources/SharedResources.resx index 0c273d99..b9c2afb3 100644 --- a/src/Tableau.Migration/Resources/SharedResources.resx +++ b/src/Tableau.Migration/Resources/SharedResources.resx @@ -458,7 +458,7 @@ Owner with ID {OwnerID}: {owner} Skipping migration of {ContentType} since it is disabled. - {ContentType} are disabled at the {Endpoints}. {ContentType} will not be migrated. + {ContentType} are disabled at the {Endpoints}. No items for this content type will be migrated. Embedded Managed OAuth Credentials migration is not supported. They will be converted to saved credentials for {0} {1} at {2}. The connection IDs are {3}. diff --git a/tests/Tableau.Migration.Tests/FixtureFactory.cs b/tests/Tableau.Migration.Tests/FixtureFactory.cs index b028fb51..b7eb0e1c 100644 --- a/tests/Tableau.Migration.Tests/FixtureFactory.cs +++ b/tests/Tableau.Migration.Tests/FixtureFactory.cs @@ -48,11 +48,12 @@ public static class FixtureFactory public static IFixture Create() => Customize(new Fixture()); private static IFixture Customize(IFixture fixture) - { - fixture = fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true }); - + { fixture.Customizations.Add(new ImmutableCollectionSpecimenBuilder()); fixture.Customizations.Add(new PageInfoSpecimenBuilder()); + fixture.Customizations.Add(new LoggerSpecimenBuilder()); + + fixture = fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true }); fixture.Register(() => new MockFileSystem()); @@ -331,6 +332,10 @@ string GetRandomExtractType() fixture.Register(() => new MemoryCacheOptions()); + var mockSharedLocalizer = new MockSharedResourcesLocalizer(); + fixture.Register(() => mockSharedLocalizer.Object); + fixture.Register>(() => mockSharedLocalizer); + return fixture; } diff --git a/tests/Tableau.Migration.Tests/TestLoggerFactory.cs b/tests/Tableau.Migration.Tests/TestLoggerFactory.cs index 10fa2a93..f3b3ca6e 100644 --- a/tests/Tableau.Migration.Tests/TestLoggerFactory.cs +++ b/tests/Tableau.Migration.Tests/TestLoggerFactory.cs @@ -24,12 +24,20 @@ namespace Tableau.Migration.Tests { public class TestLoggerFactory : Mock, ILoggerFactory { - private readonly ConcurrentDictionary _loggersByCategory = new(); + private readonly ConcurrentDictionary _loggersByCategory = new(); + + public TestLogger DefaultLogger => CreateTestLogger(string.Empty); + + public TestLogger CreateTestLogger() + => (TestLogger)_loggersByCategory.GetOrAdd(typeof(T).FullName ?? string.Empty, _ => new TestLogger()); + + private TestLogger CreateTestLogger(string category) + => (TestLogger)_loggersByCategory.GetOrAdd(category, _ => new TestLogger()); public TestLoggerFactory() { Setup(f => f.CreateLogger(It.IsAny())) - .Returns((string category) => _loggersByCategory.GetOrAdd(category, _ => new TestLogger())); + .Returns((string category) => CreateTestLogger(category).Object); } #region - ILoggerFactory - diff --git a/tests/Tableau.Migration.Tests/Unit/Resources/ISharedResourcesLocalizerTests.cs b/tests/Tableau.Migration.Tests/Unit/Resources/ISharedResourcesLocalizerTests.cs index e788c372..87f08571 100644 --- a/tests/Tableau.Migration.Tests/Unit/Resources/ISharedResourcesLocalizerTests.cs +++ b/tests/Tableau.Migration.Tests/Unit/Resources/ISharedResourcesLocalizerTests.cs @@ -163,6 +163,4 @@ public void SharedResourceKeys_and_SharedResources_are_in_sync() } } } - - } From 2e6711cd7437b217367d3cb2f57816f6d8cb2135 Mon Sep 17 00:00:00 2001 From: Derek Ng Date: Thu, 21 Aug 2025 13:31:02 -0700 Subject: [PATCH 2/2] add unit testing --- .../LoggerSpecimenBuilder.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/Tableau.Migration.Tests/LoggerSpecimenBuilder.cs diff --git a/tests/Tableau.Migration.Tests/LoggerSpecimenBuilder.cs b/tests/Tableau.Migration.Tests/LoggerSpecimenBuilder.cs new file mode 100644 index 00000000..e12b0f0d --- /dev/null +++ b/tests/Tableau.Migration.Tests/LoggerSpecimenBuilder.cs @@ -0,0 +1,81 @@ +// +// Copyright (c) 2025, Salesforce, Inc. +// SPDX-License-Identifier: Apache-2 +// +// Licensed 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. +// + +using System; +using System.Linq; +using AutoFixture.Kernel; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Tableau.Migration.Tests +{ + internal sealed class LoggerSpecimenBuilder : ISpecimenBuilder + { + private readonly TestLoggerFactory _loggerFactory; + + public LoggerSpecimenBuilder() + { + _loggerFactory = new(); + } + + public object Create(object request, ISpecimenContext context) + { + if (request is not Type t) + { + return new NoSpecimen(); + } + + if(t == typeof(TestLoggerFactory) || t == typeof(Mock)) + { + return _loggerFactory; + } + else if(t == typeof(ILoggerFactory)) + { + return _loggerFactory.Object; + } + else if(t == typeof(Mock)) + { + return _loggerFactory.DefaultLogger; + } + else if(t == typeof(ILogger)) + { + return _loggerFactory.DefaultLogger.Object; + } + else if(t.IsGenericType) + { + var genericType = t.GetGenericTypeDefinition(); + + if (genericType == typeof(ILogger<>)) + { + return typeof(TestLoggerFactory).GetMethod(nameof(TestLoggerFactory.CreateTestLogger)) + !.MakeGenericMethod(t.GenericTypeArguments.Single()).Invoke(_loggerFactory, null)!; + } + else if(genericType == typeof(Mock<>)) + { + var mockType = t.GenericTypeArguments.First(); + if(mockType.IsGenericType && mockType.GetGenericTypeDefinition() == typeof(ILogger<>)) + { + return typeof(TestLoggerFactory).GetMethod(nameof(TestLoggerFactory.CreateTestLogger)) + !.MakeGenericMethod(mockType.GenericTypeArguments.Single()).Invoke(_loggerFactory, null)!; + } + } + } + + return new NoSpecimen(); + } + } +}