Merge branch 'phase/47-romance-pack'
# Conflicts: # docs/phases/README.md # src/HSchool.Content/PeopleDefs.cs # src/HSchool.People/Seed.cs
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace HSchool.AppHost.Tests;
|
||||
|
||||
[Collection(AppHostCollection.Name)]
|
||||
public class RomancePackTests(AppHostFixture fixture)
|
||||
{
|
||||
private static readonly DateTime Start = new(2012, 3, 31, 6, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
[Fact]
|
||||
public async Task Catalog_WithoutRomance_HasNoRomanticTopics()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.LoginAsync(client);
|
||||
|
||||
var vanilla = await client.GetFromJsonAsync<CatalogResponse>("/api/catalog?lang=ru", TestContext.Current.CancellationToken);
|
||||
var example = await client.GetFromJsonAsync<CatalogResponse>(
|
||||
"/api/catalog?lang=ru&mods=example",
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(vanilla);
|
||||
Assert.NotNull(example);
|
||||
Assert.DoesNotContain(vanilla.Topics, topic => topic.DefName is "TopicCrush" or "TopicCouple" or "TopicTease");
|
||||
Assert.DoesNotContain(vanilla.Topics, topic => topic.Tags.Contains("romance", StringComparer.Ordinal));
|
||||
Assert.DoesNotContain(example.Topics, topic => topic.DefName is "TopicCrush" or "TopicCouple" or "TopicTease");
|
||||
Assert.DoesNotContain(example.Topics, topic => topic.Tags.Contains("romance", StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Catalog_WithRomance_AddsPackTopics()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.LoginAsync(client);
|
||||
var catalog = await client.GetFromJsonAsync<CatalogResponse>(
|
||||
"/api/catalog?lang=ru&mods=romance",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(catalog);
|
||||
Assert.Contains(catalog.Topics, topic => topic.DefName == "TopicCrush");
|
||||
Assert.Contains(catalog.Topics, topic => topic.Tags.Contains("romance", StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Create_WithPack_WritesOrientation_WithoutPack_OmitsField()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.ResetAsync(client);
|
||||
|
||||
var vanilla = await SchoolApiTests.CreateAsync(client, "Без романтики", Start, seed: 47);
|
||||
var vanillaJson = await ReadPeopleJsonAsync(client, vanilla.Id);
|
||||
Assert.DoesNotContain("\"orientation\"", vanillaJson, StringComparison.Ordinal);
|
||||
|
||||
var packed = await CreateWithModsAsync(client, "С романтикой", ["romance"]);
|
||||
var packedJson = await ReadPeopleJsonAsync(client, packed.Id);
|
||||
Assert.Contains("\"orientation\"", packedJson, StringComparison.Ordinal);
|
||||
|
||||
var page = await client.GetFromJsonAsync<PeoplePage>(
|
||||
$"/api/schools/{packed.Id}/people?role=student&pageSize=5",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(page);
|
||||
var card = await client.GetFromJsonAsync<Card>(
|
||||
$"/api/schools/{packed.Id}/people/{Uri.EscapeDataString(page.People[0].Id)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(card);
|
||||
Assert.False(string.IsNullOrWhiteSpace(card.Orientation?.DefName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SixteenYearOlds_DoNotPair_WhenOpinionsAreMax()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.ResetAsync(client);
|
||||
var school = await CreateWithModsAsync(client, "Шестнадцать", ["romance"]);
|
||||
|
||||
var page = await client.GetFromJsonAsync<PeoplePage>(
|
||||
$"/api/schools/{school.Id}/people?role=student&year=10&pageSize=40",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(page);
|
||||
Assert.True(page.People.Count >= 2, "need two tenth-years");
|
||||
var first = page.People[0];
|
||||
var second = page.People.First(person => person.Id != first.Id);
|
||||
|
||||
await PatchPeopleAsync(client, school.Id, people =>
|
||||
{
|
||||
Link(people, first.Id, second.Id, 100);
|
||||
Link(people, second.Id, first.Id, 100);
|
||||
});
|
||||
|
||||
var card = await client.GetFromJsonAsync<Card>(
|
||||
$"/api/schools/{school.Id}/people/{Uri.EscapeDataString(first.Id)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(card);
|
||||
Assert.Null(card.Connections?.Pair);
|
||||
Assert.DoesNotContain("\"partnerId\"", await ReadPeopleJsonAsync(client, school.Id), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Teacher_DoesNotGainCrushOnStudent()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.ResetAsync(client);
|
||||
var school = await CreateWithModsAsync(client, "Учитель", ["romance"]);
|
||||
|
||||
var staffing = await client.GetFromJsonAsync<StaffingResponse>(
|
||||
$"/api/schools/{school.Id}/staffing",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(staffing);
|
||||
var applicant = staffing.Applicants.First(row => !row.IsParent);
|
||||
using var hire = await client.PostAsJsonAsync(
|
||||
$"/api/schools/{school.Id}/staff/hire",
|
||||
new { personId = applicant.Id, position = "Teacher" },
|
||||
TestContext.Current.CancellationToken);
|
||||
hire.EnsureSuccessStatusCode();
|
||||
|
||||
var pupils = await client.GetFromJsonAsync<PeoplePage>(
|
||||
$"/api/schools/{school.Id}/people?role=student&pageSize=5",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(pupils);
|
||||
var pupilId = pupils.People[0].Id;
|
||||
|
||||
await PatchPeopleAsync(client, school.Id, people => Link(people, applicant.Id, pupilId, 100));
|
||||
|
||||
var json = await ReadPeopleJsonAsync(client, school.Id);
|
||||
Assert.DoesNotContain($"\"partnerId\": \"{pupilId}\"", json, StringComparison.Ordinal);
|
||||
var teacher = JsonNode.Parse(json)!["people"]!.AsArray()
|
||||
.First(node => node!["id"]?.GetValue<string>() == applicant.Id);
|
||||
var crushes = teacher!["bonds"]?["crushes"]?.AsArray() ?? [];
|
||||
Assert.DoesNotContain(crushes, node => node?.GetValue<string>() == pupilId);
|
||||
|
||||
var card = await client.GetFromJsonAsync<Card>(
|
||||
$"/api/schools/{school.Id}/people/{Uri.EscapeDataString(applicant.Id)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(card);
|
||||
Assert.DoesNotContain(card.Connections?.Crushes ?? [], row => row.Id == pupilId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FirstYear_DoesNotCrushEleventh()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.ResetAsync(client);
|
||||
var school = await CreateWithModsAsync(client, "Параллели", ["romance"]);
|
||||
|
||||
var first = await client.GetFromJsonAsync<PeoplePage>(
|
||||
$"/api/schools/{school.Id}/people?role=student&year=1&pageSize=5",
|
||||
TestContext.Current.CancellationToken);
|
||||
var eleventh = await client.GetFromJsonAsync<PeoplePage>(
|
||||
$"/api/schools/{school.Id}/people?role=student&year=11&pageSize=5",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(first);
|
||||
Assert.NotNull(eleventh);
|
||||
Assert.NotEmpty(first.People);
|
||||
Assert.NotEmpty(eleventh.People);
|
||||
var youngId = first.People[0].Id;
|
||||
var oldId = eleventh.People[0].Id;
|
||||
|
||||
await PatchPeopleAsync(client, school.Id, people => Link(people, youngId, oldId, 100));
|
||||
|
||||
var card = await client.GetFromJsonAsync<Card>(
|
||||
$"/api/schools/{school.Id}/people/{Uri.EscapeDataString(youngId)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(card);
|
||||
Assert.DoesNotContain(card.Connections?.Crushes ?? [], row => row.Id == oldId);
|
||||
}
|
||||
|
||||
private static async Task<SchoolApiTests.SchoolResponse> CreateWithModsAsync(
|
||||
HttpClient client,
|
||||
string name,
|
||||
string[] modIds)
|
||||
{
|
||||
using var response = await client.PostAsJsonAsync(
|
||||
"/api/schools",
|
||||
new { name, startDate = Start, modIds, seed = 47 },
|
||||
TestContext.Current.CancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var created = await response.Content.ReadFromJsonAsync<SchoolApiTests.SchoolResponse>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadPeopleJsonAsync(HttpClient client, int schoolId)
|
||||
{
|
||||
var directory = await SchoolApiTests.SavesDirectoryAsync(client);
|
||||
return await File.ReadAllTextAsync(
|
||||
Path.Combine(directory, $"{schoolId}.people.json"),
|
||||
TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
private static async Task PatchPeopleAsync(HttpClient client, int schoolId, Action<JsonArray> patch)
|
||||
{
|
||||
var directory = await SchoolApiTests.SavesDirectoryAsync(client);
|
||||
var path = Path.Combine(directory, $"{schoolId}.people.json");
|
||||
var doc = JsonNode.Parse(await File.ReadAllTextAsync(path, TestContext.Current.CancellationToken))!;
|
||||
var people = doc["people"]!.AsArray();
|
||||
patch(people);
|
||||
await File.WriteAllTextAsync(path, doc.ToJsonString(new JsonSerializerOptions { WriteIndented = true }), TestContext.Current.CancellationToken);
|
||||
|
||||
using var reload = await client.PostAsync("/api/dev/reload-schools", content: null, TestContext.Current.CancellationToken);
|
||||
reload.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
private static void Link(JsonArray people, string fromId, string toId, int opinion)
|
||||
{
|
||||
var from = people.First(node => node!["id"]?.GetValue<string>() == fromId)!.AsObject();
|
||||
from["orientation"] = "Bisexual";
|
||||
var opinions = from["opinions"] as JsonObject ?? new JsonObject();
|
||||
opinions[toId] = opinion;
|
||||
from["opinions"] = opinions;
|
||||
var bonds = from["bonds"] as JsonObject ?? new JsonObject();
|
||||
bonds["crushes"] = new JsonArray();
|
||||
from["bonds"] = bonds;
|
||||
}
|
||||
|
||||
private sealed record CatalogResponse(IReadOnlyList<TopicInfo> Topics);
|
||||
|
||||
private sealed record TopicInfo(string DefName, string Label, IReadOnlyList<string> Tags);
|
||||
|
||||
private sealed record PeoplePage(IReadOnlyList<PersonRow> People);
|
||||
|
||||
private sealed record PersonRow(string Id);
|
||||
|
||||
private sealed record Card(string Id, DefLabel? Orientation, PersonConnections? Connections);
|
||||
|
||||
private sealed record DefLabel(string DefName, string Label);
|
||||
|
||||
private sealed record PersonConnections(
|
||||
IReadOnlyList<PersonLink>? Crushes,
|
||||
PersonLink? Pair);
|
||||
|
||||
private sealed record PersonLink(string Id);
|
||||
|
||||
private sealed record StaffingResponse(IReadOnlyList<ApplicantRow> Applicants);
|
||||
|
||||
private sealed record ApplicantRow(string Id, bool IsParent);
|
||||
}
|
||||
@@ -29,6 +29,10 @@
|
||||
<Link>example\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="..\..\src\HSchool.Server\mods\romance\**\*">
|
||||
<Link>romance\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
namespace HSchool.Content.Tests;
|
||||
|
||||
public class RomancePackTests
|
||||
{
|
||||
private const string RomancePackId = "romance";
|
||||
|
||||
private readonly CatalogLoader _loader = new();
|
||||
|
||||
[Fact]
|
||||
public void VanillaCatalog_HasNoOrientationsOrRomanceTopics()
|
||||
{
|
||||
var vanilla = LoadCore();
|
||||
Assert.Empty(vanilla.Orientations);
|
||||
Assert.Null(vanilla.AffinityRules);
|
||||
Assert.DoesNotContain(vanilla.Topics.Keys, id => id.StartsWith("TopicCrush", StringComparison.Ordinal));
|
||||
Assert.DoesNotContain(vanilla.Topics.Values, topic => topic.Tags.Contains("romance", StringComparer.Ordinal));
|
||||
Assert.False(vanilla.Traits.ContainsKey("CrushProne"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pack_LoadsOrientationsTraitsTopicsAndAffinityRules()
|
||||
{
|
||||
var catalog = LoadCoreAndRomance();
|
||||
Assert.True(catalog.Orientations.ContainsKey("Heterosexual"));
|
||||
Assert.True(catalog.Orientations.ContainsKey("Bisexual"));
|
||||
Assert.True(catalog.Orientations.ContainsKey("Homosexual"));
|
||||
Assert.NotNull(catalog.AffinityRules);
|
||||
Assert.Equal(18, catalog.AffinityRules!.PairMinAge);
|
||||
Assert.True(catalog.Traits.ContainsKey("CrushProne"));
|
||||
Assert.True(catalog.Traits.ContainsKey("Jealous"));
|
||||
Assert.Equal(-15, catalog.Traits["CrushProne"].AffinityThresholdOffset);
|
||||
Assert.True(catalog.Topics.ContainsKey("TopicCrush"));
|
||||
Assert.True(catalog.Topics.ContainsKey("TopicCouple"));
|
||||
Assert.True(catalog.Topics.ContainsKey("TopicTease"));
|
||||
Assert.Contains("romance", catalog.Topics["TopicAppearance"].Tags);
|
||||
Assert.Equal("Романтика", catalog.Text("ru", "romance"));
|
||||
Assert.Equal("Romance", catalog.Text("en", "romance"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExampleWithoutRomance_KeepsVanillaTopics()
|
||||
{
|
||||
var vanilla = LoadCore();
|
||||
var example = LoadCoreAndExample();
|
||||
Assert.Equal(
|
||||
vanilla.Topics.Keys.OrderBy(id => id, StringComparer.Ordinal),
|
||||
example.Topics.Keys.OrderBy(id => id, StringComparer.Ordinal));
|
||||
Assert.Empty(example.Orientations);
|
||||
Assert.DoesNotContain(example.Topics.Values, topic => topic.Tags.Contains("romance", StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
private DefCatalog LoadCore() =>
|
||||
_loader.Load(
|
||||
[CatalogLoader.CorePackId],
|
||||
PackDocuments.FromDirectory(CatalogLoader.CorePackId, CoreRoot()));
|
||||
|
||||
private DefCatalog LoadCoreAndRomance() =>
|
||||
_loader.Load(
|
||||
[CatalogLoader.CorePackId, RomancePackId],
|
||||
[
|
||||
.. PackDocuments.FromDirectory(CatalogLoader.CorePackId, CoreRoot()),
|
||||
.. PackDocuments.FromDirectory(RomancePackId, RomanceRoot()),
|
||||
]);
|
||||
|
||||
private DefCatalog LoadCoreAndExample() =>
|
||||
_loader.Load(
|
||||
[CatalogLoader.CorePackId, "example"],
|
||||
[
|
||||
.. PackDocuments.FromDirectory(CatalogLoader.CorePackId, CoreRoot()),
|
||||
.. PackDocuments.FromDirectory("example", ExampleRoot()),
|
||||
]);
|
||||
|
||||
private static string CoreRoot() => Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||||
|
||||
private static string RomanceRoot()
|
||||
{
|
||||
var root = Path.Combine(AppContext.BaseDirectory, "romance");
|
||||
Assert.True(Directory.Exists(root), $"Romance pack was not copied to {root}.");
|
||||
return root;
|
||||
}
|
||||
|
||||
private static string ExampleRoot() => Path.Combine(AppContext.BaseDirectory, "example");
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
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 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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,19 @@ internal static class Fixtures
|
||||
PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
|
||||
}
|
||||
|
||||
public static DefCatalog RomanceCatalog()
|
||||
{
|
||||
var vanilla = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||||
var romance = Path.Combine(AppContext.BaseDirectory, "romance");
|
||||
Assert.True(Directory.Exists(romance), $"Romance pack was not copied to {romance}.");
|
||||
return new CatalogLoader().Load(
|
||||
[CatalogLoader.CorePackId, "romance"],
|
||||
[
|
||||
.. PackDocuments.FromDirectory(CatalogLoader.CorePackId, vanilla),
|
||||
.. PackDocuments.FromDirectory("romance", romance),
|
||||
]);
|
||||
}
|
||||
|
||||
public static MapLayout Classrooms(int count, int desks = 16)
|
||||
{
|
||||
var rooms = new RoomNode[count];
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
<Link>example\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="..\..\src\HSchool.Server\mods\romance\**\*">
|
||||
<Link>romance\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="golden\**\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace HSchool.People.Tests;
|
||||
|
||||
public class OrientationGeneratorTests
|
||||
{
|
||||
[Fact]
|
||||
public void VanillaGenerate_DoesNotWriteOrientation()
|
||||
{
|
||||
var roster = Fixtures.Generate(Fixtures.Classrooms(2));
|
||||
Assert.All(roster.People, person => Assert.Null(person.Orientation));
|
||||
Assert.All(roster.People, person => Assert.Null(person.Bonds));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RomanceGenerate_WritesOrientationToEveryone()
|
||||
{
|
||||
var catalog = Fixtures.RomanceCatalog();
|
||||
var roster = RosterGenerator.Generate(catalog, Fixtures.Classrooms(2), Fixtures.SchoolSeed, "Russia", Fixtures.AsOf, "RussianLanguage");
|
||||
Assert.NotEmpty(roster.People);
|
||||
Assert.All(roster.People, person =>
|
||||
{
|
||||
Assert.False(string.IsNullOrWhiteSpace(person.Orientation));
|
||||
Assert.True(catalog.Orientations.ContainsKey(person.Orientation!));
|
||||
Assert.NotNull(person.Bonds);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SameSeed_YieldsTheSameOrientations()
|
||||
{
|
||||
var catalog = Fixtures.RomanceCatalog();
|
||||
var a = RosterGenerator.Generate(catalog, Fixtures.Classrooms(2), Fixtures.SchoolSeed, "Russia", Fixtures.AsOf, "RussianLanguage");
|
||||
var b = RosterGenerator.Generate(catalog, Fixtures.Classrooms(2), Fixtures.SchoolSeed, "Russia", Fixtures.AsOf, "RussianLanguage");
|
||||
Assert.Equal(
|
||||
a.People.Select(person => (person.Id, person.Orientation)),
|
||||
b.People.Select(person => (person.Id, person.Orientation)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user