The clash penalty applied to every pair except defender looking at the victim, so the person being helped disliked the defender like a second enemy. Co-authored-by: Cursor <cursoragent@cursor.com>
238 lines
9.2 KiB
C#
238 lines
9.2 KiB
C#
namespace HSchool.People.Tests;
|
||
|
||
public class AffinityTests
|
||
{
|
||
private static readonly DateTime AsOf = new(2012, 3, 31, 6, 0, 0, DateTimeKind.Utc);
|
||
|
||
[Fact]
|
||
public void SixteenYearOlds_NeverPair_AtAnyOpinion()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var boy = Pupil("a", year: 10, age: 16, female: false, "Bisexual");
|
||
var girl = Pupil("b", year: 10, age: 16, female: true, "Bisexual");
|
||
SetOpinion(boy, girl.Id, 100);
|
||
SetOpinion(girl, boy.Id, 100);
|
||
var roster = RosterOf(boy, girl, classYear: 10);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
|
||
Assert.Contains("b", boy.Bonds!.Crushes);
|
||
Assert.Contains("a", girl.Bonds!.Crushes);
|
||
Assert.Null(boy.Bonds.PartnerId);
|
||
Assert.Null(girl.Bonds.PartnerId);
|
||
Assert.False(Affinity.CanFormPair(catalog, roster, boy, girl, AsOf));
|
||
}
|
||
|
||
[Fact]
|
||
public void FirstYear_DoesNotCrushEleventh()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var young = Pupil("c1", year: 1, age: 7, female: true, "Bisexual", classId: "y1");
|
||
var old = Pupil("c11", year: 11, age: 17, female: false, "Bisexual", classId: "y11");
|
||
SetOpinion(young, old.Id, 100);
|
||
var roster = new Roster(
|
||
[young, old],
|
||
[],
|
||
[
|
||
new SchoolClass("y1", 1, "А", "r1", 16, [young.Id]),
|
||
new SchoolClass("y11", 11, "А", "r11", 16, [old.Id]),
|
||
]);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
|
||
Assert.DoesNotContain("c11", young.Bonds!.Crushes);
|
||
Assert.False(Affinity.CanHaveSympathy(catalog, roster, young, old, AsOf));
|
||
}
|
||
|
||
[Fact]
|
||
public void Staff_NeverGetsCrushOnStudent()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var teacher = Staff("t", female: false, "Bisexual");
|
||
var pupil = Pupil("p", year: 8, age: 14, female: true, "Bisexual");
|
||
SetOpinion(teacher, pupil.Id, 100);
|
||
SetOpinion(pupil, teacher.Id, 100);
|
||
var roster = RosterOf(teacher, pupil, classYear: 8);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
|
||
Assert.DoesNotContain(pupil.Id, teacher.Bonds!.Crushes);
|
||
Assert.Contains(teacher.Id, pupil.Bonds!.Crushes);
|
||
Assert.Null(teacher.Bonds.PartnerId);
|
||
Assert.Null(pupil.Bonds.PartnerId);
|
||
Assert.False(Affinity.CanHaveSympathy(catalog, roster, teacher, pupil, AsOf));
|
||
Assert.True(Affinity.CanHaveSympathy(catalog, roster, pupil, teacher, AsOf));
|
||
}
|
||
|
||
[Fact]
|
||
public void StaffPair_IsAllowedWhenBothAdults()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var left = Staff("s1", female: false, "Bisexual");
|
||
var right = Staff("s2", female: true, "Bisexual");
|
||
SetOpinion(left, right.Id, 80);
|
||
SetOpinion(right, left.Id, 80);
|
||
var roster = new Roster([left, right], [], []);
|
||
var events = Affinity.Refresh(catalog, roster, AsOf);
|
||
|
||
Assert.Equal(right.Id, left.Bonds!.PartnerId);
|
||
Assert.Equal(left.Id, right.Bonds!.PartnerId);
|
||
Assert.Contains(events, row => row.Type == Affinity.Pair);
|
||
}
|
||
|
||
[Fact]
|
||
public void PairBreak_BlocksNewPairUntilNextDay()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var left = Staff("s1", female: false, "Bisexual");
|
||
var right = Staff("s2", female: true, "Bisexual");
|
||
var third = Staff("s3", female: true, "Bisexual");
|
||
SetOpinion(left, right.Id, 80);
|
||
SetOpinion(right, left.Id, 80);
|
||
var roster = new Roster([left, right, third], [], []);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
Assert.Equal(right.Id, left.Bonds!.PartnerId);
|
||
|
||
SetOpinion(left, right.Id, 0);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
Assert.Null(left.Bonds.PartnerId);
|
||
Assert.NotNull(left.Bonds.PairEndedOn);
|
||
|
||
SetOpinion(left, third.Id, 80);
|
||
SetOpinion(third, left.Id, 80);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
Assert.Null(left.Bonds.PartnerId);
|
||
|
||
Affinity.Refresh(catalog, roster, AsOf.AddDays(1));
|
||
Assert.Equal(third.Id, left.Bonds.PartnerId);
|
||
}
|
||
|
||
[Fact]
|
||
public void PupilTalkWithTeacherCrush_WritesRebuffAndDropsOpinion()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var teacher = Staff("t", female: false, "Bisexual");
|
||
var pupil = Pupil("p", year: 8, age: 14, female: true, "Bisexual");
|
||
SetOpinion(pupil, teacher.Id, 80);
|
||
var roster = RosterOf(teacher, pupil, classYear: 8);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
Assert.Contains(teacher.Id, pupil.Bonds!.Crushes);
|
||
var before = OpinionStore.Get(pupil, teacher.Id) ?? 0;
|
||
|
||
var events = Affinity.RebuffTalk(catalog, roster, [pupil.Id, teacher.Id], AsOf);
|
||
|
||
Assert.Contains(events, row => row.Type == Affinity.Rebuff && row.PersonId == pupil.Id);
|
||
Assert.True((OpinionStore.Get(pupil, teacher.Id) ?? 0) < before);
|
||
}
|
||
|
||
[Fact]
|
||
public void RosterJson_RoundTripsCrushesAndPartner()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var left = Staff("s1", female: false, "Bisexual");
|
||
var right = Staff("s2", female: true, "Bisexual");
|
||
SetOpinion(left, right.Id, 80);
|
||
SetOpinion(right, left.Id, 80);
|
||
var roster = new Roster([left, right], [], []);
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
Assert.Equal(right.Id, left.Bonds!.PartnerId);
|
||
|
||
var json = RosterJson.Serialize(RosterDocument.From(1, roster));
|
||
Assert.Contains("\"partnerId\"", json, StringComparison.Ordinal);
|
||
Assert.Contains("\"crushes\"", json, StringComparison.Ordinal);
|
||
var loaded = RosterJson.Parse(json).ToRoster();
|
||
var loadedLeft = loaded.People.First(person => person.Id.Equals(left.Id, StringComparison.Ordinal));
|
||
var loadedRight = loaded.People.First(person => person.Id.Equals(right.Id, StringComparison.Ordinal));
|
||
Assert.Equal(right.Id, loadedLeft.Bonds?.PartnerId);
|
||
Assert.Equal(left.Id, loadedRight.Bonds?.PartnerId);
|
||
Assert.Contains(right.Id, loadedLeft.Bonds!.Crushes);
|
||
Assert.Contains(left.Id, loadedRight.Bonds!.Crushes);
|
||
}
|
||
|
||
[Fact]
|
||
public void AdjacentYearStudents_CanHaveSympathy()
|
||
{
|
||
var catalog = Fixtures.RomanceCatalog();
|
||
var younger = Pupil("y", year: 9, age: 15, female: true, "Bisexual", classId: "c9");
|
||
var older = Pupil("o", year: 10, age: 18, female: false, "Bisexual", classId: "c10");
|
||
SetOpinion(younger, older.Id, 80);
|
||
var roster = new Roster(
|
||
[younger, older],
|
||
[],
|
||
[
|
||
new SchoolClass("c9", 9, "А", "r9", 16, [younger.Id]),
|
||
new SchoolClass("c10", 10, "А", "r10", 16, [older.Id]),
|
||
]);
|
||
Assert.True(Affinity.CanHaveSympathy(catalog, roster, younger, older, AsOf));
|
||
Affinity.Refresh(catalog, roster, AsOf);
|
||
Assert.Contains(older.Id, younger.Bonds!.Crushes);
|
||
}
|
||
|
||
private static void SetOpinion(Person person, string targetId, int value)
|
||
{
|
||
if (value == 0)
|
||
{
|
||
OpinionStore.Remove(person, targetId);
|
||
return;
|
||
}
|
||
|
||
OpinionStore.Set(person, targetId, value);
|
||
}
|
||
|
||
private static Roster RosterOf(Person first, Person second, int classYear)
|
||
{
|
||
var pupils = new[] { first, second }.Where(person => person.IsStudent).Select(person => person.Id).ToArray();
|
||
return new Roster(
|
||
[first, second],
|
||
[],
|
||
[new SchoolClass("c", classYear, "А", "room", 16, pupils)]);
|
||
}
|
||
|
||
private static Person Pupil(string id, int year, int age, bool female, string orientation, string? classId = null)
|
||
{
|
||
_ = year;
|
||
var birth = AsOf.AddYears(-age);
|
||
return AdultLike(id, female, orientation, isStudent: true, isStaff: false, birth, classId ?? "c");
|
||
}
|
||
|
||
private static Person Staff(string id, bool female, string orientation) =>
|
||
AdultLike(id, female, orientation, isStudent: false, isStaff: true, AsOf.AddYears(-30), classId: null);
|
||
|
||
private static Person AdultLike(
|
||
string id,
|
||
bool female,
|
||
string orientation,
|
||
bool isStudent,
|
||
bool isStaff,
|
||
DateTime birth,
|
||
string? classId)
|
||
{
|
||
var cases = new CaseTable
|
||
{
|
||
Nom = id,
|
||
Gen = id,
|
||
Dat = id,
|
||
Acc = id,
|
||
Ins = id,
|
||
Pre = id,
|
||
};
|
||
return new Person
|
||
{
|
||
Id = id,
|
||
FamilyId = "f",
|
||
Female = female,
|
||
BirthDate = DateTime.SpecifyKind(birth, DateTimeKind.Utc),
|
||
Name = new PersonName(id, id, id, cases, cases, cases),
|
||
IsStudent = isStudent,
|
||
IsStaff = isStaff,
|
||
IsParent = false,
|
||
ClassId = classId,
|
||
Numbers = new Dictionary<string, int>(StringComparer.Ordinal),
|
||
Choices = new Dictionary<string, string>(StringComparer.Ordinal),
|
||
Skills = new Dictionary<string, int>(StringComparer.Ordinal),
|
||
Traits = [],
|
||
Needs = new Dictionary<string, float>(StringComparer.Ordinal),
|
||
Opinions = new Dictionary<string, int>(StringComparer.Ordinal),
|
||
Orientation = orientation,
|
||
Bonds = new PersonBonds(),
|
||
};
|
||
}
|
||
}
|