Initial Commit

This commit is contained in:
2026-03-01 15:14:42 -04:00
commit f60174625e
377 changed files with 98166 additions and 0 deletions

View File

@@ -0,0 +1 @@
global using Xunit;

View File

@@ -0,0 +1,214 @@
using Moq;
using SportsDivision.Application.Services;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Tests;
public class HeatAdvancementTests
{
private readonly Mock<IUnitOfWork> _mockUow;
private readonly HeatManagementService _service;
public HeatAdvancementTests()
{
_mockUow = new Mock<IUnitOfWork>();
_service = new HeatManagementService(_mockUow.Object, null!);
}
private Round CreateRoundWithHeats(int topN, int fastestLosers, List<Heat> heats)
{
return new Round
{
RoundId = 1,
TournamentEventLevelId = 1,
RoundOrder = 1,
AdvanceTopN = topN,
AdvanceFastestLosers = fastestLosers,
Heats = heats
};
}
private Heat CreateHeat(int heatId, int heatNumber, List<HeatLane> lanes)
{
var heat = new Heat
{
HeatId = heatId,
HeatNumber = heatNumber,
Status = HeatStatus.Completed,
HeatLanes = lanes
};
foreach (var lane in lanes)
lane.Heat = heat;
return heat;
}
private HeatLane CreateLane(int laneId, int regId, decimal? time, bool isDNS = false, bool isDNF = false, bool isDQ = false)
{
return new HeatLane
{
HeatLaneId = laneId,
EventRegistrationId = regId,
LaneNumber = laneId,
Time = time,
IsDNS = isDNS,
IsDNF = isDNF,
IsDQ = isDQ,
IsAdvanced = false
};
}
[Fact]
public async Task CalculateAdvancement_TopNFromEachHeat()
{
var heat1 = CreateHeat(1, 1, new List<HeatLane>
{
CreateLane(1, 101, 10.5m),
CreateLane(2, 102, 10.8m),
CreateLane(3, 103, 11.2m),
CreateLane(4, 104, 11.5m)
});
var heat2 = CreateHeat(2, 2, new List<HeatLane>
{
CreateLane(5, 201, 10.6m),
CreateLane(6, 202, 10.9m),
CreateLane(7, 203, 11.3m),
CreateLane(8, 204, 11.6m)
});
var round = CreateRoundWithHeats(topN: 2, fastestLosers: 0, new List<Heat> { heat1, heat2 });
_mockUow.Setup(u => u.Rounds.GetWithHeatsAsync(1)).ReturnsAsync(round);
_mockUow.Setup(u => u.HeatLanes.Update(It.IsAny<HeatLane>()));
_mockUow.Setup(u => u.SaveChangesAsync()).ReturnsAsync(1);
await _service.CalculateAdvancementAsync(1);
// Top 2 from heat 1: lanes 1 (10.5) and 2 (10.8)
Assert.True(heat1.HeatLanes.ElementAt(0).IsAdvanced);
Assert.True(heat1.HeatLanes.ElementAt(1).IsAdvanced);
Assert.False(heat1.HeatLanes.ElementAt(2).IsAdvanced);
Assert.False(heat1.HeatLanes.ElementAt(3).IsAdvanced);
// Top 2 from heat 2: lanes 5 (10.6) and 6 (10.9)
Assert.True(heat2.HeatLanes.ElementAt(0).IsAdvanced);
Assert.True(heat2.HeatLanes.ElementAt(1).IsAdvanced);
Assert.False(heat2.HeatLanes.ElementAt(2).IsAdvanced);
Assert.False(heat2.HeatLanes.ElementAt(3).IsAdvanced);
}
[Fact]
public async Task CalculateAdvancement_TopNPlusFastestLosers()
{
var heat1 = CreateHeat(1, 1, new List<HeatLane>
{
CreateLane(1, 101, 10.5m),
CreateLane(2, 102, 10.8m),
CreateLane(3, 103, 11.0m), // 3rd in heat 1 → fastest loser candidate
CreateLane(4, 104, 11.5m)
});
var heat2 = CreateHeat(2, 2, new List<HeatLane>
{
CreateLane(5, 201, 10.6m),
CreateLane(6, 202, 10.9m),
CreateLane(7, 203, 11.1m), // 3rd in heat 2 → fastest loser candidate
CreateLane(8, 204, 11.8m)
});
var round = CreateRoundWithHeats(topN: 2, fastestLosers: 2, new List<Heat> { heat1, heat2 });
_mockUow.Setup(u => u.Rounds.GetWithHeatsAsync(1)).ReturnsAsync(round);
_mockUow.Setup(u => u.HeatLanes.Update(It.IsAny<HeatLane>()));
_mockUow.Setup(u => u.SaveChangesAsync()).ReturnsAsync(1);
await _service.CalculateAdvancementAsync(1);
// Top 2 from each heat advance as TopN
Assert.Equal(AdvanceReason.TopN, heat1.HeatLanes.ElementAt(0).AdvanceReason);
Assert.Equal(AdvanceReason.TopN, heat1.HeatLanes.ElementAt(1).AdvanceReason);
Assert.Equal(AdvanceReason.TopN, heat2.HeatLanes.ElementAt(0).AdvanceReason);
Assert.Equal(AdvanceReason.TopN, heat2.HeatLanes.ElementAt(1).AdvanceReason);
// Fastest 2 losers: lane 3 (11.0) and lane 7 (11.1)
Assert.True(heat1.HeatLanes.ElementAt(2).IsAdvanced);
Assert.Equal(AdvanceReason.FastestLoser, heat1.HeatLanes.ElementAt(2).AdvanceReason);
Assert.True(heat2.HeatLanes.ElementAt(2).IsAdvanced);
Assert.Equal(AdvanceReason.FastestLoser, heat2.HeatLanes.ElementAt(2).AdvanceReason);
// Last in each heat should NOT advance
Assert.False(heat1.HeatLanes.ElementAt(3).IsAdvanced);
Assert.False(heat2.HeatLanes.ElementAt(3).IsAdvanced);
}
[Fact]
public async Task CalculateAdvancement_DNSExcluded()
{
var heat1 = CreateHeat(1, 1, new List<HeatLane>
{
CreateLane(1, 101, 10.5m),
CreateLane(2, 102, null, isDNS: true), // DNS
CreateLane(3, 103, 11.0m),
});
var round = CreateRoundWithHeats(topN: 2, fastestLosers: 0, new List<Heat> { heat1 });
_mockUow.Setup(u => u.Rounds.GetWithHeatsAsync(1)).ReturnsAsync(round);
_mockUow.Setup(u => u.HeatLanes.Update(It.IsAny<HeatLane>()));
_mockUow.Setup(u => u.SaveChangesAsync()).ReturnsAsync(1);
await _service.CalculateAdvancementAsync(1);
Assert.True(heat1.HeatLanes.ElementAt(0).IsAdvanced);
Assert.False(heat1.HeatLanes.ElementAt(1).IsAdvanced); // DNS not advanced
Assert.True(heat1.HeatLanes.ElementAt(2).IsAdvanced);
}
[Fact]
public async Task CalculateAdvancement_DNFExcluded()
{
var heat1 = CreateHeat(1, 1, new List<HeatLane>
{
CreateLane(1, 101, 10.5m),
CreateLane(2, 102, 10.8m, isDNF: true), // DNF
CreateLane(3, 103, 11.0m),
});
var round = CreateRoundWithHeats(topN: 2, fastestLosers: 0, new List<Heat> { heat1 });
_mockUow.Setup(u => u.Rounds.GetWithHeatsAsync(1)).ReturnsAsync(round);
_mockUow.Setup(u => u.HeatLanes.Update(It.IsAny<HeatLane>()));
_mockUow.Setup(u => u.SaveChangesAsync()).ReturnsAsync(1);
await _service.CalculateAdvancementAsync(1);
Assert.True(heat1.HeatLanes.ElementAt(0).IsAdvanced);
Assert.False(heat1.HeatLanes.ElementAt(1).IsAdvanced); // DNF not advanced
Assert.True(heat1.HeatLanes.ElementAt(2).IsAdvanced);
}
[Fact]
public async Task CalculateAdvancement_DQExcluded()
{
var heat1 = CreateHeat(1, 1, new List<HeatLane>
{
CreateLane(1, 101, 10.5m),
CreateLane(2, 102, 10.2m, isDQ: true), // DQ - fastest time but disqualified
CreateLane(3, 103, 11.0m),
});
var round = CreateRoundWithHeats(topN: 2, fastestLosers: 0, new List<Heat> { heat1 });
_mockUow.Setup(u => u.Rounds.GetWithHeatsAsync(1)).ReturnsAsync(round);
_mockUow.Setup(u => u.HeatLanes.Update(It.IsAny<HeatLane>()));
_mockUow.Setup(u => u.SaveChangesAsync()).ReturnsAsync(1);
await _service.CalculateAdvancementAsync(1);
Assert.True(heat1.HeatLanes.ElementAt(0).IsAdvanced);
Assert.False(heat1.HeatLanes.ElementAt(1).IsAdvanced); // DQ not advanced
Assert.True(heat1.HeatLanes.ElementAt(2).IsAdvanced);
}
}

View File

@@ -0,0 +1,146 @@
using Moq;
using SportsDivision.Application.Services;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Tests;
public class HighJumpEliminationTests
{
private readonly Mock<IUnitOfWork> _mockUow;
private readonly HighJumpService _service;
public HighJumpEliminationTests()
{
_mockUow = new Mock<IUnitOfWork>();
_service = new HighJumpService(_mockUow.Object, null!);
}
private HighJumpHeight CreateHeight(int heightId, decimal height, int sortOrder, HighJumpAttempt? attempt = null)
{
var h = new HighJumpHeight
{
HighJumpHeightId = heightId,
Height = height,
SortOrder = sortOrder,
TournamentEventLevelId = 1,
Attempts = new List<HighJumpAttempt>()
};
if (attempt != null)
{
attempt.HighJumpHeightId = heightId;
h.Attempts.Add(attempt);
}
return h;
}
private HighJumpAttempt CreateAttempt(int regId, HighJumpAttemptResult? a1, HighJumpAttemptResult? a2 = null, HighJumpAttemptResult? a3 = null)
{
return new HighJumpAttempt
{
EventRegistrationId = regId,
Attempt1 = a1,
Attempt2 = a2,
Attempt3 = a3
};
}
[Fact]
public async Task IsEliminated_ThreeConsecutiveFailsAtOneHeight_ReturnsTrue()
{
var heights = new List<HighJumpHeight>
{
CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Clear)),
CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail))
};
_mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
.ReturnsAsync(heights);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);
var result = await _service.IsEliminatedAsync(1, 1);
Assert.True(result);
}
[Fact]
public async Task IsEliminated_ClearsAllHeights_ReturnsFalse()
{
var heights = new List<HighJumpHeight>
{
CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Clear)),
CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Clear)),
CreateHeight(3, 1.60m, 3, CreateAttempt(1, HighJumpAttemptResult.Clear))
};
_mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
.ReturnsAsync(heights);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(3)).ReturnsAsync(heights[2]);
var result = await _service.IsEliminatedAsync(1, 1);
Assert.False(result);
}
[Fact]
public async Task IsEliminated_TwoFailsThenClear_ResetsCount_ReturnsFalse()
{
var heights = new List<HighJumpHeight>
{
CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Clear)),
CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Clear))
};
_mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
.ReturnsAsync(heights);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);
var result = await _service.IsEliminatedAsync(1, 1);
Assert.False(result);
}
[Fact]
public async Task IsEliminated_NoAttempts_ReturnsFalse()
{
var heights = new List<HighJumpHeight>
{
CreateHeight(1, 1.50m, 1)
};
_mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
.ReturnsAsync(heights);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
var result = await _service.IsEliminatedAsync(1, 1);
Assert.False(result);
}
[Fact]
public async Task IsEliminated_PassedHeight_FailCountDoesNotReset()
{
// If an athlete passes a height, fails should continue accumulating
var heights = new List<HighJumpHeight>
{
CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Pass)),
// Height 2: passed (no attempt recorded in this test)
CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail))
};
_mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
.ReturnsAsync(heights);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
_mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);
var result = await _service.IsEliminatedAsync(1, 1);
// 2 fails at height 1 (pass doesn't reset) + 1 fail at height 2 = 3 consecutive fails
Assert.True(result);
}
}

View File

@@ -0,0 +1,261 @@
using Moq;
using SportsDivision.Application.Services;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Tests;
public class RegistrationEligibilityTests
{
private readonly Mock<IUnitOfWork> _mockUow;
private readonly RegistrationService _service;
public RegistrationEligibilityTests()
{
_mockUow = new Mock<IUnitOfWork>();
_service = new RegistrationService(_mockUow.Object, null!);
}
private void SetupTournamentEventLevel(TournamentEventLevel tel)
{
_mockUow.Setup(u => u.TournamentEventLevels.GetWithRegistrationsAsync(It.IsAny<int>()))
.ReturnsAsync(tel);
}
private void SetupStudent(Student student)
{
_mockUow.Setup(u => u.Students.GetByIdAsync(student.StudentId))
.ReturnsAsync(student);
}
private void SetupSchool(School school)
{
_mockUow.Setup(u => u.Schools.GetByIdAsync(school.SchoolId))
.ReturnsAsync(school);
}
private void SetupNotAlreadyRegistered()
{
_mockUow.Setup(u => u.EventRegistrations.IsStudentRegisteredAsync(It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(false);
}
private void SetupAlreadyRegistered()
{
_mockUow.Setup(u => u.EventRegistrations.IsStudentRegisteredAsync(It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(true);
}
private TournamentEventLevel CreateTel(Sex sex = Sex.Male, SchoolLevel schoolLevel = SchoolLevel.Secondary,
int? maxAge = null, bool isAgeBased = false, bool ageWaived = false)
{
return new TournamentEventLevel
{
TournamentEventLevelId = 1,
EventLevel = new EventLevel
{
Sex = sex,
SchoolLevel = schoolLevel,
MaxAge = maxAge,
IsAgeBased = isAgeBased
},
AgeRestrictionWaived = ageWaived,
Tournament = new Tournament
{
StartDate = new DateOnly(2024, 6, 1)
},
Registrations = new List<EventRegistration>()
};
}
private Student CreateStudent(Sex sex = Sex.Male, int schoolId = 1, DateOnly? dob = null)
{
return new Student
{
StudentId = 1,
FirstName = "Test",
LastName = "Student",
Sex = sex,
SchoolId = schoolId,
DateOfBirth = dob ?? new DateOnly(2010, 1, 1)
};
}
private School CreateSchool(SchoolLevel level = SchoolLevel.Secondary)
{
return new School
{
SchoolId = 1,
Name = "Test School",
SchoolLevel = level,
ZoneId = 1
};
}
[Fact]
public async Task CheckEligibility_EligibleStudent_ReturnsTrue()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
var student = CreateStudent(Sex.Male);
var school = CreateSchool(SchoolLevel.Secondary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
SetupNotAlreadyRegistered();
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.True(isEligible);
Assert.Null(reason);
}
[Fact]
public async Task CheckEligibility_SexMismatch_ReturnsFalse()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
var student = CreateStudent(Sex.Female);
SetupTournamentEventLevel(tel);
SetupStudent(student);
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.False(isEligible);
Assert.Contains("sex", reason!, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckEligibility_SecondaryStudentInPrimaryEvent_ReturnsFalse()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Primary);
var student = CreateStudent(Sex.Male);
var school = CreateSchool(SchoolLevel.Secondary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.False(isEligible);
Assert.Contains("level", reason!, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckEligibility_PrimaryStudentInSecondaryEvent_CompeteUp_ReturnsTrue()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
var student = CreateStudent(Sex.Male);
var school = CreateSchool(SchoolLevel.Primary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
SetupNotAlreadyRegistered();
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.True(isEligible);
}
[Fact]
public async Task CheckEligibility_AgeExceedsMax_ReturnsFalse()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary, maxAge: 13, isAgeBased: true);
// Student born 2010-01-01, tournament June 2024 → age 14
var student = CreateStudent(Sex.Male, dob: new DateOnly(2010, 1, 1));
var school = CreateSchool(SchoolLevel.Secondary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.False(isEligible);
Assert.Contains("age", reason!, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckEligibility_AgeWithinLimit_ReturnsTrue()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary, maxAge: 15, isAgeBased: true);
// Student born 2010-01-01, tournament June 2024 → age 14
var student = CreateStudent(Sex.Male, dob: new DateOnly(2010, 1, 1));
var school = CreateSchool(SchoolLevel.Secondary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
SetupNotAlreadyRegistered();
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.True(isEligible);
}
[Fact]
public async Task CheckEligibility_AgeExceedsButWaived_ReturnsTrue()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary, maxAge: 13, isAgeBased: true, ageWaived: true);
// Student age 14, max is 13, but waived
var student = CreateStudent(Sex.Male, dob: new DateOnly(2010, 1, 1));
var school = CreateSchool(SchoolLevel.Secondary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
SetupNotAlreadyRegistered();
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.True(isEligible);
}
[Fact]
public async Task CheckEligibility_AlreadyRegistered_ReturnsFalse()
{
var tel = CreateTel(Sex.Male, SchoolLevel.Secondary);
var student = CreateStudent(Sex.Male);
var school = CreateSchool(SchoolLevel.Secondary);
SetupTournamentEventLevel(tel);
SetupStudent(student);
SetupSchool(school);
SetupAlreadyRegistered();
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 1);
Assert.False(isEligible);
Assert.Contains("already registered", reason!, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckEligibility_TelNotFound_ReturnsFalse()
{
_mockUow.Setup(u => u.TournamentEventLevels.GetWithRegistrationsAsync(It.IsAny<int>()))
.ReturnsAsync((TournamentEventLevel?)null);
var (isEligible, reason) = await _service.CheckEligibilityAsync(999, 1);
Assert.False(isEligible);
Assert.Contains("not found", reason!, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task CheckEligibility_StudentNotFound_ReturnsFalse()
{
var tel = CreateTel();
SetupTournamentEventLevel(tel);
_mockUow.Setup(u => u.Students.GetByIdAsync(It.IsAny<int>()))
.ReturnsAsync((Student?)null);
var (isEligible, reason) = await _service.CheckEligibilityAsync(1, 999);
Assert.False(isEligible);
Assert.Contains("not found", reason!, StringComparison.OrdinalIgnoreCase);
}
}

View File

@@ -0,0 +1,155 @@
using SportsDivision.Application.Services;
namespace SportsDivision.Application.Tests;
public class ScoringServiceTests
{
private readonly ScoringService _service;
public ScoringServiceTests()
{
// ScoringService is needed; CalculatePoints is a pure method so we can pass nulls for unused deps
_service = new ScoringService(null!, null!);
}
// World Athletics scoring formula tests
// Track: Points = A * (B - T)^C where T = time in seconds
// Field/Jump: Points = A * (P - B)^C where P = performance (distance/height)
[Fact]
public void CalculatePoints_Track_BasicCalculation()
{
// Example: A=24.63, B=18.0, C=1.81, T=10.0 (100m time)
// Points = 24.63 * (18.0 - 10.0)^1.81
decimal a = 24.63m;
decimal b = 18.0m;
decimal c = 1.81m;
decimal time = 10.0m;
var points = _service.CalculatePoints(time, a, b, c, isTrack: true);
// (18.0 - 10.0) = 8.0
// 8.0^1.81 ≈ 49.33
// 24.63 * 49.33 ≈ 1215
Assert.True(points > 0);
Assert.True(points > 1000, $"Expected > 1000 for elite 100m time, got {points}");
}
[Fact]
public void CalculatePoints_Track_SlowerTimeLowerPoints()
{
decimal a = 24.63m;
decimal b = 18.0m;
decimal c = 1.81m;
var fastPoints = _service.CalculatePoints(10.0m, a, b, c, isTrack: true);
var slowPoints = _service.CalculatePoints(12.0m, a, b, c, isTrack: true);
Assert.True(fastPoints > slowPoints,
$"Fast time ({fastPoints}) should score more than slow time ({slowPoints})");
}
[Fact]
public void CalculatePoints_Track_TimeEqualsB_ReturnsZero()
{
// When T = B, diff = 0, points should be 0
decimal a = 24.63m;
decimal b = 18.0m;
decimal c = 1.81m;
var points = _service.CalculatePoints(18.0m, a, b, c, isTrack: true);
Assert.Equal(0, points);
}
[Fact]
public void CalculatePoints_Track_TimeExceedsB_ReturnsZero()
{
// When T > B, diff is negative, should return 0
decimal a = 24.63m;
decimal b = 18.0m;
decimal c = 1.81m;
var points = _service.CalculatePoints(20.0m, a, b, c, isTrack: true);
Assert.Equal(0, points);
}
[Fact]
public void CalculatePoints_Field_BasicCalculation()
{
// Field: Points = A * (P - B)^C
// Example: A=0.14354, B=220, C=1.4, P=800 (shot put in cm)
decimal a = 0.14354m;
decimal b = 220m;
decimal c = 1.4m;
decimal performance = 800m;
var points = _service.CalculatePoints(performance, a, b, c, isTrack: false);
Assert.True(points > 0);
}
[Fact]
public void CalculatePoints_Field_HigherPerformanceMorePoints()
{
decimal a = 0.14354m;
decimal b = 220m;
decimal c = 1.4m;
var highPoints = _service.CalculatePoints(800m, a, b, c, isTrack: false);
var lowPoints = _service.CalculatePoints(600m, a, b, c, isTrack: false);
Assert.True(highPoints > lowPoints,
$"Higher performance ({highPoints}) should score more than lower ({lowPoints})");
}
[Fact]
public void CalculatePoints_Field_PerformanceEqualsB_ReturnsZero()
{
decimal a = 0.14354m;
decimal b = 220m;
decimal c = 1.4m;
var points = _service.CalculatePoints(220m, a, b, c, isTrack: false);
Assert.Equal(0, points);
}
[Fact]
public void CalculatePoints_Field_PerformanceBelowB_ReturnsZero()
{
decimal a = 0.14354m;
decimal b = 220m;
decimal c = 1.4m;
var points = _service.CalculatePoints(100m, a, b, c, isTrack: false);
Assert.Equal(0, points);
}
[Fact]
public void CalculatePoints_FloorsTruncatesDecimal()
{
// Use values that would produce a fractional result
decimal a = 1.0m;
decimal b = 10.0m;
decimal c = 1.0m;
decimal performance = 5.0m;
// Track: Points = 1.0 * (10.0 - 5.0)^1.0 = 5.0 → Floor = 5
var points = _service.CalculatePoints(performance, a, b, c, isTrack: true);
Assert.Equal(5, points);
}
[Fact]
public void CalculatePoints_Track_KnownWorldAthleticsValues()
{
// Men's 100m: A=25.4347, B=18, C=1.81
// 10.00s should give ~1096 points
decimal a = 25.4347m;
decimal b = 18.0m;
decimal c = 1.81m;
var points = _service.CalculatePoints(10.0m, a, b, c, isTrack: true);
// Expected approximately 1096 for 10.00s 100m
Assert.InRange(points, 1050, 1150);
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SportsDivision.Application\SportsDivision.Application.csproj" />
<ProjectReference Include="..\..\src\SportsDivision.Domain\SportsDivision.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
<PackageReference Include="xunit" Version="2.*" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.*" />
<PackageReference Include="Moq" Version="4.*" />
</ItemGroup>
</Project>