using SGU.AuthBroker.Core.Profiles; using Xunit; namespace SGU.AuthBroker.Core.Tests; public sealed class SguProfileParserTests { [Fact] public void ParsesOnlyTheRequiredAdministrativeFieldsFromTheInitialResponse() { const string html = """ 017045 - JESÚS ALEJANDRO ROSALES GONZÁLEZ SINDICALIZADO QUINCENAL (ACTIVO) persona@lasalle.mx
ANALISTA DE PROYECTOS FACULTAD DE INGENIERÍA ESTE CAMPO NO DEBE EXTRAERSE
DATOS NO REQUERIDOS
"""; InstitutionalProfile? profile = SguProfileParser.ParseAdministrative(html, "017045"); Assert.NotNull(profile); Assert.Equal("017045", profile.EmployeeNumber); Assert.Equal("Jesús Alejandro Rosales González", profile.DisplayName); Assert.Equal("persona@lasalle.mx", profile.Email); Assert.Equal("Sindicalizado quincenal (activo)", profile.EmployeeType); Assert.Equal("Analista de Proyectos", profile.JobTitle); Assert.Equal("Facultad de Ingeniería", profile.Department); } [Fact] public void RejectsAdministrativeMetadataForADifferentEmployeeNumber() { const string html = """ 017045 - PERSONA INCORRECTA incorrecta@lasalle.mx """; Assert.Null(SguProfileParser.ParseAdministrative(html, "999999")); } [Fact] public void ParsesOnlyTheSupportedProfessorPayrollHeaderFields() { const string html = """
013473 - ALEJANDRO LARA VILLARREAL SINDICALIZADO QUINCENAL (ACTIVO) alejandro.lara@lasallistas.org.mx DOCENTE NO EXTRAER NO EXTRAER
"""; InstitutionalProfile? profile = SguProfileParser.ParseProfessorPayroll(html, "013473"); Assert.NotNull(profile); Assert.Equal("013473", profile.EmployeeNumber); Assert.Equal("Alejandro Lara Villarreal", profile.DisplayName); Assert.Equal("alejandro.lara@lasallistas.org.mx", profile.Email); Assert.Equal("Sindicalizado quincenal (activo)", profile.EmployeeType); Assert.Equal("Docente", profile.JobTitle); Assert.Null(profile.Department); Assert.Null(profile.GivenName); Assert.Null(profile.Surname); Assert.Null(profile.StreetAddress); } [Fact] public void RejectsProfessorPayrollMetadataForADifferentEmployeeNumber() { const string html = """ 013473 - PERSONA INCORRECTA incorrecta@lasallistas.org.mx """; Assert.Null(SguProfileParser.ParseProfessorPayroll(html, "123456")); } [Fact] public void ParsesStructuredAdministrativeNameWithoutReadingOtherPersonalData() { const string html = """ """; InstitutionalProfile? profile = SguProfileParser.ParseAdministrativePersonal(html); Assert.NotNull(profile); Assert.Equal("María del Carmen", profile.GivenName); Assert.Equal("de la Fuente O'Connor", profile.Surname); Assert.Equal("María del Carmen de la Fuente O'Connor", profile.DisplayName); Assert.Null(profile.EmployeeNumber); Assert.Null(profile.Email); } [Theory] [InlineData("ctl00_contenedor_ddlsexo", "ctl00$contenedor$ddlsexo", "1", InstitutionalGender.Male)] [InlineData("alternate-id", "ctl00$contenedor$ddlsexo", "2", InstitutionalGender.Female)] public void ParsesStaffGenderFromTheSelectedPersonalDataOption( string id, string name, string selectedValue, InstitutionalGender expected) { string html = $""" """; InstitutionalProfile? profile = SguProfileParser.ParseAdministrativePersonal(html); Assert.NotNull(profile); Assert.Equal(expected, profile.Gender); } [Theory] [InlineData("M", InstitutionalGender.Male)] [InlineData("f", InstitutionalGender.Female)] public void ParsesStudentGenderFromTheInformationSpan( string source, InstitutionalGender expected) { string html = $""" 123456 {source} """; InstitutionalProfile? profile = SguProfileParser.ParseStudent(html, "123456"); Assert.NotNull(profile); Assert.Equal(expected, profile.Gender); } [Fact] public void IgnoresUnknownGenderValuesWithoutFailingProfileParsing() { const string html = """ """; InstitutionalProfile? profile = SguProfileParser.ParseAdministrativePersonal(html); Assert.NotNull(profile); Assert.Equal("Persona", profile.DisplayName); Assert.Null(profile.Gender); } [Fact] public void ParsesAdministrativeAddressFromInputsAndSelectedOptions() { const string html = """ """; InstitutionalProfile? profile = SguProfileParser.ParseAdministrativeLocation(html); Assert.NotNull(profile); Assert.Equal( "Avenida de la Universidad 123, Int. B-4\r\nColonia del Valle", profile.StreetAddress); Assert.Equal("Benito Juárez", profile.City); Assert.Equal("Ciudad de México", profile.State); Assert.Equal("08500", profile.PostalCode); Assert.Null(profile.Email); } [Fact] public void ResolvesAdministrativeAddressFromPageMethodIdentifiers() { const string html = """ """; const string directionJson = """ {"d":[{"p_IdCP":"091263","p_IdEstado":"09","p_NombreEstado":"","p_IdMunicipio":"006","p_NombreMunicipio":"","p_NombreColonia":"","p_Cp":"08500"}]} """; const string localitiesJson = """ {"d":[{"ID_Estado":"09","Id_Municipio":"002","Nombre":"AZCAPOTZALCO"},{"ID_Estado":"09","Id_Municipio":"006","Nombre":"IZTACALCO"}]} """; const string neighborhoodsJson = """ {"d":[{"p_IdCP":"091263","p_Nombre":"AGRÍCOLA ORIENTAL","p_Cp":"08500"}]} """; SguAdministrativeLocationSelection? selection = SguProfileParser.ParseAdministrativeLocationSelection(directionJson, "08500"); InstitutionalProfile? profile = SguProfileParser.ParseAdministrativeLocation( html, selection, localitiesJson, neighborhoodsJson); Assert.NotNull(selection); Assert.Equal("09", selection.StateId); Assert.Equal("006", selection.MunicipalityId); Assert.Equal("091263", selection.NeighborhoodId); Assert.NotNull(profile); Assert.Equal("Retorno 1, Sur 16 74\r\nAgrícola Oriental", profile.StreetAddress); Assert.Equal("Iztacalco", profile.City); Assert.Equal("Ciudad de México", profile.State); Assert.Equal("08500", profile.PostalCode); } [Theory] [InlineData("not-json")] [InlineData("{\"d\":{}}")] [InlineData("{\"d\":[]}")] public void RejectsUnexpectedAdministrativeLocationPayloads(string json) { Assert.Null(SguProfileParser.ParseAdministrativeLocationSelection(json, "08500")); } [Fact] public void AdministrativePagesOverlayTheVerifiedIncidentsProfile() { InstitutionalProfile verified = new( EmployeeNumber: "017045", DisplayName: "Nombre Anterior", Email: "persona@lasalle.mx", JobTitle: "Analista", Department: "Ingeniería"); InstitutionalProfile personal = new( DisplayName: "María del Carmen de la Fuente", GivenName: "María del Carmen", Surname: "de la Fuente", Gender: InstitutionalGender.Female); InstitutionalProfile location = new( StreetAddress: "Calle Uno 10", City: "Ciudad de México", State: "Ciudad de México", PostalCode: "01000"); InstitutionalProfile combined = verified.Overlay(personal).Overlay(location); Assert.Equal("017045", combined.EmployeeNumber); Assert.Equal("María del Carmen de la Fuente", combined.DisplayName); Assert.Equal("María del Carmen", combined.GivenName); Assert.Equal("de la Fuente", combined.Surname); Assert.Equal("persona@lasalle.mx", combined.Email); Assert.Equal("Analista", combined.JobTitle); Assert.Equal("Ingeniería", combined.Department); Assert.Equal("Calle Uno 10", combined.StreetAddress); Assert.Equal("01000", combined.PostalCode); Assert.Equal(InstitutionalGender.Female, combined.Gender); } [Fact] public void ParsesTheRequiredStudentIdentityCareerAndAddressFields() { const string html = """
123456 MARÍA DEL CARMEN DE LA FUENTE O'CONNOR ALUMNA@LASALLE.MX F DATO-SENSIBLE-QUE-NO-DEBE-EXTRAERSE
LICENCIATURA EN INGENIERÍA CIBERNÉTICA Y SISTEMAS COMPUTACIO NALES
AVENIDA DE LA UNIVERSIDAD 123 COLONIA DEL VALLE CIUDAD DE MÉXICO CIUDAD DE MÉXICO ALCALDÍA BENITO JUÁREZ 8500
"""; InstitutionalProfile? profile = SguProfileParser.ParseStudent(html, "123456"); Assert.NotNull(profile); Assert.Equal("123456", profile.EmployeeNumber); Assert.Equal("María del Carmen de la Fuente O'Connor", profile.DisplayName); Assert.Equal("María del Carmen", profile.GivenName); Assert.Equal("de la Fuente O'Connor", profile.Surname); Assert.Equal("alumna@lasalle.mx", profile.Email); Assert.Equal("Alumno", profile.EmployeeType); Assert.Equal( "Estudiante de Ingeniería Cibernética y Sistemas Computacionales", profile.JobTitle); Assert.Null(profile.Department); Assert.Equal( "Avenida de la Universidad 123\r\nColonia del Valle\r\nAlcaldía Benito Juárez", profile.StreetAddress); Assert.Equal("Ciudad de México", profile.City); Assert.Equal("Ciudad de México", profile.State); Assert.Equal("08500", profile.PostalCode); Assert.Equal(InstitutionalGender.Female, profile.Gender); } [Fact] public void RejectsStudentMetadataForADifferentStudentNumber() { const string html = """ 123456 PERSONA INCORRECTA incorrecta@lasalle.mx """; Assert.Null(SguProfileParser.ParseStudent(html, "654321")); } [Fact] public void UsesMunicipalityAsStudentCityWhenThePortalCityIsMissing() { const string html = """ 123456 SAN PEDRO CHOLULA 72760 """; InstitutionalProfile? profile = SguProfileParser.ParseStudent(html, "123456"); Assert.NotNull(profile); Assert.Equal("San Pedro Cholula", profile.City); Assert.Equal("San Pedro Cholula", profile.StreetAddress); Assert.Equal("72760", profile.PostalCode); } [Fact] public void ParsesTheMenuNameAsAConservativeFallback() { const string html = """ MARÍA & JOSÉ """; InstitutionalProfile? profile = SguProfileParser.ParseMenu(html); Assert.NotNull(profile); Assert.Equal("María & José", profile.DisplayName); Assert.Null(profile.Email); Assert.Null(profile.JobTitle); Assert.Null(profile.Department); } [Theory] [InlineData("MARÍA DEL CARMEN", "María del Carmen")] [InlineData("MIGUEL DE CERVANTES", "Miguel de Cervantes")] [InlineData("CARLOS DE LA FUENTE", "Carlos de la Fuente")] [InlineData("MARÍA-JOSÉ O'CONNOR", "María-José O'Connor")] public void PreservesSpanishNameParticlesAndAccents(string source, string expected) { const string marker = "ctl00_lblNombreUsuario"; InstitutionalProfile? profile = SguProfileParser.ParseMenu( $"{source}"); Assert.NotNull(profile); Assert.Equal(expected, profile.DisplayName); } [Fact] public void RejectsReplacementCharactersInsteadOfWritingCorruptMetadata() { const string html = """ 017045 - JES�S ROSALES ANALISTA DE INMERSI�N """; InstitutionalProfile? profile = SguProfileParser.ParseAdministrative(html, "017045"); Assert.NotNull(profile); Assert.Null(profile.DisplayName); Assert.Null(profile.JobTitle); Assert.Equal("017045", profile.EmployeeNumber); } [Fact] public void MissingKnownFieldsProducesNoProfile() { Assert.Null(SguProfileParser.ParseMenu("Portal SGU")); Assert.Null(SguProfileParser.ParseAdministrative( "Portal SGU", "017045")); } }