Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,10 @@

return Habit.Create(new HabitCreateParams(
userId, title, frequencyUnit, frequencyQuantity,
JsonArgumentParser.ParseDateOnly(args, "due_date") ?? today,
JsonArgumentParser.GetOptionalString(args, "description"),
Days: JsonArgumentParser.ParseDays(args),
IsBadHabit: isBadHabit,
DueDate: JsonArgumentParser.ParseDateOnly(args, "due_date") ?? today,
DueTime: JsonArgumentParser.ParseTimeOnly(args, "due_time"),
ReminderEnabled: JsonArgumentParser.GetOptionalBool(args, "reminder_enabled") ?? false,
ReminderTimes: JsonArgumentParser.ParseIntArray(args, "reminder_times"),
Expand Down Expand Up @@ -244,10 +244,10 @@
JsonArgumentParser.GetOptionalString(sub, TitleProperty) ?? "Untitled",
JsonArgumentParser.ParseFrequencyUnit(sub) ?? parentFreqUnit,
JsonArgumentParser.GetOptionalInt(sub, "frequency_quantity") ?? parentFreqQty,
JsonArgumentParser.ParseDateOnly(sub, "due_date") ?? parentDueDate,
JsonArgumentParser.GetOptionalString(sub, "description"),
Days: subDays,
IsBadHabit: JsonArgumentParser.GetOptionalBool(sub, "is_bad_habit") ?? false,
DueDate: JsonArgumentParser.ParseDateOnly(sub, "due_date") ?? parentDueDate,
ParentHabitId: parentId,
Emoji: JsonArgumentParser.GetOptionalString(sub, "emoji"),
ChecklistItems: JsonArgumentParser.ParseChecklistItems(sub)));
Expand Down Expand Up @@ -329,7 +329,7 @@
string.IsNullOrEmpty(s) ? s : char.ToUpper(s[0]) + s[1..].ToLower();

private static readonly System.Text.RegularExpressions.Regex HabitWordRegex =
new(@"\bhabit\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Compiled);

Check warning on line 332 in src/Orbit.Application/Chat/Tools/Implementations/CreateHabitTool.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Pass a timeout to limit the execution time.

private static bool IsHabitFlavoredTitle(string title)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

public enum BulkItemStatus { Success, Failed }

public partial class BulkCreateHabitsCommandHandler(

Check warning on line 52 in src/Orbit.Application/Habits/Commands/BulkCreateHabitsCommand.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Constructor has 8 parameters, which is greater than the 7 authorized.
IGenericRepository<Habit> habitRepository,
IGenericRepository<GoogleCalendarSyncSuggestion> suggestionRepository,
IGenericRepository<Tag> tagRepository,
Expand Down Expand Up @@ -122,11 +122,11 @@
item.Title,
item.FrequencyUnit,
item.FrequencyQuantity,
item.DueDate ?? userToday,
item.Description,
Emoji: item.Emoji,
Days: item.Days,
IsBadHabit: item.IsBadHabit,
DueDate: item.DueDate ?? userToday,
DueTime: item.DueTime,
DueEndTime: item.DueEndTime,
ReminderEnabled: item.ReminderEnabled,
Expand Down Expand Up @@ -163,11 +163,11 @@
subItem.Title,
subItem.FrequencyUnit ?? item.FrequencyUnit,
subItem.FrequencyQuantity ?? item.FrequencyQuantity,
subItem.DueDate ?? item.DueDate ?? userToday,
subItem.Description,
Emoji: subItem.Emoji,
Days: subItem.Days ?? item.Days,
IsBadHabit: subItem.IsBadHabit,
DueDate: subItem.DueDate ?? item.DueDate ?? userToday,
ParentHabitId: parentHabit.Id,
IsGeneral: item.IsGeneral,
IsFlexible: subItem.IsFlexible,
Expand Down
4 changes: 2 additions & 2 deletions src/Orbit.Application/Habits/Commands/CreateHabitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
IMemoryCache cache,
ILogger<CreateHabitCommandHandler> logger) : IRequestHandler<CreateHabitCommand, Result<Guid>>
{
public async Task<Result<Guid>> Handle(CreateHabitCommand request, CancellationToken cancellationToken)

Check warning on line 44 in src/Orbit.Application/Habits/Commands/CreateHabitCommand.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed.
{
var opts = request.Options ?? new HabitCommandOptions();

Expand Down Expand Up @@ -90,11 +90,11 @@
request.Title,
request.FrequencyUnit,
request.FrequencyQuantity,
dueDate,
request.Description,
Emoji: request.Emoji,
Days: opts.Days,
IsBadHabit: request.IsBadHabit,
DueDate: dueDate,
DueTime: opts.DueTime,
DueEndTime: opts.DueEndTime,
ReminderEnabled: opts.ReminderEnabled,
Expand Down Expand Up @@ -145,7 +145,7 @@
subTitle,
request.FrequencyUnit,
request.FrequencyQuantity,
DueDate: request.DueDate ?? dueDate,
request.DueDate ?? dueDate,
ParentHabitId: parentId,
IsGeneral: request.IsGeneral,
EndDate: opts.EndDate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ public async Task<Result<Guid>> Handle(CreateSubHabitCommand request, Cancellati
request.Title,
request.FrequencyUnit ?? parent.FrequencyUnit,
request.FrequencyQuantity ?? parent.FrequencyQuantity,
childDueDate,
request.Description,
Emoji: request.Emoji,
Days: opts.Days,
IsBadHabit: request.IsBadHabit,
DueDate: childDueDate,
DueTime: opts.DueTime,
DueEndTime: opts.DueEndTime,
ParentHabitId: parent.Id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ private static Result<Habit> CloneHabit(
source.Title,
source.FrequencyUnit,
source.FrequencyQuantity,
source.DueDate,
source.Description,
Emoji: source.Emoji,
Days: source.Days.ToList(),
IsBadHabit: source.IsBadHabit,
DueDate: source.DueDate,
DueTime: source.DueTime,
DueEndTime: source.DueEndTime,
ParentHabitId: parentHabitId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
/// by construction — an already-onboarded user is a no-op (<c>Applied:false</c>) — so the client can
/// flush unconditionally after any successful auth and retry safely under the concurrency pipeline.
/// </summary>
public class ApplyOnboardingCommandHandler(

Check warning on line 60 in src/Orbit.Application/Profile/Commands/ApplyOnboardingCommand.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Constructor has 8 parameters, which is greater than the 7 authorized.
IGenericRepository<User> userRepository,
IGenericRepository<Habit> habitRepository,
IGenericRepository<Goal> goalRepository,
Expand All @@ -67,7 +67,7 @@
IUnitOfWork unitOfWork,
IMemoryCache cache) : IRequestHandler<ApplyOnboardingCommand, Result<ApplyOnboardingResponse>>
{
public async Task<Result<ApplyOnboardingResponse>> Handle(

Check warning on line 70 in src/Orbit.Application/Profile/Commands/ApplyOnboardingCommand.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Refactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed.
ApplyOnboardingCommand request, CancellationToken cancellationToken)
{
var result = await unitOfWork.ExecuteInTransactionAsync(async ct =>
Expand Down Expand Up @@ -155,11 +155,11 @@
item.Title,
item.FrequencyUnit,
item.FrequencyQuantity,
item.DueDate ?? today,
item.Description,
Emoji: item.Emoji,
Days: item.Days,
IsBadHabit: item.IsBadHabit,
DueDate: item.DueDate ?? today,
DueTime: item.DueTime,
ReminderEnabled: item.ReminderEnabled,
ReminderTimes: item.ReminderTimes,
Expand Down
8 changes: 3 additions & 5 deletions src/Orbit.Domain/Entities/Habit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ public record HabitCreateParams(
string Title,
FrequencyUnit? FrequencyUnit,
int? FrequencyQuantity,
DateOnly DueDate,
string? Description = null,
IReadOnlyList<System.DayOfWeek>? Days = null,
bool IsBadHabit = false,
DateOnly? DueDate = null,
TimeOnly? DueTime = null,
TimeOnly? DueEndTime = null,
Guid? ParentHabitId = null,
Expand Down Expand Up @@ -129,8 +129,6 @@ public static Result<Habit> Create(HabitCreateParams p)
if (reminderValidation is not null)
return Result.Failure<Habit>(reminderValidation);

var effectiveDueDate = p.DueDate ?? DateOnly.FromDateTime(DateTime.UtcNow);

return Result.Success(new Habit
{
UserId = p.UserId,
Expand All @@ -143,9 +141,9 @@ public static Result<Habit> Create(HabitCreateParams p)
IsBadHabit = p.IsBadHabit,
IsGeneral = p.IsGeneral,
IsFlexible = p.IsFlexible,
DueDate = effectiveDueDate,
DueDate = p.DueDate,
OriginalDayOfMonth = p.FrequencyUnit is Enums.FrequencyUnit.Month or Enums.FrequencyUnit.Year
? effectiveDueDate.Day
? p.DueDate.Day
: null,
DueTime = p.DueTime,
DueEndTime = p.DueEndTime,
Expand Down
5 changes: 2 additions & 3 deletions src/Orbit.Domain/Entities/HabitInvariants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,15 @@ internal static class HabitInvariants
public static AppError? ValidateDateOptions(
TimeOnly? dueTime, TimeOnly? dueEndTime,
DateOnly? endDate, FrequencyUnit? frequencyUnit,
bool isGeneral, DateOnly? dueDate)
bool isGeneral, DateOnly dueDate)
{
if (dueEndTime.HasValue && dueTime.HasValue && dueEndTime.Value <= dueTime.Value)
return DomainErrors.EndTimeBeforeStartTime;

if (endDate.HasValue && frequencyUnit is null && !isGeneral)
return DomainErrors.OneTimeTaskHasEndDate;

var effectiveDueDate = dueDate ?? DateOnly.FromDateTime(DateTime.UtcNow);
if (endDate.HasValue && endDate.Value < effectiveDueDate)
if (endDate.HasValue && endDate.Value < dueDate)
return DomainErrors.EndDateBeforeStartDate;

return null;
Expand Down
2 changes: 1 addition & 1 deletion tests/Orbit.Application.Tests/Chat/QueryHabitsToolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ private static Habit CreateHabit(
string? emoji = null)
{
var habit = Habit.Create(new HabitCreateParams(UserId, title, freq, qty,
DueDate: dueDate, IsBadHabit: isBadHabit, IsGeneral: isGeneral,
DueDate: dueDate ?? DateOnly.FromDateTime(DateTime.UtcNow), IsBadHabit: isBadHabit, IsGeneral: isGeneral,
ParentHabitId: parentId, Emoji: emoji)).Value;
if (position.HasValue) habit.SetPosition(position.Value);
return habit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ public async Task Handle_Success_DedupesAgainstExistingHabits()

var existingHabit = Habit.Create(new HabitCreateParams(
user.Id, "Daily standup", FrequencyUnit.Day, 1,
DueDate: DateOnly.FromDateTime(DateTime.UtcNow),
GoogleEventId: "evt_a")).Value;
_habitRepo.FindAsync(Arg.Any<Expression<Func<Habit, bool>>>(), Arg.Any<CancellationToken>())
.Returns(new List<Habit> { existingHabit }.AsReadOnly());
Expand Down Expand Up @@ -659,7 +660,7 @@ public async Task Handle_UniqueViolationButNoNewSuggestions_Propagates()
.Returns(new GoogleTokenRefreshOutcome("new_access", GoogleTokenRefreshResult.Success, null));

var existingHabit = Habit.Create(new HabitCreateParams(
user.Id, "Standup", FrequencyUnit.Day, 1, GoogleEventId: "evt_a")).Value;
user.Id, "Standup", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow), GoogleEventId: "evt_a")).Value;
_habitRepo.FindAsync(Arg.Any<Expression<Func<Habit, bool>>>(), Arg.Any<CancellationToken>())
.Returns(new List<Habit> { existingHabit }.AsReadOnly());
_fetcher.FetchAsync(Arg.Any<string>(), Arg.Any<IReadOnlyCollection<string>?>(), Arg.Any<DateTime?>(), Arg.Any<CancellationToken>())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public LinkHabitsToGoalCommandHandlerTests()
public async Task Handle_ValidLink_LinksHabitsToGoal()
{
var goal = Goal.Create(UserId, "Goal", 100, "km").Value;
var habit1 = Habit.Create(new HabitCreateParams(UserId, "Habit 1", FrequencyUnit.Day, 1)).Value;
var habit2 = Habit.Create(new HabitCreateParams(UserId, "Habit 2", FrequencyUnit.Day, 1)).Value;
var habit1 = Habit.Create(new HabitCreateParams(UserId, "Habit 1", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var habit2 = Habit.Create(new HabitCreateParams(UserId, "Habit 2", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;

_goalRepo.FindOneTrackedAsync(
Arg.Any<Expression<Func<Goal, bool>>>(),
Expand Down Expand Up @@ -91,7 +91,7 @@ public async Task Handle_TooManyHabits_ReturnsFailure()
public async Task Handle_EmptyHabitList_ClearsExistingLinks()
{
var goal = Goal.Create(UserId, "Goal", 100, "km").Value;
var existingHabit = Habit.Create(new HabitCreateParams(UserId, "Existing", FrequencyUnit.Day, 1)).Value;
var existingHabit = Habit.Create(new HabitCreateParams(UserId, "Existing", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
goal.AddHabit(existingHabit);

_goalRepo.FindOneTrackedAsync(
Expand All @@ -117,8 +117,8 @@ public async Task Handle_EmptyHabitList_ClearsExistingLinks()
public async Task Handle_ReplacesExistingLinks()
{
var goal = Goal.Create(UserId, "Goal", 100, "km").Value;
var oldHabit = Habit.Create(new HabitCreateParams(UserId, "Old", FrequencyUnit.Day, 1)).Value;
var newHabit = Habit.Create(new HabitCreateParams(UserId, "New", FrequencyUnit.Day, 1)).Value;
var oldHabit = Habit.Create(new HabitCreateParams(UserId, "Old", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var newHabit = Habit.Create(new HabitCreateParams(UserId, "New", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
goal.AddHabit(oldHabit);

_goalRepo.FindOneTrackedAsync(
Expand Down Expand Up @@ -161,7 +161,7 @@ public async Task Handle_PaywalledUser_ReturnsPayGateFailure()
public async Task Handle_ForeignOrMissingHabitId_ReturnsFailureWithoutClearing()
{
var goal = Goal.Create(UserId, "Goal", 100, "km").Value;
var existingHabit = Habit.Create(new HabitCreateParams(UserId, "Existing", FrequencyUnit.Day, 1)).Value;
var existingHabit = Habit.Create(new HabitCreateParams(UserId, "Existing", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
goal.AddHabit(existingHabit);

_goalRepo.FindOneTrackedAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public BulkDeleteHabitsCommandHandlerTests()
[Fact]
public async Task Handle_ValidIds_DeletesAllHabits()
{
var habit1 = Habit.Create(new HabitCreateParams(UserId, "Habit 1", FrequencyUnit.Day, 1)).Value;
var habit2 = Habit.Create(new HabitCreateParams(UserId, "Habit 2", FrequencyUnit.Day, 1)).Value;
var habit1 = Habit.Create(new HabitCreateParams(UserId, "Habit 1", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var habit2 = Habit.Create(new HabitCreateParams(UserId, "Habit 2", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;

_habitRepo.FindTrackedAsync(
Arg.Any<Expression<Func<Habit, bool>>>(),
Expand All @@ -66,7 +66,7 @@ await _unitOfWork.Received(1).ExecuteInTransactionAsync(
[Fact]
public async Task Handle_SomeNotFound_ReportsPartialFailure()
{
var habit1 = Habit.Create(new HabitCreateParams(UserId, "Habit 1", FrequencyUnit.Day, 1)).Value;
var habit1 = Habit.Create(new HabitCreateParams(UserId, "Habit 1", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var missingId = Guid.NewGuid();

_habitRepo.FindTrackedAsync(
Expand Down Expand Up @@ -105,7 +105,7 @@ public async Task Handle_AllNotFound_ReportsAllFailed()
[Fact]
public async Task Handle_InvalidatesSummaryCache()
{
var habit = Habit.Create(new HabitCreateParams(UserId, "Habit", FrequencyUnit.Day, 1)).Value;
var habit = Habit.Create(new HabitCreateParams(UserId, "Habit", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
_habitRepo.FindTrackedAsync(
Arg.Any<Expression<Func<Habit, bool>>>(),
Arg.Any<CancellationToken>())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,25 @@ await _habitRepo.Received(1).AddAsync(
Arg.Any<CancellationToken>());
}

[Fact]
public async Task Handle_UserTimezoneAheadOfUtc_UsesUserTodayNotUtcToday()
{
var utcToday = DateOnly.FromDateTime(DateTime.UtcNow);
var userToday = utcToday.AddDays(1);
_userDateService.GetUserTodayAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>())
.Returns(userToday);

var command = new CreateHabitCommand(
UserId, "Timezone Habit", null, FrequencyUnit.Day, 1);

var result = await _handler.Handle(command, CancellationToken.None);

result.IsSuccess.Should().BeTrue();
await _habitRepo.Received(1).AddAsync(
Arg.Is<Habit>(h => h.DueDate == userToday && h.DueDate != utcToday),
Arg.Any<CancellationToken>());
}

[Fact]
public async Task Handle_WithOptions_PassesDaysAndTimes()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public async Task Handle_HabitNotFound_ReturnsFailure()
[Fact]
public async Task Handle_PayGateLimitReached_ReturnsPayGateFailure()
{
var original = Habit.Create(new HabitCreateParams(UserId, "Habit", FrequencyUnit.Day, 1)).Value;
var original = Habit.Create(new HabitCreateParams(UserId, "Habit", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
SetupAllHabitsForUser(new List<Habit> { original });

_payGate.CanCreateHabits(Arg.Any<Guid>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
Expand Down Expand Up @@ -118,10 +118,10 @@ await _habitRepo.Received(1).AddAsync(
public async Task Handle_WithChildren_DuplicatesChildHabits()
{
var parent = Habit.Create(new HabitCreateParams(
UserId, "Morning Routine", FrequencyUnit.Day, 1)).Value;
UserId, "Morning Routine", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var child = Habit.Create(new HabitCreateParams(
UserId, "Brush teeth", FrequencyUnit.Day, 1,
ParentHabitId: parent.Id)).Value;
ParentHabitId: parent.Id, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;

SetupAllHabitsForUser(new List<Habit> { parent, child });

Expand All @@ -138,10 +138,10 @@ public async Task Handle_WithChildren_DuplicatesChildHabits()
public async Task Handle_WithChildren_SubHabitPayGated_ReturnsFailure()
{
var parent = Habit.Create(new HabitCreateParams(
UserId, "Routine", FrequencyUnit.Day, 1)).Value;
UserId, "Routine", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var child = Habit.Create(new HabitCreateParams(
UserId, "Sub", FrequencyUnit.Day, 1,
ParentHabitId: parent.Id)).Value;
ParentHabitId: parent.Id, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;

SetupAllHabitsForUser(new List<Habit> { parent, child });

Expand All @@ -159,7 +159,7 @@ public async Task Handle_WithChildren_SubHabitPayGated_ReturnsFailure()
[Fact]
public async Task Handle_InvalidatesSummaryCache()
{
var original = Habit.Create(new HabitCreateParams(UserId, "Habit", FrequencyUnit.Day, 1)).Value;
var original = Habit.Create(new HabitCreateParams(UserId, "Habit", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
SetupAllHabitsForUser(new List<Habit> { original });

var realToday = DateOnly.FromDateTime(DateTime.UtcNow);
Expand Down Expand Up @@ -199,10 +199,10 @@ public async Task Handle_CompletedOneTimeTask_PreservesCompletionOnDuplicate()
public async Task Handle_CompletedOneTimeChild_PreservesCompletionOnDuplicatedChild()
{
var parent = Habit.Create(new HabitCreateParams(
UserId, "Plan trip", FrequencyUnit.Day, 1)).Value;
UserId, "Plan trip", FrequencyUnit.Day, 1, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
var child = Habit.Create(new HabitCreateParams(
UserId, "Book hotel", null, null,
ParentHabitId: parent.Id)).Value;
ParentHabitId: parent.Id, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;
child.Log(new DateOnly(2026, 6, 3)).IsSuccess.Should().BeTrue();

SetupAllHabitsForUser(new List<Habit> { parent, child });
Expand All @@ -223,7 +223,7 @@ public async Task Handle_CompletedOneTimeChild_PreservesCompletionOnDuplicatedCh
[Fact]
public async Task Handle_UncompletedOneTimeTask_StaysUncompleted()
{
var original = Habit.Create(new HabitCreateParams(UserId, "Renew passport", null, null)).Value;
var original = Habit.Create(new HabitCreateParams(UserId, "Renew passport", null, null, DueDate: DateOnly.FromDateTime(DateTime.UtcNow))).Value;

SetupAllHabitsForUser(new List<Habit> { original });
var added = CaptureAddedHabits();
Expand Down
Loading
Loading