476 lines
20 KiB
C#
476 lines
20 KiB
C#
using SGU.AuthBroker.Core.Profiles;
|
|
using Xunit;
|
|
|
|
namespace SGU.AuthBroker.Core.Tests;
|
|
|
|
public sealed class SguProfileParserTests
|
|
{
|
|
[Fact]
|
|
public void ParsesOnlyTheRequiredAdministrativeFieldsFromTheInitialResponse()
|
|
{
|
|
const string html = """
|
|
<html><body>
|
|
<span id="ctl00_contenedor_decEncabezado_lblNombre">
|
|
017045 - JESÚS ALEJANDRO ROSALES GONZÁLEZ
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblIndicadorValue">
|
|
SINDICALIZADO QUINCENAL (ACTIVO)
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblCorreo">
|
|
<a href="mailto:persona@lasalle.mx">persona@lasalle.mx</a>
|
|
</span>
|
|
<div style="display:none">
|
|
<span id="ctl00_contenedor_decEncabezado_lblPuesto">
|
|
ANALISTA DE PROYECTOS
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblDependencia">
|
|
FACULTAD DE INGENIERÍA
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblJefeNombre">
|
|
ESTE CAMPO NO DEBE EXTRAERSE
|
|
</span>
|
|
</div>
|
|
<table id="incidencias"><tr><td>DATOS NO REQUERIDOS</td></tr></table>
|
|
</body></html>
|
|
""";
|
|
|
|
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 = """
|
|
<span id='ctl00_contenedor_decEncabezado_lblNombre'>017045 - PERSONA INCORRECTA</span>
|
|
<span id='ctl00_contenedor_decEncabezado_lblCorreo'>incorrecta@lasalle.mx</span>
|
|
""";
|
|
|
|
Assert.Null(SguProfileParser.ParseAdministrative(html, "999999"));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParsesOnlyTheSupportedProfessorPayrollHeaderFields()
|
|
{
|
|
const string html = """
|
|
<div id="ctl00_contenedor_decEncabezado_pnlSinPoP">
|
|
<span id="ctl00_contenedor_decEncabezado_lblNombre">
|
|
013473 - ALEJANDRO LARA VILLARREAL
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblIndicadorValue">
|
|
SINDICALIZADO QUINCENAL (ACTIVO)
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblCorreo">
|
|
alejandro.lara@lasallistas.org.mx
|
|
</span>
|
|
<img id="ctl00_contenedor_decEncabezado_imgFoto"
|
|
src="../admonPersonal/ashx/Fotografia.ashx?id=013473&tp=2" />
|
|
<span id="ctl00_contenedor_decEncabezado_lblPuesto">DOCENTE</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblDependencia"></span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblJefeNombre">NO EXTRAER</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblJefePuesto">NO EXTRAER</span>
|
|
</div>
|
|
""";
|
|
|
|
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 = """
|
|
<span id="ctl00_contenedor_decEncabezado_lblNombre">
|
|
013473 - PERSONA INCORRECTA
|
|
</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblCorreo">
|
|
incorrecta@lasallistas.org.mx
|
|
</span>
|
|
""";
|
|
|
|
Assert.Null(SguProfileParser.ParseProfessorPayroll(html, "123456"));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParsesStructuredAdministrativeNameWithoutReadingOtherPersonalData()
|
|
{
|
|
const string html = """
|
|
<html><body>
|
|
<input id="ctl00_contenedor_txtNombre" value="MARÍA DEL CARMEN" />
|
|
<input id="ctl00_contenedor_txtApaterno" value="DE LA FUENTE" />
|
|
<input id="ctl00_contenedor_txtAmaterno" value="O'CONNOR" />
|
|
<input id="ctl00_contenedor_txtCURP" value="DATO-QUE-NO-DEBE-EXTRAERSE" />
|
|
<select id="ctl00_contenedor_ddlNacimientoFecha_ddlYear">
|
|
<option selected="selected">1990</option>
|
|
</select>
|
|
</body></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 = $"""
|
|
<select id="{id}" name="{name}">
|
|
<option value="">Seleccione...</option>
|
|
<option value="1"{(selectedValue == "1" ? " selected=\"selected\"" : string.Empty)}>Masculino</option>
|
|
<option value="2"{(selectedValue == "2" ? " selected=\"selected\"" : string.Empty)}>Femenino</option>
|
|
</select>
|
|
""";
|
|
|
|
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 = $"""
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblClaveAlumnoHP">123456</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblSexoAlumnoHP">{source}</span>
|
|
""";
|
|
|
|
InstitutionalProfile? profile = SguProfileParser.ParseStudent(html, "123456");
|
|
|
|
Assert.NotNull(profile);
|
|
Assert.Equal(expected, profile.Gender);
|
|
}
|
|
|
|
[Fact]
|
|
public void IgnoresUnknownGenderValuesWithoutFailingProfileParsing()
|
|
{
|
|
const string html = """
|
|
<input id="ctl00_contenedor_txtNombre" value="PERSONA" />
|
|
<select id="ctl00_contenedor_ddlsexo">
|
|
<option selected="selected" value="9">SIN CLASIFICAR</option>
|
|
</select>
|
|
""";
|
|
|
|
InstitutionalProfile? profile = SguProfileParser.ParseAdministrativePersonal(html);
|
|
|
|
Assert.NotNull(profile);
|
|
Assert.Equal("Persona", profile.DisplayName);
|
|
Assert.Null(profile.Gender);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParsesAdministrativeAddressFromInputsAndSelectedOptions()
|
|
{
|
|
const string html = """
|
|
<html><body>
|
|
<input id='ctl00_contenedor_txtCalle' value='AVENIDA DE LA UNIVERSIDAD' />
|
|
<input id='ctl00_contenedor_txtNoExt' value='123' />
|
|
<input id='ctl00_contenedor_txtNoInt' value='B-4' />
|
|
<input id='ctl00_contenedor_txtCP' value='8500' />
|
|
<select id='ctl00_contenedor_ddlEstado'>
|
|
<option value='0'>Seleccione...</option>
|
|
<option selected='selected' value='9'>CIUDAD DE MÉXICO</option>
|
|
</select>
|
|
<select id='ctl00_contenedor_ddlLocalidad'>
|
|
<option value='1'>AZCAPOTZALCO</option>
|
|
<option selected='selected' value='4'>BENITO JUÁREZ</option>
|
|
</select>
|
|
<select id='ctl00_contenedor_ddlColonia'>
|
|
<option value='10'>COLONIA DEL VALLE</option>
|
|
</select>
|
|
<input id='ctl00_contenedor_txtTelefono' value='5555555555' />
|
|
<textarea id='ctl00_contenedor_txtDirEmergencia'>NO EXTRAER</textarea>
|
|
</body></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 = """
|
|
<html><body>
|
|
<input id='ctl00_contenedor_txtCalle' value='RETORNO 1, SUR 16' />
|
|
<input id='ctl00_contenedor_txtNoExt' value='74' />
|
|
<input id='ctl00_contenedor_txtNoInt' value='' />
|
|
<input id='ctl00_contenedor_txtCP' value='08500' />
|
|
<select id='ctl00_contenedor_ddlEstado'>
|
|
<option selected='selected' value='0'>Seleccione...</option>
|
|
<option value='09'>CIUDAD DE MÉXICO</option>
|
|
</select>
|
|
<select id='ctl00_contenedor_ddlLocalidad'>
|
|
<option selected='selected' value='0'>Seleccione alguna localidad...</option>
|
|
</select>
|
|
<select id='ctl00_contenedor_ddlColonia'>
|
|
<option selected='selected' value='0,0'>Seleccione alguna colonia...</option>
|
|
</select>
|
|
</body></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 = """
|
|
<html><body>
|
|
<div id="ctl00_contenedor_HistorialAlumno1_div1">
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblClaveAlumnoHP">123456</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblNombreAlumnoHP">MARÍA DEL CARMEN</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblApPatAlumnoHP">DE LA FUENTE</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblApMatAlumnoHP">O'CONNOR</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblCorreoAlumnoHP">
|
|
<a href="mailto:alumna@lasalle.mx">ALUMNA@LASALLE.MX</a>
|
|
</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblSexoAlumnoHP">F</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblCURPAlumnoHP">
|
|
DATO-SENSIBLE-QUE-NO-DEBE-EXTRAERSE
|
|
</span>
|
|
</div>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_Header1_lblCarrera">
|
|
LICENCIATURA EN INGENIERÍA CIBERNÉTICA Y SISTEMAS COMPUTACIO NALES
|
|
</span>
|
|
<div id="ctl00_contenedor_HistorialAlumno1_div2">
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblDomicilioAlumnoHP">
|
|
AVENIDA DE LA UNIVERSIDAD 123
|
|
</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblColoniaAlumnoHP">
|
|
COLONIA DEL VALLE
|
|
</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblEstadoAlumnoHP">
|
|
CIUDAD DE MÉXICO
|
|
</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblCiudadAlumnoHP">
|
|
CIUDAD DE MÉXICO
|
|
</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblDeloMunAlumnoHP">
|
|
ALCALDÍA BENITO JUÁREZ
|
|
</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblCPAlumnoHP">8500</span>
|
|
</div>
|
|
</body></html>
|
|
""";
|
|
|
|
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 = """
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblClaveAlumnoHP">123456</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblNombreAlumnoHP">PERSONA INCORRECTA</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblCorreoAlumnoHP">incorrecta@lasalle.mx</span>
|
|
""";
|
|
|
|
Assert.Null(SguProfileParser.ParseStudent(html, "654321"));
|
|
}
|
|
|
|
[Fact]
|
|
public void UsesMunicipalityAsStudentCityWhenThePortalCityIsMissing()
|
|
{
|
|
const string html = """
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblClaveAlumnoHP">123456</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblDeloMunAlumnoHP">SAN PEDRO CHOLULA</span>
|
|
<span id="ctl00_contenedor_HistorialAlumno1_lblCPAlumnoHP">72760</span>
|
|
""";
|
|
|
|
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 = """
|
|
<span class="usuario" id="ctl00_lblNombreUsuario">
|
|
MARÍA & JOSÉ
|
|
</span>
|
|
""";
|
|
|
|
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(
|
|
$"<span id=\"{marker}\">{source}</span>");
|
|
|
|
Assert.NotNull(profile);
|
|
Assert.Equal(expected, profile.DisplayName);
|
|
}
|
|
|
|
[Fact]
|
|
public void RejectsReplacementCharactersInsteadOfWritingCorruptMetadata()
|
|
{
|
|
const string html = """
|
|
<span id="ctl00_contenedor_decEncabezado_lblNombre">017045 - JES�S ROSALES</span>
|
|
<span id="ctl00_contenedor_decEncabezado_lblPuesto">ANALISTA DE INMERSI�N</span>
|
|
""";
|
|
|
|
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("<html><body>Portal SGU</body></html>"));
|
|
Assert.Null(SguProfileParser.ParseAdministrative(
|
|
"<html><body>Portal SGU</body></html>",
|
|
"017045"));
|
|
}
|
|
}
|