Enhance school creation and staffing management with native language support
ci / server (push) Failing after 3m44s
ci / client (push) Successful in 14s

- Updated protocol documentation to include `nativeLanguages` in `nameSets` and added `nativeLanguage` to school creation options.
- Enhanced the UI for school creation to allow selection of native languages, improving user experience.
- Revised API interfaces to accommodate new native language features, ensuring proper data handling.
- Improved localization strings to support new native language functionalities in both English and Russian.
- Updated tests to validate the new native language features and ensure robust functionality in staffing scenarios.
This commit is contained in:
Leonid Pershin
2026-08-19 22:22:31 +03:00
parent 21d79cb49b
commit 5a00ad7746
48 changed files with 2064 additions and 312 deletions
@@ -0,0 +1,185 @@
namespace HSchool.People.Tests;
public class SkillGrantTests
{
[Fact]
public void FirstYear_HasCoreAndPrimary_ButNotChemistryOrEnglish()
{
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
var pupils = PupilsIn(roster, 1).ToArray();
Assert.NotEmpty(pupils);
Assert.All(
pupils,
person =>
{
Assert.True(person.Skills.ContainsKey("Communication"));
Assert.True(person.Skills.ContainsKey("RussianLanguage"));
Assert.True(person.Skills.ContainsKey("Agility"));
Assert.True(person.Skills.ContainsKey("Mathematics"));
Assert.False(person.Skills.ContainsKey("Chemistry"));
Assert.False(person.Skills.ContainsKey("Physics"));
Assert.False(person.Skills.ContainsKey("English"));
Assert.False(person.Skills.ContainsKey("German"));
Assert.False(person.Skills.ContainsKey("Pedagogy"));
});
}
[Fact]
public void YearFive_HasEnglish_YearEight_HasChemistry()
{
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
Assert.All(
PupilsIn(roster, 5),
person =>
{
Assert.True(person.Skills.ContainsKey("English"));
Assert.False(person.Skills.ContainsKey("Chemistry"));
Assert.False(person.Skills.ContainsKey("German"));
});
Assert.All(PupilsIn(roster, 8), person => Assert.True(person.Skills.ContainsKey("Chemistry")));
}
[Fact]
public void Adults_HaveNativeAndCommunication_ButNotEverySkill()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
var adults = roster.People.Where(person => !person.IsStudent).ToArray();
Assert.NotEmpty(adults);
var concrete = catalog.Skills.Values.Count(skill => !skill.Abstract);
Assert.All(
adults,
person =>
{
Assert.True(person.Skills.ContainsKey("Communication"));
Assert.True(person.Skills.ContainsKey("RussianLanguage"));
Assert.True(person.Skills.Count < concrete);
Assert.InRange(person.Skills.Keys.Count(name => catalog.Skills[name].Work), 0, 2);
});
Assert.Contains(
adults,
person => person.Skills.Keys.Any(name => catalog.Skills[name].Work));
}
[Fact]
public void BelarusianNative_IsGrantedToEveryone_PupilsStillStudyRussian()
{
var catalog = Fixtures.Catalog();
var roster = RosterGenerator.Generate(
catalog,
Fixtures.Classrooms(11),
Fixtures.SchoolSeed,
"Slavic",
Fixtures.AsOf,
"BelarusianLanguage");
var max = catalog.NameSets["Slavic"].RelatedLanguageMax;
Assert.All(
roster.People,
person =>
{
Assert.True(person.Skills.ContainsKey("BelarusianLanguage"));
if (person.Skills.TryGetValue("UkrainianLanguage", out var ukrainian))
{
Assert.InRange(ukrainian, 0, max);
}
});
Assert.All(
roster.People.Where(person => person.IsStudent),
person => Assert.True(person.Skills.ContainsKey("RussianLanguage")));
Assert.Contains(
roster.People.Where(person => person.IsStudent),
person => person.Skills["RussianLanguage"] > max);
}
[Fact]
public void RelatedLanguages_AreCommonAndStayLow()
{
var catalog = Fixtures.Catalog();
var max = catalog.NameSets["Slavic"].RelatedLanguageMax;
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
Assert.Contains(
roster.People,
person =>
person.Skills.ContainsKey("BelarusianLanguage")
|| person.Skills.ContainsKey("UkrainianLanguage"));
Assert.Contains(roster.People, person => !person.Skills.ContainsKey("BelarusianLanguage"));
Assert.Contains(roster.People, person => !person.Skills.ContainsKey("UkrainianLanguage"));
Assert.True(
roster.People.Count(person =>
person.Skills.ContainsKey("BelarusianLanguage")
|| person.Skills.ContainsKey("UkrainianLanguage"))
> roster.People.Count / 2);
Assert.All(
roster.People,
person =>
{
Assert.True(person.Skills.ContainsKey("RussianLanguage"));
if (person.Skills.TryGetValue("BelarusianLanguage", out var belarusian))
{
Assert.InRange(belarusian, 0, max);
}
if (person.Skills.TryGetValue("UkrainianLanguage", out var ukrainian))
{
Assert.InRange(ukrainian, 0, max);
}
});
}
[Fact]
public void OmittedNativeLanguage_IsStableForTheSameSeed()
{
var map = Fixtures.Classrooms(2);
var a = RosterGenerator.Generate(Fixtures.Catalog(), map, 7, "Slavic", Fixtures.AsOf);
var b = RosterGenerator.Generate(Fixtures.Catalog(), map, 7, "Slavic", Fixtures.AsOf);
Assert.Equal(
a.People.Select(person => string.Join(',', person.Skills.Keys.Order(StringComparer.Ordinal))),
b.People.Select(person => string.Join(',', person.Skills.Keys.Order(StringComparer.Ordinal))));
}
[Fact]
public void Pupils_DoNotRollWorkSkills()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
Assert.All(
roster.People.Where(person => person.IsStudent),
person => Assert.DoesNotContain(person.Skills.Keys, name => catalog.Skills[name].Work));
}
[Fact]
public void Intake_YearFourGainsEnglishWithoutRerollingMath()
{
var catalog = Fixtures.Catalog();
var before = Fixtures.Generate(Fixtures.Classrooms(5));
var year4 = before.Classes.Single(schoolClass => schoolClass.Year == 4);
var pupil = before.People.First(person => person.Id == year4.PupilIds[0]);
Assert.False(pupil.Skills.ContainsKey("English"));
var math = pupil.Skills["Mathematics"];
var after = YearlyIntake.Apply(
catalog,
before,
Fixtures.SchoolSeed,
"Slavic",
new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc));
var grown = after.People.First(person => person.Id == pupil.Id);
Assert.True(grown.ClassId is { } classId && after.Classes.Any(schoolClass =>
schoolClass.Id.Equals(classId, StringComparison.Ordinal) && schoolClass.Year == 5));
Assert.True(grown.Skills.ContainsKey("English"));
Assert.Equal(math, grown.Skills["Mathematics"]);
}
private static IEnumerable<Person> PupilsIn(Roster roster, int year)
{
var classIds = roster.Classes
.Where(schoolClass => schoolClass.Year == year)
.Select(schoolClass => schoolClass.Id)
.ToHashSet(StringComparer.Ordinal);
return roster.People.Where(person =>
person.IsStudent && person.ClassId is { } classId && classIds.Contains(classId));
}
}