nickdodd79 / AutoBogus

A C# library complementing the Bogus generator by adding auto creation and population capabilities.
MIT License
423 stars 49 forks source link

Ability to generate dates in UTC #83

Open pdevito3 opened 2 years ago

pdevito3 commented 2 years ago

What Issue Does This Solve?

As of .NET 6, NpgSQL is more strict with date time types and defaults to UTC. I think this is a good thing, but it does cause problems when generating fake dates in some cases.

In my case, when I'm writing integration tests that touch a Postgres DB, the generated DateTime for my fakers cause npgsql to throw a type error because I'm trying to insert a non UTC value.

Example

Say I have this entity:

public class User 
{
    public DateTime? Birthday { get; set; }
}

And this faker:

public class FakeUserForCreationDto : AutoFaker<UserForCreationDto>
{
}

When I run this test, I get an issue:

    [Test]
    public async Task can_update_user()
    {
        // Arrange
        var fakeUserOne = User.Generate(new FakeUserForCreationDto()
            .Generate());
        var updatedUserDto = new FakeUserUpdateDto()
            .Generate();
        await InsertAsync(fakeUserOne);

        var user = await ExecuteDbContextAsync(db => db.Users.SingleOrDefaultAsync());
        var id = user.Id;

        // Act
        var command = new UpdateUser.UpdateUserCommand(id, updatedUserDto);
        await SendAsync(command);
        var updatedUser = await ExecuteDbContextAsync(db => db.Users.Where(d => d.Id == id).SingleOrDefaultAsync());

        // Assert
        updatedUser.Should().BeEquivalentTo(updatedUserDto, options =>
            options.ExcludingMissingMembers());
    }

issue

Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while saving the entity changes. See the inner exception for details.
  ----> System.InvalidCastException : Cannot write DateTime with Kind=Local to PostgreSQL type 'timestamp with time zone', only UTC is supported. Note that it's not possible to mix DateTimes with different Kinds in an array/range. See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.

But when I update my faker to use a UTC conversion, I'm fine:

public class FakeUserForCreationDto : AutoFaker<UserForCreationDto>
{
    public FakeUserForCreationDto(Guid? userId = null)
    {
        RuleFor(u => u.Birthday, f => f.Date.Past().ToUniversalTime());
    }
}

Proposed Option

I'm open to different option, but it would be nice to globally apply this somehow. Maybe a new builder option? This would let us use local as is or convert to UTC if specified... I would thinkUnspecified would do nothing and use the default of Local?

AutoFaker.Configure(builder =>
{
  builder
    .WithDateTimeKind(DateTimeKind.Utc)  // Configures dates to be generated with a given DateTimeKind. Defaults to `Local`

    .WithLocale()           // Configures the locale to use
    .WithRepeatCount()      // Configures the number of items in a collection
    .WithDataTableRowCount()    // Configures the number of data table rows to generate
    .WithRecursiveDepth()   // Configures how deep nested types should recurse
    .WithTreeDepth()        // Configures the tree depth of an object graph
    .WithBinder()           // Configures the binder to use
    .WithFakerHub()         // Configures a Bogus.Faker instance to be used - instead of a default instance
    .WithSkip()             // Configures members to be skipped for a type
    .WithOverride();        // Configures the generator overrides to use - can be called multiple times
});

Closing

This is causing some pain for me, so I'm happy to take a crack at a PR for this if you're open to it and could possibly give me some guidance?