Fix all 45 audited bugs from bug-fixes.md
Verified every finding against the code, then fixed correctness (redirects, advancement reset, duplicate heats, high-jump attempt entry, placement ranking with tie handling, delete-dependency 500s), security (secrets out of config, config-driven admin seed, login lockout, role authorization with school scoping, heat-time IDOR, forwarded headers, last-admin guards), schema integrity (nullable+filtered ExistingStudentId, unique indexes for rounds/heats/bar heights via SchemaIntegrityFixes migration), performance (N+1 removal in high jump/reports/standings/dashboard, SQL-side student paging), and hygiene (duplicate notifications, auto-dismiss scope, local bootstrap-icons, orphaned files, test-data.sql tournament creation). FluentValidation is now registered; AutoMapper bumped to 14.0.0 (advisory fully patched only in licence-changed 15.1.1 — documented). 11 new tests; 63/63 passing. Credential rotation and deploy-time DB_CONNECTION_STRING are required manual follow-ups, documented in bug-fixes.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -232,6 +232,102 @@ public class RegistrationEligibilityTests
|
||||
Assert.Contains("already registered", reason!, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckEligibility_InactiveStudent_ReturnsFalse()
|
||||
{
|
||||
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
|
||||
var student = CreateStudent(Sex.Male);
|
||||
student.IsActive = false;
|
||||
|
||||
SetupTournamentEventLevel(tel);
|
||||
SetupStudent(student);
|
||||
|
||||
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
|
||||
|
||||
Assert.False(isEligible);
|
||||
Assert.Contains("deactivated", reason!, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckEligibility_InactiveSchool_ReturnsFalse()
|
||||
{
|
||||
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
|
||||
var student = CreateStudent(Sex.Male);
|
||||
var school = CreateSchool(SchoolLevel.Secondary);
|
||||
school.IsActive = false;
|
||||
|
||||
SetupTournamentEventLevel(tel);
|
||||
SetupStudent(student);
|
||||
SetupSchool(school);
|
||||
|
||||
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
|
||||
|
||||
Assert.False(isEligible);
|
||||
Assert.Contains("school is deactivated", reason!, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckEligibility_CompletedTournament_ReturnsFalse()
|
||||
{
|
||||
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
|
||||
tel.Tournament.Status = TournamentStatus.Completed;
|
||||
var student = CreateStudent(Sex.Male);
|
||||
|
||||
SetupTournamentEventLevel(tel);
|
||||
SetupStudent(student);
|
||||
|
||||
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
|
||||
|
||||
Assert.False(isEligible);
|
||||
Assert.Contains("completed", reason!, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckEligibility_ArchivedTournament_ReturnsFalse()
|
||||
{
|
||||
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
|
||||
tel.Tournament.IsArchived = true;
|
||||
var student = CreateStudent(Sex.Male);
|
||||
|
||||
SetupTournamentEventLevel(tel);
|
||||
SetupStudent(student);
|
||||
|
||||
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
|
||||
|
||||
Assert.False(isEligible);
|
||||
Assert.Contains("archived", reason!, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RegisterStudent_RelayOnlyRegistration_ThrowsInsteadOfCrashing()
|
||||
{
|
||||
var dto = new Application.DTOs.EventRegistrationCreateDto
|
||||
{
|
||||
TournamentEventLevelId = 1,
|
||||
StudentId = null,
|
||||
RelayTeamId = 5
|
||||
};
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => _service.RegisterStudentAsync(dto, "tester"));
|
||||
Assert.Contains("relay", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RegisterStudent_NeitherStudentNorRelay_Throws()
|
||||
{
|
||||
var dto = new Application.DTOs.EventRegistrationCreateDto
|
||||
{
|
||||
TournamentEventLevelId = 1,
|
||||
StudentId = null,
|
||||
RelayTeamId = null
|
||||
};
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => _service.RegisterStudentAsync(dto, "tester"));
|
||||
Assert.Contains("student", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckEligibility_TelNotFound_ReturnsFalse()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user