feat(backend): Create Sprint 2 backend Stories and Tasks
Created detailed implementation plans for Sprint 2 backend work: Story 1: Audit Log Foundation (Phase 1) - Task 1: Design AuditLog database schema and create migration - Task 2: Create AuditLog entity and Repository - Task 3: Implement EF Core SaveChangesInterceptor - Task 4: Write unit tests for audit logging - Task 5: Integrate with ProjectManagement Module Story 2: Audit Log Core Features (Phase 2) - Task 1: Implement Changed Fields Detection (JSON Diff) - Task 2: Integrate User Context Tracking - Task 3: Add Multi-Tenant Isolation - Task 4: Implement Audit Query API - Task 5: Write Integration Tests Story 3: Sprint Management Module - Task 1: Create Sprint Aggregate Root and Domain Events - Task 2: Implement Sprint Repository and EF Core Configuration - Task 3: Create CQRS Commands and Queries - Task 4: Implement Burndown Chart Calculation - Task 5: Add SignalR Real-Time Notifications - Task 6: Write Integration Tests Total: 3 Stories, 16 Tasks, 24 Story Points (8+8+8) Estimated Duration: 10-12 days All tasks include: - Detailed technical implementation guidance - Code examples and file paths - Testing requirements (>= 90% coverage) - Performance benchmarks (< 5ms audit overhead) - Multi-tenant security validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
175
docs/plans/sprint_2_story_1_task_4.md
Normal file
175
docs/plans/sprint_2_story_1_task_4.md
Normal file
@@ -0,0 +1,175 @@
|
||||
---
|
||||
task_id: sprint_2_story_1_task_4
|
||||
story: sprint_2_story_1
|
||||
status: not_started
|
||||
estimated_hours: 4
|
||||
created_date: 2025-11-05
|
||||
assignee: Backend Team
|
||||
---
|
||||
|
||||
# Task 4: Write Unit Tests for Audit Logging
|
||||
|
||||
**Story**: Story 1 - Audit Log Foundation (Phase 1)
|
||||
**Estimated**: 4 hours
|
||||
|
||||
## Description
|
||||
|
||||
Create comprehensive unit tests for audit logging functionality to ensure correctness, multi-tenant isolation, and performance. Target >= 90% code coverage.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Unit tests for AuditLogRepository created
|
||||
- [ ] Unit tests for AuditLogInterceptor created
|
||||
- [ ] Test coverage >= 90%
|
||||
- [ ] All tests passing
|
||||
- [ ] Performance tests verify < 5ms overhead
|
||||
|
||||
## Implementation Details
|
||||
|
||||
**Files to Create**:
|
||||
|
||||
1. **Repository Tests**: `colaflow-api/tests/ColaFlow.Infrastructure.Tests/Repositories/AuditLogRepositoryTests.cs`
|
||||
```csharp
|
||||
public class AuditLogRepositoryTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task GetByEntityAsync_ShouldReturnAuditLogs_WhenEntityExists()
|
||||
{
|
||||
// Arrange
|
||||
var tenantId = Guid.NewGuid();
|
||||
var entityId = Guid.NewGuid();
|
||||
var options = CreateDbContextOptions();
|
||||
|
||||
await using var context = new ColaFlowDbContext(options);
|
||||
var tenantContext = new Mock<ITenantContext>();
|
||||
tenantContext.Setup(t => t.TenantId).Returns(tenantId);
|
||||
|
||||
var auditLog = new AuditLog
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tenantId,
|
||||
EntityType = "Project",
|
||||
EntityId = entityId,
|
||||
Action = AuditAction.Create,
|
||||
Timestamp = DateTime.UtcNow
|
||||
};
|
||||
context.AuditLogs.Add(auditLog);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var repository = new AuditLogRepository(context, tenantContext.Object);
|
||||
|
||||
// Act
|
||||
var result = await repository.GetByEntityAsync("Project", entityId);
|
||||
|
||||
// Assert
|
||||
Assert.Single(result);
|
||||
Assert.Equal(entityId, result[0].EntityId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByEntityAsync_ShouldNotReturnOtherTenantsLogs()
|
||||
{
|
||||
// Test multi-tenant isolation
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Interceptor Tests**: `colaflow-api/tests/ColaFlow.Infrastructure.Tests/Interceptors/AuditLogInterceptorTests.cs`
|
||||
```csharp
|
||||
public class AuditLogInterceptorTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SavingChangesAsync_ShouldCreateAuditLog_WhenEntityCreated()
|
||||
{
|
||||
// Arrange
|
||||
var tenantId = Guid.NewGuid();
|
||||
var userId = Guid.NewGuid();
|
||||
var options = CreateDbContextOptions();
|
||||
|
||||
var tenantContext = new Mock<ITenantContext>();
|
||||
tenantContext.Setup(t => t.TenantId).Returns(tenantId);
|
||||
|
||||
var httpContextAccessor = CreateMockHttpContextAccessor(userId);
|
||||
|
||||
var interceptor = new AuditLogInterceptor(tenantContext.Object, httpContextAccessor);
|
||||
|
||||
await using var context = new ColaFlowDbContext(options);
|
||||
context.AddInterceptors(interceptor);
|
||||
|
||||
var project = new Project
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tenantId,
|
||||
Name = "Test Project",
|
||||
Key = "TEST"
|
||||
};
|
||||
|
||||
// Act
|
||||
context.Projects.Add(project);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// Assert
|
||||
var auditLogs = await context.AuditLogs.ToListAsync();
|
||||
Assert.Single(auditLogs);
|
||||
Assert.Equal("Project", auditLogs[0].EntityType);
|
||||
Assert.Equal(AuditAction.Create, auditLogs[0].Action);
|
||||
Assert.Equal(userId, auditLogs[0].UserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SavingChangesAsync_ShouldCaptureOldAndNewValues_WhenEntityUpdated()
|
||||
{
|
||||
// Test old vs new values capture
|
||||
// ...
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SavingChangesAsync_ShouldHaveLowPerformanceOverhead()
|
||||
{
|
||||
// Arrange
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
// Act
|
||||
// Create 100 entities and measure time
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
context.Projects.Add(new Project { /* ... */ });
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
// Assert
|
||||
var avgTime = stopwatch.ElapsedMilliseconds / 100.0;
|
||||
Assert.True(avgTime < 5, $"Average time {avgTime}ms exceeds 5ms target");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Test Cases to Cover**:
|
||||
1. Create operation audit logging
|
||||
2. Update operation audit logging
|
||||
3. Delete operation audit logging
|
||||
4. Multi-tenant isolation (audit logs filtered by TenantId)
|
||||
5. UserId capture from HTTP context
|
||||
6. Old/New values serialization
|
||||
7. Performance overhead < 5ms
|
||||
8. Null userId handling (system operations)
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- Use in-memory database for fast unit tests
|
||||
- Mock ITenantContext and IHttpContextAccessor
|
||||
- Use Stopwatch for performance benchmarks
|
||||
- Target >= 90% code coverage
|
||||
|
||||
## Testing
|
||||
|
||||
- Run: `dotnet test --filter "FullyQualifiedName~AuditLog"`
|
||||
- Verify all tests pass
|
||||
- Check code coverage report
|
||||
|
||||
---
|
||||
|
||||
**Created**: 2025-11-05 by Backend Agent
|
||||
Reference in New Issue
Block a user