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
59 changes: 59 additions & 0 deletions test/RAPS/RoleTemplateSimplifiedTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Viper.Areas.RAPS.Models;
using Viper.Models.RAPS;

namespace Viper.test.RAPS
{
public class RoleTemplateSimplifiedTests
{
[Fact]
public void Constructor_MapsScalarsAndFlattensRoles()
{
// arrange
var rt = new RoleTemplate
{
RoleTemplateId = 42,
TemplateName = "Test Template",
Description = "Desc",
RoleTemplateRoles = new List<RoleTemplateRole>
{
new() { Role = new TblRole { RoleId = 1, Role = "Alpha", DisplayName = "Alpha" } },
new() { Role = new TblRole { RoleId = 2, Role = "Beta", DisplayName = "Beta" } }
}
};

// act
var dto = new RoleTemplateSimplified(rt);

// assert
Assert.Equal(42, dto.RoleTemplateId);
Assert.Equal("Test Template", dto.TemplateName);
Assert.Equal("Desc", dto.Description);
var roles = dto.Roles.ToList();
Assert.Equal(2, roles.Count);
Assert.Equal(1, roles[0].RoleId);
Assert.Equal("Alpha", roles[0].FriendlyName);
Assert.Equal(2, roles[1].RoleId);
Assert.Equal("Beta", roles[1].FriendlyName);
}

[Fact]
public void Constructor_EmptyRoles_ReturnsEmptyCollection()
{
// arrange
var rt = new RoleTemplate
{
RoleTemplateId = 7,
TemplateName = "No Roles",
Description = "",
RoleTemplateRoles = new List<RoleTemplateRole>()
};

// act
var dto = new RoleTemplateSimplified(rt);

// assert
Assert.Equal(7, dto.RoleTemplateId);
Assert.Empty(dto.Roles);
}
}
}
8 changes: 8 additions & 0 deletions test/Utilities/AcademicYearHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ public void GetAcademicYearStart_DateInJanuary_ReturnsJuly1OfPriorYear()
Assert.Equal(new DateTime(2025, 7, 1, 0, 0, 0, DateTimeKind.Local), result);
}

[Fact]
public void GetAcademicYearStart_DateBeforeJulyYearOne_Throws()
{
// date.Year == 1 && month < 7 would underflow to year 0 in AddYears(-1)
Assert.Throws<ArgumentOutOfRangeException>(() =>
AcademicYearHelper.GetAcademicYearStart(DateTime.MinValue));
}

#endregion

#region GetDateRange Tests
Expand Down
6 changes: 3 additions & 3 deletions web/Areas/CTS/Models/AuditRow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ public AuditRow(CtsAudit dbAudit)
Timestamp = dbAudit.TimeStamp;
ModifiedById = dbAudit.ModifiedBy;
ModifiedByName = dbAudit.Modifier.LastName + ", " + dbAudit.Modifier.FirstName;
if (dbAudit?.Encounter?.Student != null)
if (dbAudit.Encounter?.Student != null)
{
ModifiedPersonId = dbAudit.Encounter?.StudentUserId;
ModifiedPersonName = dbAudit.Encounter?.Student?.LastName + ", " + dbAudit.Encounter?.Student?.FirstName;
ModifiedPersonId = dbAudit.Encounter.StudentUserId;
ModifiedPersonName = dbAudit.Encounter.Student.LastName + ", " + dbAudit.Encounter.Student.FirstName;
}

}
Expand Down
6 changes: 5 additions & 1 deletion web/Areas/Effort/Services/PercentRolloverService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public PercentRolloverService(

public async Task<PercentRolloverPreviewDto> GetRolloverPreviewAsync(int year, CancellationToken ct = default)
{
// Bound year for DateTime constructions below (year and year+1 must be valid years).
ArgumentOutOfRangeException.ThrowIfLessThan(year, 1);
ArgumentOutOfRangeException.ThrowIfGreaterThan(year, 9998);

var result = new PercentRolloverPreviewDto();

result.SourceAcademicYear = year;
Expand All @@ -42,7 +46,7 @@ public async Task<PercentRolloverPreviewDto> GetRolloverPreviewAsync(int year, C
var july1Start = new DateTime(year, 7, 1, 0, 0, 0, DateTimeKind.Local);
result.OldEndDate = june30Start;
result.NewStartDate = july1Start;
result.NewEndDate = new DateTime(year + 1, 6, 30, 0, 0, 0, DateTimeKind.Local);
result.NewEndDate = june30Start.AddYears(1);

// Find assignments ending on June 30 of source year (any time on that day)
var assignments = await _context.Percentages
Expand Down
3 changes: 2 additions & 1 deletion web/Areas/Effort/Services/PercentageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ public async Task<PercentageValidationResult> ValidatePercentageAsync(
.Where(p => !string.Equals(p.PercentAssignType.Class, LeaveTypeClass, StringComparison.OrdinalIgnoreCase))
.Sum(p => (decimal)EffortConstants.ToDisplayPercent(p.PercentageValue));

if (isNewActive && type != null && !string.Equals(type.Class, LeaveTypeClass, StringComparison.OrdinalIgnoreCase))
// type is guaranteed non-null here: early-return above when result.IsValid==false (set when type==null).
if (isNewActive && !string.Equals(type!.Class, LeaveTypeClass, StringComparison.OrdinalIgnoreCase))
{
var newTotal = activeNonLeaveTotal + Math.Round(request.PercentageValue, EffortConstants.PercentDisplayDecimals);
if (newTotal > 100)
Expand Down
15 changes: 9 additions & 6 deletions web/Areas/RAPS/Controllers/RAPSController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,17 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context
List<string>? path = HttpContext?.Request?.Path.ToString().Split("/").ToList();
int? rapsIdx = path?.FindIndex(p => p.Equals("raps", StringComparison.OrdinalIgnoreCase));
string instance = "VIPER";
if (rapsIdx != null && rapsIdx > -1 && path?.Count > rapsIdx + 1)
{
instance = path[(int)rapsIdx + 1];
}
string page = "";
if (rapsIdx != null && rapsIdx > -1 && path?.Count > rapsIdx + 2)
if (path != null && rapsIdx is { } idx && idx > -1)
{
page = path[(int)rapsIdx + 2];
if (path.Count > idx + 1)
{
instance = path[idx + 1];
}
if (path.Count > idx + 2)
{
page = path[idx + 2];
}
}
ViewData["ViperLeftNav"] = await Nav(roleIdValid ? roleId : null,
permIdValid ? permissionId : null,
Expand Down
28 changes: 11 additions & 17 deletions web/Areas/RAPS/Controllers/RoleTemplatesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,9 @@ public async Task<ActionResult<IEnumerable<RoleTemplateSimplified>>> GetRoleTemp
.OrderBy(rt => rt.TemplateName)
.ToListAsync();

List<RoleTemplateSimplified> roleTemplates = new();
foreach (var rt in dbRoleTemplates)
{
roleTemplates.Add(new RoleTemplateSimplified(rt));
}
return roleTemplates;
return dbRoleTemplates
.Select(rt => new RoleTemplateSimplified(rt))
.ToList();
}

// GET: RoleTemplates/5
Expand Down Expand Up @@ -159,9 +156,6 @@ public async Task<ActionResult<RoleTemplateApplyPreview>> RoleTemplateApply(stri
: u.MothraId == memberId)
.FirstOrDefaultAsync();

List<RoleApplyPreview> rolesToApply = new();

//if user is not found, return the object with placeholder text
if (user == null)
{
return null;
Expand All @@ -172,21 +166,21 @@ public async Task<ActionResult<RoleTemplateApplyPreview>> RoleTemplateApply(stri
.Where(rm => rm.MemberId == user.MothraId)
.Select(rm => rm.Role)
.ToListAsync();
foreach (var role in roleTemplate.RoleTemplateRoles)
{
rolesToApply.Add(new RoleApplyPreview
HashSet<int> userRoleIds = userRoles.Select(r => r.RoleId).ToHashSet();
List<RoleApplyPreview> rolesToApply = roleTemplate.RoleTemplateRoles
.Select(role => new RoleApplyPreview
{
RoleId = role.Role.RoleId,
RoleName = role.Role.FriendlyName,
Description = role.Role.Description,
UserHasRole = userRoles.Find(r => r.RoleId == role.RoleTemplateRoleRoleId) != null
});
}
UserHasRole = userRoleIds.Contains(role.RoleTemplateRoleRoleId)
})
.ToList();

return new RoleTemplateApplyPreview
{
DisplayName = user?.DisplayFullName ?? "User not found",
MemberId = user?.MothraId ?? "",
DisplayName = user.DisplayFullName,
MemberId = user.MothraId,
Roles = rolesToApply
};
}
Expand Down
6 changes: 3 additions & 3 deletions web/Areas/RAPS/Services/RAPSAuditService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ public async Task<List<AuditLog>> GetMemberRolesAndPermissionHistory(string inst
Dictionary<string, List<string>> actionsPerformedOnObject = new();
foreach (AuditLog auditLog in auditEntries)
{
if (auditLog?.RoleId != null || auditLog?.PermissionId != null)
if (auditLog.RoleId != null || auditLog.PermissionId != null)
{
string key = auditLog?.RoleId != null
string key = auditLog.RoleId != null
? "role-" + auditLog.RoleId
: "permission-" + auditLog!.PermissionId;
: "permission-" + auditLog.PermissionId;
if (actionsPerformedOnObject.TryGetValue(key, out var moreRecentActions))
{
bool undone = false;
Expand Down
10 changes: 8 additions & 2 deletions web/Classes/Utilities/AcademicYearHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,14 @@ public static List<int> GetTermCodesForAcademicYear(IEnumerable<int> allTermCode
/// </summary>
public static DateTime GetAcademicYearStart(DateTime date)
{
var year = date.Month < 7 ? date.Year - 1 : date.Year;
return new DateTime(year, 7, 1, 0, 0, 0, DateTimeKind.Local);
// A date before July of year 1 has no representable academic-year start (year 0 is invalid).
if (date.Year == 1 && date.Month < 7)
{
throw new ArgumentOutOfRangeException(nameof(date),
"Date must be on or after July 1, year 1 for academic year calculation.");
}
var julyOfYear = new DateTime(date.Year, 7, 1, 0, 0, 0, DateTimeKind.Local);
return date.Month < 7 ? julyOfYear.AddYears(-1) : julyOfYear;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion web/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,8 @@ private async Task<IActionResult> AuthenticateCasLogin(string? ticket, string? r
{
var claimsIdentity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, validatedUserName), new Claim(ClaimTypes.NameIdentifier, validatedUserName), new Claim(ClaimTypes.AuthenticationMethod, "CAS") }, CookieAuthenticationDefaults.AuthenticationScheme);

XElement? attributesNode = successNode?.Element(_ns + "attributes");
// successNode is guaranteed non-null here: validatedUserName is derived from successNode?.Element(user)?.Value.
XElement? attributesNode = successNode!.Element(_ns + "attributes");
if (attributesNode != null)
{
foreach (string attributeName in _casAttributesToCapture)
Expand Down
10 changes: 5 additions & 5 deletions web/Models/Students/StudentClassYear.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Viper.Models.Students
{
public class StudentClassYear
public sealed class StudentClassYear
{

public int StudentClassYearId { get; set; }
Expand All @@ -20,10 +20,10 @@ public class StudentClassYear
public int? UpdatedBy { get; set; }
public string? Comment { get; set; }

public virtual Person? Student { get; set; }
public virtual ClassYearLeftReason? ClassYearLeftReason { get; set; }
public virtual Person? AddedByPerson { get; set; }
public virtual Person? UpdatedByPerson { get; set; }
public Person? Student { get; set; }
public ClassYearLeftReason? ClassYearLeftReason { get; set; }
public Person? AddedByPerson { get; set; }
public Person? UpdatedByPerson { get; set; }

[NotMapped]
public string? LeftReasonText
Expand Down
Loading