using Administrationservices;
using CloudServiceDAO;
using EntityFrameworkDAOServices;
using Grpc.Core;
using Xunit;
namespace DynamicCrud.Tests.Grpc;
///
/// Lectura de los servicios legacy (servicioDAOService, administrationService),
/// más ProfileService y CatalogService. Solo se invocan métodos de consulta.
///
///
///
/// Los parámetros salen de , que los descubre con el CRUD dinámico:
/// varios de los stored procedures reciben el filtro nulo y lo tratan como "no coincide con
/// nada" en vez de "sin filtro", así que llamarlos en vacío no ejerce su lógica real.
/// Cuando la tabla origen está vacía el caso termina temprano en lugar de fallar.
///
///
/// Hay dos niveles de exigencia, según lo que se pueda afirmar en cada caso:
/// acepta NotFound como respuesta válida, y
/// lo trata como fallo porque el id vino de una fila que
/// existe. La distinción es necesaria: la capa legacy atrapa las excepciones de MySQL y las
/// devuelve como NotFound, así que sin ella un stored procedure inexistente se ve
/// desde fuera exactamente igual que una tabla vacía.
///
///
[Collection(GrpcCollection.Name)]
[Trait("suite", "grpc")]
public class LegacyReadTests
{
private readonly servicioDAOService.servicioDAOServiceClient _dao;
private readonly administrationService.administrationServiceClient _admin;
private readonly ProfileService.ProfileServiceClient _profiles;
private readonly CatalogService.CatalogServiceClient _catalog;
private readonly DataProbe _probe;
public LegacyReadTests(GrpcFixture fixture)
{
_dao = new servicioDAOService.servicioDAOServiceClient(fixture.Channel);
_admin = new administrationService.administrationServiceClient(fixture.Channel);
_profiles = new ProfileService.ProfileServiceClient(fixture.Channel);
_catalog = new CatalogService.CatalogServiceClient(fixture.Channel);
_probe = new DataProbe(
new CloudServicesCrud.DynamicCrudService.DynamicCrudServiceClient(fixture.Channel));
}
// ---------------------------------------------------------------
// servicioDAOService
// ---------------------------------------------------------------
[Fact]
public void GetProductType_Responde() =>
Responde("getProductType", () => _dao.getProductType(new GetProductTypeRequest()));
[Fact]
public void GetPlansService_Responde()
{
// spslGetPlansServicesById filtra por service_id sin contemplar null.
var idService = _probe.Int("plans", "serviceId");
if (idService is null) return;
Responde("getPlansService", () => _dao.getPlansService(
new GetPlansServiceRequest { IdService = idService.Value }));
}
[Fact]
public void GetServicesCatalog_Responde()
{
var idProductType = _probe.Int("product_type", "id");
if (idProductType is null) return;
Responde("getServicesCatalog", () => _dao.getServicesCatalog(
new CloudServiceDAO.GetServicesByProductTypeRequest { IdProductType = idProductType.Value }));
}
[Fact]
public void GetServicesAllContracted_Responde()
{
var id = _probe.Int("contracted_services", "contractedServicesId");
if (id is null) return;
Responde("getServicesAllContracted", () => _dao.getServicesAllContracted(
new GetAllContractedServicesRequest { ContractedServicesId = id.Value }));
}
[Fact]
public void GetIpServiceById_Responde()
{
var idFirewall = _probe.Int("firewall", "firewallId");
if (idFirewall is null) return;
RespondeConDatos("getIpServiceById", () => _dao.getIpServiceById(
new GetIpServiceByIdRequest { IdFirewall = idFirewall.Value }));
}
[Fact]
public void GetUserVCD_Responde()
{
var userId = _probe.Int("user_vcd", "userVcdId");
if (userId is null) return;
RespondeConDatos("getUserVCD", () => _dao.getUserVCD(
new CloudServiceDAO.GetUserVCDRequest { UserId = userId.Value }));
}
[Fact]
public void GetOrgVCD_Responde()
{
var orgId = _probe.Int("org_vcd", "orgVcdId");
if (orgId is null) return;
RespondeConDatos("getOrgVCD", () => _dao.getOrgVCD(
new CloudServiceDAO.GetOrgVCDRequest { OrgId = orgId.Value }));
}
[Fact]
public void GetRequestQueueService_Responde()
{
var id = _probe.Int("requests_queue", "requestQueueId");
if (id is null) return;
Responde("getRequestQueueService", () => _dao.getRequestQueueService(
new GetRequestQueueRequest { RequestQueueId = id.Value }));
}
[Fact]
public void GetLicenses_Responde()
{
var id = _probe.Int("licenses", "idlicenses");
if (id is null) return;
Responde("GetLicenses", () => _dao.GetLicenses(new GetLicensesRequest { Idlicenses = id.Value }));
}
[Fact]
public void GetStatusCatalog_Responde()
{
var id = _probe.Int("status_catalog", "statusCatalogId");
if (id is null) return;
RespondeConDatos("getStatusCatalog", () => _dao.getStatusCatalog(
new GetStatusCatalogRequest { StatusCatalogId = id.Value }));
}
[Fact]
public void GetConsumptionHistory_Responde()
{
var id = _probe.Int("consumption_history", "consumptionHistoryId");
if (id is null) return;
RespondeConDatos("getConsumptionHistory", () => _dao.getConsumptionHistory(
new GetConsumptionHistoryRequest { ConsumptionHistoryId = id.Value }));
}
[Fact]
public void GetTermsAndConditions_Responde()
{
var id = _probe.Int("terms_conditions", "idtermsConditions");
if (id is null) return;
RespondeConDatos("getTermsandConditions", () => _dao.getTermsandConditions(
new GetTermsAndConditionsRequest { IdTermsConditions = id.Value }));
}
[Fact]
public void GetServicesToDeploy_Responde()
{
var status = _probe.Value("requests_queue", "status");
if (status is null) return;
Responde("getServicesToDeploy", () => _dao.getServicesToDeploy(
new GetServicesToDeployRequest { Status = status }));
}
[Fact]
public void GetOrgVCD_Service_Responde()
{
var idservice = _probe.Int("services", "servicesId");
if (idservice is null) return;
Responde("getOrgVCD_Service", () => _dao.getOrgVCD_Service(
new GetOrgVCD_ServiceRequest { Idservice = idservice.Value }));
}
[Fact]
public void GetContainerImage_Responde()
{
var idservice = _probe.Int("container_images", "servicesId");
if (idservice is null) return;
RespondeConDatos("getContainerImage", () => _dao.getContainerImage(
new GetContainerImageRequest { Idservice = idservice.Value }));
}
[Fact]
public void GetFieldsVdc_Responde()
{
var vdcId = _probe.Int("vdc_vcd", "id");
if (vdcId is null) return;
Responde("getfieldsvdc", () => _dao.getfieldsvdc(
new CloudServiceDAO.getfieldsvdcRequest { VdcId = vdcId.Value }));
}
[Fact]
public void GetGenericInformation_Responde()
{
// spskDy_tablefromclause arma un SELECT dinámico sobre la tabla y columna que se le pasen.
var idregion = _probe.Value("region", "idregion");
if (idregion is null) return;
RespondeConDatos("getGenericInformation", () => _dao.getGenericInformation(new GetGenericInformationRequest
{
TabName = "region",
WcolNameFilter = "idregion",
WParam = idregion
}));
}
[Fact]
public void StatusSupplyService_Responde() =>
Responde("StatusSupplyService", () => _dao.StatusSupplyService(new GetStatusSupplyRequest()));
[Fact]
public void ServiceStatusService_Responde() =>
Responde("ServiceStatusService", () => _dao.ServiceStatusService(new GetServiceStatusRequest()));
// ---------------------------------------------------------------
// administrationService
// ---------------------------------------------------------------
[Fact]
public void GetAllMenuComponents_Responde() =>
Responde("getAllMenuComponents", () => _admin.getAllMenuComponents(new GetAllMenuComponentsRequest()));
[Fact]
public void GetUserAccessInfo_Responde()
{
var userId = _probe.Int("user_role", "iduser");
if (userId is null) return;
Responde("getUserAccessInfo", () => _admin.getUserAccessInfo(
new GetUserAccessInfoRequest { UserId = userId.Value }));
}
[Fact]
public void GetRoleInfo_Responde()
{
var idrole = _probe.Int("role", "idrole");
if (idrole is null) return;
Responde("getRoleInfo", () => _admin.getRoleInfo(new GetRoleInfoRequest { Idrole = idrole.Value }));
}
// ---------------------------------------------------------------
// ProfileService
// ---------------------------------------------------------------
[Fact]
public void Ping_Responde() =>
Responde("Ping", () => _profiles.Ping(new EmptyRequest()));
[Fact]
public void GetAllProfiles_Responde() =>
Responde("GetAllProfiles", () => _profiles.GetAllProfiles(new GetAllProfilesRequest()));
[Fact]
public void GetProfilesById_Responde()
{
var profileId = _probe.Int("profiles", "profileId");
if (profileId is null) return;
RespondeConDatos("GetProfilesById", () => _profiles.GetProfilesById(
new GetProfileByIdRequest { ProfileId = profileId.Value }));
}
[Fact]
public void GetUserPermissions_Responde()
{
// Un usuario que sí tiene perfil asignado; con uno cualquiera la respuesta legítima
// sería "sin permisos".
var userId = _probe.Int("users_profiles", "userId");
if (userId is null) return;
Responde("GetUserPermissions", () => _profiles.GetUserPermissions(
new GetUserPermissionsRequest { UserId = userId.Value }));
}
// ---------------------------------------------------------------
// CatalogService (Entity Framework, sin stored procedures)
// ---------------------------------------------------------------
[Fact]
public void GetServicesCatalogEF_Responde()
{
var idProductType = _probe.Int("product_type", "id");
if (idProductType is null) return;
Responde("GetServicesCatalogEF", () => _catalog.GetServicesCatalogEF(
new EntityFrameworkDAOServices.GetServicesByProductTypeRequest
{
ProductTypeId = idProductType.Value
}));
}
[Fact]
public void GetAllowedProductTypes_Responde()
{
var userId = _probe.Int("user_vcd", "userVcdId");
if (userId is null) return;
Responde("GetAllowedProductTypes", () => _catalog.GetAllowedProductTypes(
new GetAllowedProductTypesRequest { UserId = userId.Value }));
}
[Fact]
public void GetAllowedServices_Responde()
{
var userId = _probe.Int("user_vcd", "userVcdId");
if (userId is null) return;
Responde("GetAllowedServices", () => _catalog.GetAllowedServices(
new GetAllowedServicesRequest { UserId = userId.Value }));
}
// ---------------------------------------------------------------
///
/// Para rpcs invocados sin sembrar parámetros, o cuyo resultado depende de joins que
/// pueden legítimamente no producir filas. NotFound se acepta como respuesta válida.
///
private static void Responde(string rpc, Func call) => Invoca(rpc, call, exigeDatos: false);
///
/// Para rpcs que consultan una sola tabla con un id que acaba de
/// leer de esa misma tabla. Aquí NotFound sí es un fallo: sabemos que la fila
/// existe, así que "sin resultados" significa que la consulta está rota.
///
///
/// Esta distinción importa porque la capa legacy atrapa las excepciones de MySQL y las
/// convierte en NotFound: sin esta variante, un stored procedure inexistente se ve
/// desde fuera igual que una tabla vacía.
///
private static void RespondeConDatos(string rpc, Func call) => Invoca(rpc, call, exigeDatos: true);
private static void Invoca(string rpc, Func call, bool exigeDatos)
{
try
{
Assert.NotNull(call());
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.NotFound)
{
if (!exigeDatos) return;
Assert.Fail(
$"'{rpc}' devolvió NotFound con un id que existe en su propia tabla. " +
"O la consulta está rota, o el stored procedure no existe y la capa legacy " +
$"convirtió el error en NotFound. Detalle: {ex.Status.Detail?.Trim()}");
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unimplemented)
{
Assert.Fail(
$"'{rpc}' está declarado en el proto pero el servidor responde Unimplemented: " +
"o no hay override en C#, o la imagen desplegada es anterior al proto.");
}
catch (RpcException ex)
{
Assert.Fail(
$"'{rpc}' falló con {ex.StatusCode} — excepción no manejada en el servidor. " +
$"Detalle: {ex.Status.Detail}");
}
}
}