Skip to content

Commit f982a2d

Browse files
author
Brian Cummings
committed
Make email field nullable on Pathfinder model
- Updated Pathfinder model to make Email property nullable - Removed [Required] attribute from Email in DTOs - Updated PathfinderValidator to only validate email format when provided - Added database migration to make email column nullable and enforce first_name/last_name as required - Updated validator tests to reflect email is now optional
1 parent af4b0fd commit f982a2d

File tree

10 files changed

+574
-91
lines changed

10 files changed

+574
-91
lines changed

PathfinderHonorManager.Tests/Service/PathfinderServiceTests.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,12 @@ public async Task GetByIdAsync_ReturnsPathfinderById(string clubCode)
138138
public async Task AddAsync_AddsNewPathfinderAndReturnsDto(string clubCode)
139139
{
140140
// Arrange
141-
var newPathfinderDto = new Incoming.PathfinderDto { /* Populate required properties */ };
141+
var newPathfinderDto = new Incoming.PathfinderDto
142+
{
143+
FirstName = "Test",
144+
LastName = "Pathfinder",
145+
Email = "test.pathfinder@example.com"
146+
};
142147
var cancellationToken = new CancellationToken();
143148

144149
// Act

PathfinderHonorManager.Tests/Validator/PathfinderValidatorTests.cs

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using NUnit.Framework;
77
using PathfinderHonorManager.DataAccess;
88
using PathfinderHonorManager.Model;
9+
using PathfinderHonorManager.Tests.Helpers;
910
using PathfinderHonorManager.Validators;
1011
using Incoming = PathfinderHonorManager.Dto.Incoming;
1112

@@ -27,17 +28,18 @@ public PathfinderValidatorTests()
2728
protected DbContextOptions<PathfinderContext> ContextOptions { get; }
2829

2930
[SetUp]
30-
public void SetUp()
31+
public async Task SetUp()
3132
{
3233
var context = new PathfinderContext(ContextOptions);
34+
await DatabaseCleaner.CleanDatabase(context);
3335
_pathfinderValidator = new PathfinderValidator(context);
34-
SeedDatabase(context);
36+
await DatabaseSeeder.SeedDatabase(ContextOptions);
3537
}
3638

37-
// Email tests
38-
[TestCase("")]
39-
[TestCase(null)]
39+
// Email tests - invalid format should fail
4040
[TestCase("nonemailstring")]
41+
[TestCase("invalid@")]
42+
[TestCase("@invalid.com")]
4143
public async Task Validate_InvalidEmail_ValidationError(string email)
4244
{
4345
var newPathfinder = new Incoming.PathfinderDtoInternal
@@ -58,12 +60,32 @@ public async Task Validate_InvalidEmail_ValidationError(string email)
5860

5961
}
6062

63+
// Email tests - empty/null emails should be allowed
64+
[TestCase("")]
65+
[TestCase(null)]
66+
public async Task Validate_EmptyOrNullEmail_ShouldPass(string email)
67+
{
68+
var newPathfinder = new Incoming.PathfinderDtoInternal
69+
{
70+
FirstName = "test",
71+
LastName = "user",
72+
Email = email
73+
};
74+
75+
var validationResult = await _pathfinderValidator
76+
.TestValidateAsync(newPathfinder, options =>
77+
{
78+
options.IncludeAllRuleSets();
79+
});
80+
81+
validationResult.ShouldNotHaveValidationErrorFor(p => p.Email);
82+
}
83+
6184
// FirstName tests
6285
[TestCase("")]
6386
[TestCase(null)]
6487
public async Task Validate_FirstName_ValidationError(string firstName)
6588
{
66-
var randEmail = RandomString(10);
6789
var newPathfinder = new Incoming.PathfinderDtoInternal
6890
{
6991
FirstName = firstName,
@@ -271,6 +293,23 @@ public async Task Validate_AllRulesApplied_ValidationError()
271293
validationResult.ShouldHaveValidationErrorFor(p => p.ClubID);
272294
}
273295

296+
[Test]
297+
public async Task Validate_ValidEmail_ShouldPass()
298+
{
299+
var newPathfinder = new Incoming.PathfinderDtoInternal
300+
{
301+
FirstName = "Test",
302+
LastName = "User",
303+
Email = "valid@example.com",
304+
ClubID = Guid.NewGuid()
305+
};
306+
307+
var validationResult = await _pathfinderValidator
308+
.TestValidateAsync(newPathfinder, options => options.IncludeRuleSets("post"));
309+
310+
validationResult.ShouldNotHaveValidationErrorFor(p => p.Email);
311+
}
312+
274313
[Test]
275314
public async Task Validate_InvalidClubId_ValidationError()
276315
{
@@ -355,58 +394,15 @@ public async Task Validate_EmptyGuidClubId_ShouldPass()
355394
validationResult.ShouldNotHaveValidationErrorFor(p => p.ClubID);
356395
}
357396

358-
public static void SeedDatabase(PathfinderContext context)
359-
{
360-
context.Database.EnsureDeleted();
361-
context.Database.EnsureCreated();
362-
363-
context.Pathfinders.AddRange(
364-
new Pathfinder[]
365-
{
366-
367-
new Pathfinder
368-
{
369-
FirstName = "test1",
370-
LastName = "pathfinder",
371-
Email = "test1@validemail.com",
372-
Created = System.DateTime.Now,
373-
Updated = System.DateTime.Now
374-
},
375-
new Pathfinder
376-
{
377-
FirstName = "test2",
378-
LastName = "pathfinder",
379-
Email = "test2@validemail.com",
380-
Created = System.DateTime.Now,
381-
Updated = System.DateTime.Now
382-
},
383-
new Pathfinder
384-
{
385-
FirstName = "test3",
386-
LastName = "pathfinder",
387-
Email = "test3@validemail.com",
388-
Created = System.DateTime.Now,
389-
Updated = System.DateTime.Now
390-
}
391-
}); ;
392-
393-
context.SaveChangesAsync();
394-
}
395-
public static string RandomString(int length)
397+
[TearDown]
398+
public async Task TearDown()
396399
{
397-
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
398-
var stringChars = new char[length];
399-
var random = new Random();
400-
401-
for (int i = 0; i < stringChars.Length; i++)
400+
using (var context = new PathfinderContext(ContextOptions))
402401
{
403-
stringChars[i] = chars[random.Next(chars.Length)];
402+
await DatabaseCleaner.CleanDatabase(context);
404403
}
405-
406-
var finalString = new String(stringChars);
407-
408-
return finalString;
409404
}
405+
410406
}
411407
}
412408

PathfinderHonorManager/DataAccess/PathfinderDBContext.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
3535
.HasForeignKey(p => p.ClubID)
3636
.OnDelete(DeleteBehavior.Restrict);
3737

38+
modelBuilder.Entity<Pathfinder>()
39+
.Property(p => p.FirstName)
40+
.IsRequired();
41+
42+
modelBuilder.Entity<Pathfinder>()
43+
.Property(p => p.LastName)
44+
.IsRequired();
45+
46+
modelBuilder.Entity<Pathfinder>()
47+
.Property(p => p.Email)
48+
.IsRequired(false);
49+
3850
// Pathfinder -> PathfinderClass: Restrict (nullable relationship already)
3951
modelBuilder.Entity<Pathfinder>()
4052
.HasOne(p => p.PathfinderClass)

PathfinderHonorManager/Dto/Incoming/PathfinderDto.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
#nullable enable
2+
using System;
23
using System.Collections.Generic;
34
using System.ComponentModel.DataAnnotations;
45
using System.Diagnostics.CodeAnalysis;
@@ -9,13 +10,12 @@ namespace PathfinderHonorManager.Dto.Incoming
910
public class PathfinderDto
1011
{
1112
[Required]
12-
public string FirstName { get; set; }
13+
public string FirstName { get; set; } = null!;
1314

1415
[Required]
15-
public string LastName { get; set; }
16+
public string LastName { get; set; } = null!;
1617

17-
[Required]
18-
public string Email { get; set; }
18+
public string? Email { get; set; }
1919

2020
public int? Grade { get; set; }
2121

@@ -43,7 +43,7 @@ public class PutPathfinderDto
4343
public class BulkPutPathfinderDto
4444
{
4545
[Required]
46-
public IEnumerable<BulkPutPathfinderItemDto> Items { get; set; }
46+
public IEnumerable<BulkPutPathfinderItemDto> Items { get; set; } = null!;
4747
}
4848

4949
[ExcludeFromCodeCoverage]

PathfinderHonorManager/Dto/Outgoing/PathfinderDto.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class PathfinderDto
1414

1515
public string LastName { get; set; } = null!;
1616

17-
public string Email { get; set; } = null!;
17+
public string? Email { get; set; }
1818

1919
public int? Grade { get; set; }
2020

@@ -36,7 +36,7 @@ public class PathfinderDependantDto
3636

3737
public string LastName { get; set; } = null!;
3838

39-
public string Email { get; set; } = null!;
39+
public string? Email { get; set; }
4040

4141
public int? Grade { get; set; }
4242

0 commit comments

Comments
 (0)