Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
80 changes: 79 additions & 1 deletion LineUp.Backend.Tests/CRUDTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ public async Task DeleteSchedule_Test()
public async Task CreateAvailability_Test()
{
// Arrange
sampleAvailability.Schedule = sampleSchedule;
var options = new DbContextOptionsBuilder<LineUpContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
Expand Down Expand Up @@ -411,4 +410,83 @@ public async Task UpdateAvailability_Test()
);
}
}

[Fact]
public async Task CreateRandomSchedule_Test()
{
// Arrange
var options = new DbContextOptionsBuilder<LineUpContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;

Random random = new Random();

List<DateOnly> randomCoverage = new List<DateOnly>();

for (int i = 0; i < random.Next(10); i++)
{
randomCoverage.Add(DateOnly.FromDateTime(DateTime.UtcNow.AddDays(i)));
}

Schedule randomSchedule = new Schedule
{
Guid = Guid.Empty,
Auth0UserId = "always-test-on-schedule",
DateCoverage = randomCoverage.ToArray(),
StartTime = new TimeOnly(9, 0),
EndTime = new TimeOnly(17, 0),
SchedulePreferences = new SchedulePreferences
{
MinutesPerSlot = 30,
ShiftIntervals = 30,
UsersPerShift = 1,
MaximumShiftDurationMinutes = 120,
MaximumShiftsPerWorker = 1,
},
Name = "Test Schedule",
};

using (var context = new LineUpContext(options))
{
context.Database.EnsureCreated();

var controller = new ScheduleController(context);

// Create a mock ClaimsPrincipal with the required NameIdentifier claim
var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "test-user-123") };
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = principal },
};

var scheduleDto = new ScheduleDto
{
DateCoverage = randomSchedule.DateCoverage,
StartTime = randomSchedule.StartTime,
EndTime = randomSchedule.EndTime,
SchedulePreferences = randomSchedule.SchedulePreferences,
Name = randomSchedule.Name,
};

// Act
var result = await controller.CreateSchedule(scheduleDto);

// Assert
var createdResult = Assert.IsType<CreatedAtActionResult>(result);
Assert.Equal(nameof(ScheduleController.GetSchedule), createdResult.ActionName);
Assert.NotNull(createdResult.Value);

var returnedSchedule = Assert.IsType<Schedule>(createdResult.Value);
Assert.Equal("test-user-123", returnedSchedule.Auth0UserId);
Assert.Equal(randomSchedule.Name, returnedSchedule.Name);
Assert.Equal(randomSchedule.StartTime, returnedSchedule.StartTime);
Assert.Equal(randomSchedule.EndTime, returnedSchedule.EndTime);

// Verify that the schedule was actually saved to the database
var savedSchedules = await context.Schedules.CountAsync();
Assert.Equal(1, savedSchedules);
}
}
}
196 changes: 196 additions & 0 deletions LineUp.Backend.Tests/SwapTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
using System.Net;
using System.Security.Claims;
using Azure;
using LineUp.Backend.Controllers;
using LineUp.Backend.Models;
using LineUp.Core.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Moq;

namespace LineUp.Backend.Tests;

public class SwapTests
{
Schedule sampleSchedule = new Schedule
{
Guid = Guid.Empty,
Auth0UserId = "always-test-on-schedule",
DateCoverage =
[
DateOnly.FromDateTime(DateTime.UtcNow),
DateOnly.FromDateTime(DateTime.UtcNow.AddDays(1)),
DateOnly.FromDateTime(DateTime.UtcNow.AddDays(2)),
],
StartTime = new TimeOnly(9, 0),
EndTime = new TimeOnly(12, 0),
SchedulePreferences = new SchedulePreferences
{
MinutesPerSlot = 30,
ShiftIntervals = 30,
UsersPerShift = 1,
MaximumShiftDurationMinutes = 120,
MaximumShiftsPerWorker = 1,
},
Name = "Test Schedule",
};

Availability sample1 = new Availability
{
UserName = "Test Availability",
UserEmail = "test@email.com",
AvailabilitySlots =
[
// Day 0: 9:00 - 12:00
DateTime.UtcNow.Date.AddHours(9),
DateTime.UtcNow.Date.AddHours(9).AddMinutes(30),
DateTime.UtcNow.Date.AddHours(10),
DateTime.UtcNow.Date.AddHours(10).AddMinutes(30),
DateTime.UtcNow.Date.AddHours(11),
DateTime.UtcNow.Date.AddHours(11).AddMinutes(30),
],
Schedule = new Schedule
{
Guid = Guid.Empty,
Auth0UserId = "replace this schedule",
DateCoverage = [],
StartTime = new TimeOnly(0, 0),
EndTime = new TimeOnly(0, 0),
SchedulePreferences = new SchedulePreferences
{
MinutesPerSlot = 30,
ShiftIntervals = 30,
UsersPerShift = 1,
MaximumShiftDurationMinutes = 120,
MaximumShiftsPerWorker = 1,
},
Name = "ReplaceThisScheduleWithSampleSchedule",
},
Preferences = new AvailabilityPreferences(),
};
Availability sample2 = new Availability
{
UserName = "Test Availability",
UserEmail = "test@email.com",
AvailabilitySlots =
[
// Day 1: 9:00 - 12:00
DateTime.UtcNow.Date.AddDays(1).AddHours(9),
DateTime.UtcNow.Date.AddDays(1).AddHours(9).AddMinutes(30),
DateTime.UtcNow.Date.AddDays(1).AddHours(10),
DateTime.UtcNow.Date.AddDays(1).AddHours(10).AddMinutes(30),
DateTime.UtcNow.Date.AddDays(1).AddHours(11),
DateTime.UtcNow.Date.AddDays(1).AddHours(11).AddMinutes(30),
],
Schedule = new Schedule
{
Guid = Guid.Empty,
Auth0UserId = "replace this schedule",
DateCoverage = [],
StartTime = new TimeOnly(0, 0),
EndTime = new TimeOnly(0, 0),
SchedulePreferences = new SchedulePreferences
{
MinutesPerSlot = 30,
ShiftIntervals = 30,
UsersPerShift = 1,
MaximumShiftDurationMinutes = 120,
MaximumShiftsPerWorker = 1,
},
Name = "ReplaceThisScheduleWithSampleSchedule",
},
Preferences = new AvailabilityPreferences(),
};
Availability sample3 = new Availability
{
UserName = "Test Availability",
UserEmail = "test@email.com",
AvailabilitySlots =
[
// Day 2: 9:00 - 12:00
DateTime.UtcNow.Date.AddDays(2).AddHours(9),
DateTime.UtcNow.Date.AddDays(2).AddHours(9).AddMinutes(30),
DateTime.UtcNow.Date.AddDays(2).AddHours(10),
DateTime.UtcNow.Date.AddDays(2).AddHours(10).AddMinutes(30),
DateTime.UtcNow.Date.AddDays(2).AddHours(11),
DateTime.UtcNow.Date.AddDays(2).AddHours(11).AddMinutes(30),
],
Schedule = new Schedule
{
Guid = Guid.Empty,
Auth0UserId = "replace this schedule",
DateCoverage = [],
StartTime = new TimeOnly(0, 0),
EndTime = new TimeOnly(0, 0),
SchedulePreferences = new SchedulePreferences
{
MinutesPerSlot = 30,
ShiftIntervals = 30,
UsersPerShift = 1,
MaximumShiftDurationMinutes = 120,
MaximumShiftsPerWorker = 1,
},
Name = "ReplaceThisScheduleWithSampleSchedule",
},
Preferences = new AvailabilityPreferences(),
};

//[Fact]
public async Task SwapAccepted_Test()
{
// Arrange
var options = new DbContextOptionsBuilder<LineUpContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;

using (var context = new LineUpContext(options))
{
context.Database.EnsureCreated();

var controller = new ScheduleController(context);

// Create a mock ClaimsPrincipal with the required NameIdentifier claim
var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "test-user-123") };
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = principal },
};

var scheduleDto = new ScheduleDto
{
DateCoverage = sampleSchedule.DateCoverage,
StartTime = sampleSchedule.StartTime,
EndTime = sampleSchedule.EndTime,
SchedulePreferences = sampleSchedule.SchedulePreferences,
Name = sampleSchedule.Name,
};
var result = await controller.CreateSchedule(scheduleDto);
CreatedAtActionResult scheduleCreatedResult = Assert.IsType<CreatedAtActionResult>(
result
);
Schedule returnedSchedule = Assert.IsType<Schedule>(scheduleCreatedResult.Value);
Guid guid = returnedSchedule.Guid;

var availability1Dto = new AvailabilityCreateDto
{
AvailabilitySlots = sample1.AvailabilitySlots,
UserName = sample1.UserName,
UserEmail = sample1.UserEmail,
Preferences = sample1.Preferences,
FormAnswers = sample1.FormAnswers,
};
var availabilityCreateResult = await controller.CreateAvailability(
guid,
availability1Dto
);

// Act

// Assert
Assert.IsType<CreatedAtActionResult>(availabilityCreateResult);
}
}
}
93 changes: 93 additions & 0 deletions LineUp.Backend/Controllers/ScheduleController.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System.Diagnostics;
using System.Security.Claims;
using LineUp.Backend.Models;
using LineUp.Backend.Services;
using LineUp.Core.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NuGet.Protocol;
using SQLitePCL;

namespace LineUp.Backend.Controllers;

Expand Down Expand Up @@ -296,4 +299,94 @@ await context.Availabilities.AnyAsync(a =>
availabilityToInsert
);
}

[HttpGet("{guid:guid}/getByEmail")]
public async Task<IActionResult> GetAvailability(Guid guid, [FromQuery] string email)
{
var result = await context
.Availabilities.Include(a => a.Schedule)
.FirstOrDefaultAsync(a => a.UserEmail == email && a.Schedule.Guid == guid);
if (result != null)
return Ok(result);
return StatusCode(StatusCodes.Status406NotAcceptable);
}

[HttpPost("{guid:guid}/requestSwap")]
public async Task<IActionResult> RequestSwap(Guid guid, [FromBody] SwapRequestDto request)
{
DateTime[] shiftStartTimes = request.shiftStartTimes;
int requesterId = request.RequesterId;
int recipientId = request.RecipientId;

Schedule? schedule = context.Schedules.FirstOrDefault<Schedule>(s => s.Guid == guid);
if (schedule == null || shiftStartTimes == null || !shiftStartTimes.Any())
{
return BadRequest("Request does not specify all necessary fields.");
}
List<ShiftAssignment> requesterShiftCollection = new List<ShiftAssignment>();
List<ShiftAssignment> recipientShiftCollection = new List<ShiftAssignment>();
var scheduleResult = await context.Schedules.FirstOrDefaultAsync(s => s.Guid == guid);

if (scheduleResult == null)
return NotFound("The provided schedule could not be found.");
int scheduleID = scheduleResult.Id;

try
{ //Attempt to find the shift assignments from the backend.
foreach (DateTime start in shiftStartTimes)
{
// Console.WriteLine("\n Finding owner of shift " + start + " ...");
var result = await context.ShiftAssignments.FirstOrDefaultAsync(s =>
s.ScheduleId == scheduleID
&& s.StartTime == start
&& s.Availability.Id == requesterId
); //Attempt to find if the given shift belongs to requester
if (result != null)
{
requesterShiftCollection.Add(result);
// Console.WriteLine("\n Shift found for " + requesterId);
// Console.WriteLine("Look: " + requesterShiftCollection.ToString());
}
else
{
result = await context.ShiftAssignments.FirstOrDefaultAsync(s =>
s.ScheduleId == scheduleID
&& s.StartTime == start
&& s.Availability.Id == recipientId
); //Attempt to find if the given shift belongs to recipient
if (result != null)
{
recipientShiftCollection.Add(result);
// Console.WriteLine("\n Shift found for " + recipientId);
// Console.WriteLine("Look: " + recipientShiftCollection[^1].ToJson);
}
else
{ //If it doesn't belong to either, throw an exception
// Console.WriteLine("\n shift aint go nowhere hon");
throw new FileNotFoundException();
}
}
}
}
catch (FileNotFoundException)
{
return UnprocessableEntity(
"One or more of the dates provided did not have a shift assigned to either party."
);
}
if (requesterShiftCollection.Count < 1 && recipientShiftCollection.Count < 1)
return UnprocessableEntity("No shift assignments were found for the time specified.");

//Sort through the shifts (assume an unsorted list)
SwapRequest swapRequest = new SwapRequest
{
FromPartyA = requesterShiftCollection,
FromPartyB = recipientShiftCollection,
Schedule = schedule,
};
Console.WriteLine("\n Creating Swap Request: " + swapRequest.ToJson());
context.SwapRequests.Add(swapRequest);
await context.SaveChangesAsync();
return Ok(swapRequest.Guid);
}
}
Loading