using System; using JobTrackerApi.Models; using JobTrackerApi.Services; using Xunit; namespace JobTrackerApi.Tests; /// /// Guards the rule that matters most after Phase 0 added pre-application stages: a job the user /// has not applied to must never be chased for a reply or auto-ghosted. Nothing was submitted, /// so there is nobody to follow up with and nobody to be ghosted by. /// public sealed class RulesEngineProspectTests { private static readonly RuleSettings AggressiveSettings = new() { Id = 1, AppliedFollowUpDays = 1, AppliedGhostDays = 2, OfferFollowUpDays = 1, OfferGhostDays = 2, FeedbackFollowUpDays = 1, FeedbackGhostDays = 2, }; private static readonly DateTime Now = new(2026, 7, 17, 12, 0, 0, DateTimeKind.Utc); [Theory] [InlineData("Saved")] [InlineData("Interested")] [InlineData("Preparing")] public void A_prospect_is_never_followed_up_or_ghosted_however_old(string status) { // Saved a year ago with thresholds of 1-2 days: an unguarded rule would ghost this. var job = new JobApplication { Status = status, DateApplied = null, SavedAt = Now.AddDays(-365), }; var decision = RulesEngine.Evaluate(AggressiveSettings, job, Now, lastMessageAt: null); Assert.False(decision.NeedsFollowUp); Assert.False(decision.ShouldGhost); } [Fact] public void An_applied_job_with_no_applied_date_is_not_ghosted() { // Shouldn't happen (SyncAppliedDate stamps on the way into Applied), but a null must fail // safe rather than be read as "infinitely old" and silently ghost the job. var job = new JobApplication { Status = "Applied", DateApplied = null, SavedAt = Now.AddDays(-365), }; var decision = RulesEngine.Evaluate(AggressiveSettings, job, Now, lastMessageAt: null); Assert.False(decision.NeedsFollowUp); Assert.False(decision.ShouldGhost); } [Fact] public void An_applied_job_past_the_threshold_still_ghosts() { // The guards above must not have disabled the actual rule. var job = new JobApplication { Status = "Applied", DateApplied = Now.AddDays(-30), SavedAt = Now.AddDays(-31), }; var decision = RulesEngine.Evaluate(AggressiveSettings, job, Now, lastMessageAt: null); Assert.True(decision.NeedsFollowUp); Assert.True(decision.ShouldGhost); } }