Ability to generate dates in UTC
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?