up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Notifier.Worker.Digest;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Digest;
|
||||
|
||||
public class InMemoryDigestSchedulerTests
|
||||
{
|
||||
private readonly FakeTimeProvider _timeProvider;
|
||||
private readonly InMemoryDigestScheduler _scheduler;
|
||||
|
||||
public InMemoryDigestSchedulerTests()
|
||||
{
|
||||
_timeProvider = new FakeTimeProvider(new DateTimeOffset(2024, 1, 15, 10, 0, 0, TimeSpan.Zero));
|
||||
_scheduler = new InMemoryDigestScheduler(
|
||||
_timeProvider,
|
||||
NullLogger<InMemoryDigestScheduler>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertScheduleAsync_CreatesNewSchedule()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1");
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("schedule-1", result.ScheduleId);
|
||||
Assert.NotNull(result.NextRunAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertScheduleAsync_UpdatesExistingSchedule()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1");
|
||||
await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
var updated = schedule with { Name = "Updated Name" };
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.UpsertScheduleAsync(updated);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Updated Name", result.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetScheduleAsync_ReturnsSchedule()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1");
|
||||
await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.GetScheduleAsync("tenant1", "schedule-1");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("schedule-1", result.ScheduleId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetScheduleAsync_ReturnsNullForUnknown()
|
||||
{
|
||||
// Act
|
||||
var result = await _scheduler.GetScheduleAsync("tenant1", "unknown");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSchedulesAsync_ReturnsTenantSchedules()
|
||||
{
|
||||
// Arrange
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-1", "tenant1"));
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-2", "tenant1"));
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-3", "tenant2"));
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.GetSchedulesAsync("tenant1");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.All(result, s => Assert.Equal("tenant1", s.TenantId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteScheduleAsync_RemovesSchedule()
|
||||
{
|
||||
// Arrange
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-1"));
|
||||
|
||||
// Act
|
||||
var deleted = await _scheduler.DeleteScheduleAsync("tenant1", "schedule-1");
|
||||
|
||||
// Assert
|
||||
Assert.True(deleted);
|
||||
var result = await _scheduler.GetScheduleAsync("tenant1", "schedule-1");
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteScheduleAsync_ReturnsFalseForUnknown()
|
||||
{
|
||||
// Act
|
||||
var deleted = await _scheduler.DeleteScheduleAsync("tenant1", "unknown");
|
||||
|
||||
// Assert
|
||||
Assert.False(deleted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDueSchedulesAsync_ReturnsDueSchedules()
|
||||
{
|
||||
// Arrange - create a schedule that should run every minute
|
||||
var schedule = CreateTestSchedule("schedule-1") with
|
||||
{
|
||||
CronExpression = "0 * * * * *" // Every minute
|
||||
};
|
||||
await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
// Advance time past next run
|
||||
_timeProvider.Advance(TimeSpan.FromMinutes(2));
|
||||
|
||||
// Act
|
||||
var dueSchedules = await _scheduler.GetDueSchedulesAsync(_timeProvider.GetUtcNow());
|
||||
|
||||
// Assert
|
||||
Assert.Single(dueSchedules);
|
||||
Assert.Equal("schedule-1", dueSchedules[0].ScheduleId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDueSchedulesAsync_ExcludesDisabledSchedules()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1") with
|
||||
{
|
||||
Enabled = false,
|
||||
CronExpression = "0 * * * * *"
|
||||
};
|
||||
await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
_timeProvider.Advance(TimeSpan.FromMinutes(2));
|
||||
|
||||
// Act
|
||||
var dueSchedules = await _scheduler.GetDueSchedulesAsync(_timeProvider.GetUtcNow());
|
||||
|
||||
// Assert
|
||||
Assert.Empty(dueSchedules);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateLastRunAsync_UpdatesTimestamps()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1") with
|
||||
{
|
||||
CronExpression = "0 0 * * * *" // Every hour
|
||||
};
|
||||
await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
var runTime = _timeProvider.GetUtcNow();
|
||||
|
||||
// Act
|
||||
await _scheduler.UpdateLastRunAsync("tenant1", "schedule-1", runTime);
|
||||
|
||||
// Assert
|
||||
var updated = await _scheduler.GetScheduleAsync("tenant1", "schedule-1");
|
||||
Assert.NotNull(updated);
|
||||
Assert.Equal(runTime, updated.LastRunAt);
|
||||
Assert.NotNull(updated.NextRunAt);
|
||||
Assert.True(updated.NextRunAt > runTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertScheduleAsync_CalculatesNextRunWithTimezone()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1") with
|
||||
{
|
||||
CronExpression = "0 0 9 * * *", // 9 AM every day
|
||||
Timezone = "America/New_York"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result.NextRunAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpsertScheduleAsync_HandlesInvalidCron()
|
||||
{
|
||||
// Arrange
|
||||
var schedule = CreateTestSchedule("schedule-1") with
|
||||
{
|
||||
CronExpression = "invalid-cron"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.UpsertScheduleAsync(schedule);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result.NextRunAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSchedulesAsync_OrdersByName()
|
||||
{
|
||||
// Arrange
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-c") with { Name = "Charlie" });
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-a") with { Name = "Alpha" });
|
||||
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-b") with { Name = "Bravo" });
|
||||
|
||||
// Act
|
||||
var result = await _scheduler.GetSchedulesAsync("tenant1");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal("Alpha", result[0].Name);
|
||||
Assert.Equal("Bravo", result[1].Name);
|
||||
Assert.Equal("Charlie", result[2].Name);
|
||||
}
|
||||
|
||||
private DigestSchedule CreateTestSchedule(string id, string tenantId = "tenant1")
|
||||
{
|
||||
return new DigestSchedule
|
||||
{
|
||||
ScheduleId = id,
|
||||
TenantId = tenantId,
|
||||
Name = $"Test Schedule {id}",
|
||||
Enabled = true,
|
||||
CronExpression = "0 0 8 * * *", // 8 AM daily
|
||||
DigestType = DigestType.Daily,
|
||||
Format = DigestFormat.Html,
|
||||
CreatedAt = _timeProvider.GetUtcNow(),
|
||||
Recipients =
|
||||
[
|
||||
new DigestRecipient { Type = "email", Address = "test@example.com" }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user