NHSISL / LibPostalClient

MIT License
1 stars 3 forks source link

Libpostal.zip not correctly unzip when included in Test project #58

Open bard83 opened 1 month ago

bard83 commented 1 month ago

Executing the test the below error is returned:

$ dotnet test

  MyProject -> XXX\MyProject\bin\Debug\net8.0\MyProject.dll
  Checking for LibpostalData.zip
  Unzipping LibpostalData.zip
  Unzipped LibpostalData.zip
  Copying libpostal library files to output directory
  MyProjectTests -> XXX\MyProjectTests\bin\Debug\net8.0\MyProjectTests.dll
Test run for XXX\MyProjectTests\bin\Debug\net8.0\MyProjectTests.dll (.NETCoreApp,Version=v8.0)
Microsoft (R) Test Execution Command Line Tool Version 17.8.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Setup failed for test fixture MyProjectTests.LibpostalClientTests
System.DllNotFoundException : Unable to load DLL 'libpostal-1' or one of its dependencies: The specified module could not be found. (0x8007007E)
StackTrace:    at LibPostalNet.libpostal.__Internal.LibpostalSetupDatadir(String datadir)
   at LibPostalNet.libpostal.LibpostalSetupDatadir(String datadir)
   at NHSISL.LibPostalClient.Brokers.LibPostal.LibPostalBroker..ctor(LibPostalConfiguration libPostalConfiguration)
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at NHSISL.LibPostalClient.Clients.LibPostalClient..ctor(LibPostalConfiguration config)
   at MyProjectTests.LibpostalClientTests..ctor() in XXX\MyProjectTests\LibpostalClientTests.cs:line 25
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
  Failed GivenShortAddress_ParseAddressAsync_ReturnsExpectedResult [157 ms]
  Error Message:
   OneTimeSetUp: System.DllNotFoundException : Unable to load DLL 'libpostal-1' or one of its dependencies: The specified module could not be found. (0x8007007E)

Failed!  - Failed:     1, Passed:     0, Skipped:     0, Total:     1, Duration: 157 ms - MyProjectTests.dll (net8.0)

My program is really simple.

$ cat MyProject/Program.cs
using System.Reflection;
using NHSISL.LibPostalClient.Clients;
using NHSISL.LibPostalClient.Models.Brokers.LibPostal;

Console.WriteLine("Hello, World!");
var assembly = Assembly.GetExecutingAssembly().Location;
var dataFolderPath = Path.Combine(
    Path.GetDirectoryName(assembly) ?? throw new InvalidOperationException("Assembly path null"),
    @"Data"
);

var config = new LibPostalConfiguration {
    DataDirectory = dataFolderPath,
    ParserDataDirectory = dataFolderPath,
    LanguageClassifierDataDirectory = dataFolderPath
};

var libPostalClient = new LibPostalClient(config);
var inputAddress = "123 Main St";
var outputParsedAddress =
    new List<KeyValuePair<string, string>> { new("road", "main st"), new("house_number", "123"), };
var expectedParsedAddress = outputParsedAddress.ToList();

// Act
var actualParsedAddress = await libPostalClient.ParseAddressAsync(inputAddress);

foreach (var parsedAddress in actualParsedAddress)
{
        Console.WriteLine(parsedAddress.Value);
}

The csproj is below:

$ cat MyProject/MyProject.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
          <PackageReference Include="NHSISL.LibPostalClient" Version="1.0.0.8"/>
  </ItemGroup>

</Project>

The test csproj is below:

$ cat MyProjectTests.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

        <ItemGroup>
                <Using Include="FluentAssertions" />
                <Using Include="Moq" />
                <Using Include="NUnit.Framework" />
        </ItemGroup>

  <ItemGroup>
        <PackageReference Include="FluentAssertions" Version="6.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
        <PackageReference Include="Moq" Version="4.20.70" />
    <PackageReference Include="NUnit" Version="3.13.3" />
    <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
    <PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
    <PackageReference Include="coverlet.collector" Version="6.0.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyProject\MyProject.csproj" />
  </ItemGroup>

</Project>

The unit test

$ cat LibpostalClientTests.cs
using System.Reflection;
using NHSISL.LibPostalClient.Clients;
using NHSISL.LibPostalClient.Models.Brokers.LibPostal;

namespace MyProjectTests;

public class LibpostalClientTests
{
        private readonly ILibPostalClient _libPostalClient;

        public LibpostalClientTests()
        {
                var assembly = Assembly.GetExecutingAssembly().Location;
                var dataFolderPath = Path.Combine(
                    Path.GetDirectoryName(assembly) ?? throw new InvalidOperationException("Assembly path null"),
                    @"Data"
                );

                var config = new LibPostalConfiguration {
                    DataDirectory = dataFolderPath,
                    ParserDataDirectory = dataFolderPath,
                    LanguageClassifierDataDirectory = dataFolderPath
                };

                _libPostalClient = new LibPostalClient(config);
        }

        [Test]
        public async Task GivenShortAddress_ParseAddressAsync_ReturnsExpectedResult()
        {
                // Arrange
                const string inputAddress = "123 Main St";
                var outputParsedAddress =
                    new List<KeyValuePair<string, string>> { new("road", "main st"), new("house_number", "123"), };
                var expectedParsedAddress = outputParsedAddress.ToList();

                // Act
                var actualParsedAddress = await _libPostalClient.ParseAddressAsync(inputAddress);

                // Assert
                actualParsedAddress.Should().BeEquivalentTo(expectedParsedAddress);
        }
}

Expected Behavior

The Libpost data files are copied under the MyprojectTests/bin/(Debug|Release)/net8.0 folder instead of MyProject folder.

bard83 commented 1 month ago

Workaround to add also the package to the test project:

$ cat MyProjectTests.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

        <ItemGroup>
                <Using Include="FluentAssertions" />
                <Using Include="Moq" />
                <Using Include="NUnit.Framework" />
        </ItemGroup>

  <ItemGroup>
        <PackageReference Include="FluentAssertions" Version="6.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
        <PackageReference Include="Moq" Version="4.20.70" />
    <PackageReference Include="NUnit" Version="3.13.3" />
    <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
    <PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
    <PackageReference Include="coverlet.collector" Version="6.0.0" />
        <PackageReference Include="NHSISL.LibPostalClient" Version="1.0.0.8"/>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyProject\MyProject.csproj" />
  </ItemGroup>

</Project>

Not the best approach