diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b31db58..547edd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,57 +3,122 @@ name: Continuous Integration on: push: branches: - - develop + - main paths-ignore: - - 'README.md' - - 'docs/**' - - '.github/workflows/docs.yml' - - '.github/workflows/release.yml' + - "**.md" + - "renovate.json" + - ".github/ISSUE_TEMPLATE/**" + - ".github/PULL_REQUEST_TEMPLATE/**" + - ".github/workflows/docs.yml" + - ".github/workflows/release.yml" pull_request: paths-ignore: - - 'README.md' - - 'docs/**' - - '.github/workflows/docs.yml' - - '.github/workflows/release.yml' - + - "**.md" + - "renovate.json" + - ".github/ISSUE_TEMPLATE/**" + - ".github/PULL_REQUEST_TEMPLATE/**" + - ".github/workflows/docs.yml" + - ".github/workflows/release.yml" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - unit-tests: - name: Unit Tests + test-and-analysis: + name: Test & Analysis runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_ORGANIZATION: ${{ secrets.SONAR_ORGANIZATION }} - SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_ANALYSIS: true steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - show-progress: false - - name: Install .NET SDK - uses: actions/setup-dotnet@v5 - with: - global-json-file: global.json - - name: NuGet Cache - uses: actions/cache@v5 - with: - path: ~/.nuget/packages - key: ${{ runner.os}}-nuget-${{ hashFiles('**/packages.lock.json') }} - restore-keys: ${{ runner.os }}-nuget - - name: Test - run: ./build.sh Test - - name: Publish Test Results - if: always() - uses: dorny/test-reporter@v2 - with: - name: Test Results - path: .nuke/temp/TestResults/*.trx - reporter: dotnet-trx - path-replace-backslashes: true + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + show-progress: false + + - name: Setup .NET + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 + with: + global-json-file: global.json + cache: true + cache-dependency-path: '**/Directory.Packages.props' + + - name: Restore dependencies + run: dotnet restore + + - name: Install SonarCloud scanner + if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' + run: dotnet tool update dotnet-sonarscanner --tool-path .sonar/scanner + + - name: Begin SonarCloud analysis + if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' + run: | + .sonar/scanner/dotnet-sonarscanner begin \ + /k:"visus:AddressValidation" \ + /o:"visus" \ + /d:sonar.token="$SONAR_TOKEN" \ + /d:sonar.host.url="https://sonarcloud.io" \ + /d:sonar.cs.vscoveragexml.reportsPaths="**/TestResults/**/*.xml" \ + /d:sonar.cs.vstest.reportsPaths="**/TestResults/**/*.trx" + + - name: Build + run: dotnet build -c Release --no-restore + + - name: Run tests + run: | + dotnet test -c Release --no-build --no-restore -- \ + --coverage \ + --coverage-output-format xml \ + --report-trx + + - name: Upload test results + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: test-results + path: "**/TestResults/*.trx" + retention-days: 1 + + - name: End SonarCloud analysis + if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' + run: | + .sonar/scanner/dotnet-sonarscanner end \ + /d:sonar.token="$SONAR_TOKEN" + + publish-test-results: + name: Publish Test Results + runs-on: ubuntu-latest + needs: test-and-analysis + if: always() + timeout-minutes: 10 + permissions: + contents: read + checks: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + show-progress: false + + - name: Download test results + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: test-results + path: test-results + + - name: Publish test results + uses: dorny/test-reporter@df6247429542221bc30d46a036ee47af1102c451 # v2.7.0 + with: + name: Test Results + path: test-results/**/*.trx + reporter: dotnet-trx + fail-on-error: false diff --git a/src/Visus.AddressValidation.Integration.FedEx/Clients/FedExAddressValidationClient.cs b/src/Visus.AddressValidation.Integration.FedEx/Clients/FedExAddressValidationClient.cs index cb8f6f9..714942c 100644 --- a/src/Visus.AddressValidation.Integration.FedEx/Clients/FedExAddressValidationClient.cs +++ b/src/Visus.AddressValidation.Integration.FedEx/Clients/FedExAddressValidationClient.cs @@ -36,7 +36,10 @@ public FedExAddressValidationClient(HttpClient httpClient, IOptions @@ -81,12 +80,12 @@ public sealed class FedExServiceOptions : IValidatableObject /// used for address validation responses returned by the FedEx API. /// /// - /// Defaults to . - /// of the executing thread at the time the options object is - /// constructed. Use a value such as en-US or fr-FR to + /// When (the default), the x-locale + /// request header is omitted and FedEx will use its own default locale. + /// Set to an IETF BCP 47 tag such as en-US or fr-FR to /// request responses in a specific language and region. /// - public string Locale { get; set; } = CultureInfo.CurrentCulture.Name; + public string? Locale { get; set; } /// /// Performs cross-property validation on the options object. @@ -115,7 +114,7 @@ public IEnumerable Validate(ValidationContext validationContex [nameof(EndpointOverrideUri),]); } - if ( !Constants.SupportedLocales.Contains(Locale) ) + if ( Locale is not null && !Constants.SupportedLocales.Contains(Locale) ) { yield return new ValidationResult( $"'{Locale}' is not a supported IETF BCP 47 language tag.", diff --git a/tests/Visus.AddressValidation.Integration.FedEx.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt b/tests/Visus.AddressValidation.Integration.FedEx.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt index 961bc11..5fb2228 100644 --- a/tests/Visus.AddressValidation.Integration.FedEx.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt +++ b/tests/Visus.AddressValidation.Integration.FedEx.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt @@ -16,7 +16,7 @@ namespace Visus.AddressValidation.Integration.FedEx.Configuration public required string ClientSecret { get; set; } public System.Uri EndpointBaseUri { get; } public System.Uri? EndpointOverrideUri { get; set; } - public string Locale { get; set; } + public string? Locale { get; set; } public System.Collections.Generic.IEnumerable Validate(System.ComponentModel.DataAnnotations.ValidationContext validationContext) { } } [Microsoft.Extensions.Options.OptionsValidator] diff --git a/tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt b/tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt new file mode 100644 index 0000000..1f4a88c --- /dev/null +++ b/tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt @@ -0,0 +1,12 @@ +[assembly: System.Resources.NeutralResourcesLanguage("en")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DynamicProxyGenAssembly2")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Visus.AddressValidation.SourceGeneration.Tests")] +namespace Visus.AddressValidation.SourceGeneration +{ + [Microsoft.CodeAnalysis.Generator] + public sealed class CustomResponseDataGenerator : Microsoft.CodeAnalysis.IIncrementalGenerator + { + public CustomResponseDataGenerator() { } + public void Initialize(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext context) { } + } +} \ No newline at end of file diff --git a/tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.cs b/tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.cs new file mode 100644 index 0000000..af8bade --- /dev/null +++ b/tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.cs @@ -0,0 +1,25 @@ +extern alias sourcegen; + +namespace Visus.AddressValidation.SourceGeneration.Tests; + +using System.Runtime.CompilerServices; +using PublicApiGenerator; + +internal sealed class ApiTests +{ + [Test] + [MethodImpl(MethodImplOptions.NoInlining)] + public async Task PublicApi_HasNoBreakingChanges_Async() + { + string api = typeof(sourcegen::Visus.AddressValidation.SourceGeneration.CustomResponseDataGenerator).Assembly.GeneratePublicApi(new ApiGeneratorOptions + { + ExcludeAttributes = + [ + "System.Reflection.AssemblyMetadataAttribute", + "System.Runtime.Versioning.TargetFrameworkAttribute", + ], + }); + + await Verify(api).ConfigureAwait(false); + } +} diff --git a/tests/Visus.AddressValidation.SourceGeneration.Tests/Visus.AddressValidation.SourceGeneration.Tests.csproj b/tests/Visus.AddressValidation.SourceGeneration.Tests/Visus.AddressValidation.SourceGeneration.Tests.csproj index 8495712..dfc7a44 100644 --- a/tests/Visus.AddressValidation.SourceGeneration.Tests/Visus.AddressValidation.SourceGeneration.Tests.csproj +++ b/tests/Visus.AddressValidation.SourceGeneration.Tests/Visus.AddressValidation.SourceGeneration.Tests.csproj @@ -12,7 +12,7 @@