-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✅ test(add job test): add tests for the add job cmd
update job and resumeContext class for tests. Add updated check in qodana analysis Signed-off-by: csc530 <[email protected]>
- Loading branch information
Showing
15 changed files
with
340 additions
and
91 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using resume_builder; | ||
using resume_builder.models; | ||
using Spectre.Console.Testing; | ||
|
||
namespace TestResumeBuilder | ||
{ | ||
//todo: find way to pass text to test command app for prompts | ||
public abstract class TestBase: IDisposable, IAsyncDisposable | ||
{ | ||
internal CommandAppTester TestApp; | ||
internal ResumeContext TestDb; | ||
|
||
public TestBase() | ||
{ | ||
//given | ||
TestApp = new CommandAppTester(); | ||
TestApp.Configure(Program.AppConfiguration); | ||
TestDb = new ResumeContext(); | ||
} | ||
|
||
public async ValueTask DisposeAsync() | ||
{ | ||
TestDb.Jobs.RemoveRange(TestDb.Jobs); | ||
TestDb.Projects.RemoveRange(TestDb.Projects); | ||
TestDb.Profiles.RemoveRange(TestDb.Profiles); | ||
TestDb.Companies.RemoveRange(TestDb.Companies); | ||
TestDb.Skills.RemoveRange(TestDb.Skills); | ||
await TestDb.SaveChangesAsync(); | ||
await TestDb.DisposeAsync(); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
TestDb.Jobs.RemoveRange(TestDb.Jobs); | ||
TestDb.Projects.RemoveRange(TestDb.Projects); | ||
TestDb.Profiles.RemoveRange(TestDb.Profiles); | ||
TestDb.Companies.RemoveRange(TestDb.Companies); | ||
TestDb.Skills.RemoveRange(TestDb.Skills); | ||
TestDb.SaveChanges(); | ||
TestDb.Dispose(); | ||
|
||
GC.SuppressFinalize(this); | ||
} | ||
|
||
protected CommandAppResult Run(IEnumerable<string> cmdArgs, params string[] args) => | ||
TestApp.Run(cmdArgs.Concat(args).ToArray()); | ||
|
||
protected CommandAppResult Run(string cmd, params string[] args) => | ||
Run(args.Prepend(cmd)); | ||
|
||
protected CommandAppFailure RunAndCatch<T>(IEnumerable<string> cmdArgs, params string[] args) | ||
where T : Exception => TestApp.RunAndCatch<T>(cmdArgs.Concat(args).ToArray()); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,27 @@ | ||
using resume_builder.models; | ||
|
||
namespace TestResumeBuilder.commands; | ||
|
||
public class InitTest : AppTestBase | ||
public class InitTest: TestBase | ||
{ | ||
[Fact] | ||
public void Init_WithNoArgs_ShouldReturnSuccess() | ||
{ | ||
//when | ||
var result = TestApp.Run("init"); | ||
//then | ||
Assert.Equal(0, result.ExitCode); | ||
} | ||
|
||
|
||
[Theory] | ||
[MemberData(nameof(TestData.RandomArgs), MemberType = typeof(TestData))] | ||
public void Init_WithArgs_ShouldReturnSuccess(string[] args) | ||
[Fact] | ||
public void Init_WithNoArgs_ShouldCreateDb() | ||
{ | ||
Assert.True(true); | ||
//var result = Run("init", args); | ||
//Assert.That(result.ExitCode, Is.EqualTo(0)); | ||
//when | ||
var result = TestApp.Run("init"); | ||
//then | ||
Assert.True(File.Exists("TestResumeBuilder.db")); | ||
Assert.Equal(0, result.ExitCode); | ||
TestDb = new ResumeContext(); | ||
Assert.True(TestDb.Database.CanConnect()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
using Bogus; | ||
using resume_builder; | ||
using resume_builder.cli.commands.add; | ||
using resume_builder.models; | ||
|
||
namespace TestResumeBuilder.commands.add; | ||
|
||
public class AddJobTest: TestBase | ||
{ | ||
static readonly string[] cmdArgs = ["add", "job"]; | ||
|
||
public static readonly TheoryData<string, string, DateOnly, string?, string?, DateOnly?> AddJobData = | ||
new() | ||
{ | ||
{ | ||
"jobTitle", "companyName", new DateOnly(2021, 1, 1), "jobDescription", "experience", | ||
new DateOnly(2231, 1, 1) | ||
} | ||
}; | ||
|
||
[Fact(Skip = | ||
"hangs forever (at least in vs2022) because it's expecting input in interactive mode which can't be emulated right now with its (spectre) test console")] | ||
public void AddJob_WithNoArgs_ShouldFail() | ||
{ | ||
//this hangs forever becuase a prompt is required | ||
//and I have no idea how to inject a response to the app | ||
//I added a timeout so it sould stop | ||
var result = TestApp.Run(cmdArgs); | ||
Assert.Equal(ExitCode.Error.ToInt(), result.ExitCode); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(AddJobTestData.JobTitleAndStartDate), MemberType = typeof(AddJobTestData))] | ||
public void AddJob_WithNameAndStartOptions_ShouldSucceed(string jobTitle, string companyName, DateOnly startDate) | ||
{ | ||
//when | ||
var result = TestApp.Run([.. cmdArgs, "-t", jobTitle, "-s", startDate.ToString(), "-c", companyName]); | ||
var resultSettings = result.Settings as AddJobSettings; | ||
//then | ||
Assert.Multiple(() => | ||
{ | ||
Assert.Equal(0, result.ExitCode); | ||
Assert.NotNull(resultSettings); | ||
Assert.Equal(jobTitle, resultSettings.JobTitle); | ||
Assert.Equal(companyName, resultSettings.Company); | ||
Assert.Equal(startDate, resultSettings.StartDate); | ||
}); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(AddJobTestData.AllOptions), MemberType = typeof(AddJobTestData))] | ||
public void AddJob_WithAllOptions_ShouldSucceed(string jobTitle, string companyName, DateOnly startDate, | ||
string? jobDescription, string? experience, DateOnly? endDate) | ||
{ | ||
//? job description and experience are non-null because in the run it will be parsed/replaced with an empty string | ||
// and users can't enter the null value | ||
//which is to say they can choose to enter nothing with empty quotes which this test tests for too | ||
//when | ||
string[]? descriptionArg = jobDescription == null ? [null] : ["-d", $"{jobDescription}"]; | ||
string[]? expArg = experience == null ? [null] : ["-x", $"{experience}"]; | ||
string[]? endArg = endDate == null ? [null] : ["-e", $"{endDate}"]; | ||
List<string?> args = | ||
[ | ||
..cmdArgs, ..descriptionArg, ..expArg, ..endArg, "-t", jobTitle, "-s", startDate.ToString(), "-c", | ||
companyName | ||
]; | ||
var result = Run(args.Where(x => x != null).ToArray()!); | ||
var resultSettings = result.Settings as AddJobSettings; | ||
//then | ||
Assert.Equal(0, result.ExitCode); | ||
Assert.NotNull(resultSettings); | ||
Assert.Equal(jobTitle, resultSettings.JobTitle); | ||
Assert.Equal(companyName, resultSettings.Company); | ||
Assert.Equal(startDate, resultSettings.StartDate); | ||
Assert.Equal(jobDescription, resultSettings.JobDescription); | ||
Assert.Equal(experience, resultSettings.Experience); | ||
Assert.Equal(endDate, resultSettings.EndDate); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(AddJobData))] | ||
[MemberData(nameof(AddJobTestData.AllOptions), MemberType = typeof(AddJobTestData))] | ||
public void AddJob_WithAllOptions_ShouldUpdateDB(string jobTitle, string companyName, DateOnly startDate, | ||
string? jobDescription, string? experience, DateOnly? endDate) | ||
{ | ||
string[] descriptionArg = jobDescription == null ? [null] : [$"-d", $"{jobDescription}"]; | ||
string[] expArg = experience == null ? [null] : [$"-x", $"{experience}"]; | ||
string[] endArg = endDate == null ? [null] : [$"-e", $"{endDate}"]; | ||
string?[] args = | ||
[ | ||
..cmdArgs, "-t", jobTitle, "-s", startDate.ToString(), "-c", companyName, ..descriptionArg, ..expArg, | ||
..endArg | ||
]; | ||
var result = TestApp.Run(args.Where(x => x != null).ToArray()!); | ||
Assert.Equal(0, result.ExitCode); | ||
var job = TestDb.Jobs.First(j => j.Title == jobTitle && j.Company == companyName && j.StartDate == startDate); | ||
Assert.NotNull(job); | ||
//clean inputs; trim and nullify empty strings | ||
jobDescription = jobDescription?.Trim(); | ||
companyName = companyName.Trim(); | ||
experience = experience?.Trim(); | ||
jobTitle = jobTitle.Trim(); | ||
|
||
|
||
Assert.Equal(jobTitle, job!.Title); | ||
Assert.Equal(companyName, job.Company); | ||
Assert.Equal(startDate, job.StartDate); | ||
Assert.Equal(jobDescription.IsBlank() ? null : jobDescription, job.Description); | ||
Assert.Equal(experience.IsBlank() ? null : experience, job.Experience); | ||
Assert.Equal(endDate, job.EndDate); | ||
} | ||
} | ||
|
||
internal class AddJobTestData: JobTestData | ||
{ | ||
public static TheoryData<string, string, DateOnly> JobTitleAndStartDate() | ||
{ | ||
var data = new TheoryData<string, string, DateOnly>(); | ||
for(var i = TestRepetitions - 1; i >= 0; i--) | ||
data.Add(RandomJobTitle(), RandomCompany(), RandomPastDate()); | ||
return data; | ||
} | ||
|
||
public static TheoryData<string, string, DateOnly, string?, string?, DateOnly?> AllOptions() | ||
{ | ||
var data = new TheoryData<string, string, DateOnly, string?, string?, DateOnly?>(); | ||
for(var i = TestRepetitions - 1; i >= 0; i--) | ||
data.Add(RandomJobTitle(), RandomCompany(), RandomPastDate(), WaffleOrNull(), WaffleOrNull(), | ||
RandomFutureDate().OrNull(Faker)); | ||
return data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using Bogus; | ||
using resume_builder.models; | ||
|
||
namespace TestResumeBuilder; | ||
|
||
internal class JobTestData: TestData | ||
{ | ||
protected static Faker<Job> BogusJob { get; set; } = new Faker<Job>() | ||
.RuleFor(job => job.Title, RandomJobTitle) | ||
.RuleFor(job => job.Description, RandomTextOrNull) | ||
.RuleFor(job => job.Experience, RandomTextOrNull) | ||
.RuleFor(job => job.Company, RandomCompany) | ||
.RuleFor(job => job.StartDate, RandomPastDate) | ||
.RuleFor(job => job.EndDate, RandomEndDate); | ||
|
||
public static string RandomJobTitle() => Faker.Name.JobTitle(); | ||
public static string RandomCompany() => Faker.Company.CompanyName(); | ||
public static DateOnly? RandomEndDate() => RandomPastDate().OrNull(Faker); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using Bogus; | ||
using WaffleGenerator; | ||
|
||
namespace TestResumeBuilder; | ||
|
||
internal class TestData | ||
{ | ||
private protected const int TestRepetitions = 10; | ||
|
||
|
||
private static Random Random { get; set; } = new(); | ||
protected static Faker Faker { get; set; } = new(); | ||
|
||
static private protected int MaxRandomYearsAfterToday() | ||
{ | ||
DateOnly.MaxValue.Deconstruct(out var maxYear, out _, out _); | ||
DateOnly.FromDateTime(DateTime.Today).Deconstruct(out var todayYear, out _, out _); | ||
return maxYear - todayYear; | ||
} | ||
|
||
static private protected int MaxRandomYearsBeforeToday() | ||
{ | ||
DateOnly.MinValue.Deconstruct(out var minYear, out _, out _); | ||
DateOnly.FromDateTime(DateTime.Today).Deconstruct(out var todayYear, out _, out _); | ||
return todayYear - minYear; | ||
} | ||
|
||
public static TheoryData<object[]> RandomArgs() => [Faker.Random.WordsArray(2)]; | ||
|
||
|
||
public static DateOnly RandomPastDate() => Faker.Date.PastDateOnly(MaxRandomYearsBeforeToday()); | ||
public static DateOnly RandomFutureDate() => Faker.Date.FutureDateOnly(MaxRandomYearsAfterToday()); | ||
public static DateOnly RandomDate() => Faker.Date.BetweenDateOnly(RandomPastDate(), RandomFutureDate()); | ||
public static string? RandomTextOrNull() => Faker.Lorem.Paragraph().OrNull(Faker); | ||
|
||
public static string Waffle() => WaffleEngine.Text(Random.Next(TestRepetitions), Faker.Random.Bool()); | ||
public static string? WaffleOrNull() => Waffle().OrNull(Faker); | ||
} |
Oops, something went wrong.