revert(tests): eliminar tests de reserva/concurrencia/secuencialidad ADM-008

Eliminar SecuenciaComprobanteTests, ReservarNumeroCommandHandlerTests,
GetProximoNumeroQueryHandlerTests y 7 tests de integración en
PuntosDeVentaControllerTests (reserva/proximo/concurrencia/secuencialidad).
SqlTestFixture ahora limpia SecuenciaComprobante+SP si existen (drops idempotentes)
y solo crea PuntoDeVenta + temporal table.
This commit is contained in:
2026-04-17 14:16:21 -03:00
parent 6be637b4cf
commit 6458ee0106
5 changed files with 39 additions and 612 deletions

View File

@@ -125,13 +125,6 @@ public sealed class PuntosDeVentaControllerTests : IAsyncLifetime
"SELECT Id FROM dbo.Medio WHERE Codigo = @Codigo", new { Codigo = codigo });
if (id is null) return;
// Delete SecuenciaComprobante for PuntosDeVenta of this Medio (no versioning)
await conn.ExecuteAsync("""
DELETE sc FROM dbo.SecuenciaComprobante sc
INNER JOIN dbo.PuntoDeVenta pdv ON pdv.Id = sc.PuntoDeVentaId
WHERE pdv.MedioId = @id
""", new { id });
// Delete dependent PuntosDeVenta (disable versioning to also clear history)
await conn.ExecuteAsync("ALTER TABLE dbo.PuntoDeVenta SET (SYSTEM_VERSIONING = OFF)");
await conn.ExecuteAsync("DELETE FROM dbo.PuntoDeVenta_History WHERE MedioId = @id", new { id });
@@ -604,238 +597,4 @@ public sealed class PuntosDeVentaControllerTests : IAsyncLifetime
}
}
// ── SECUENCIAS: RESERVAR ──────────────────────────────────────────────────
/// <summary>T5.3 — Primera reserva inicializa en 1.</summary>
[Fact]
public async Task ReservarNumero_FirstReservation_Returns1()
{
const string medioCodigo = "ADMS08_MED_RSV1";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Reservar 1", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Reservar First", token);
using var req = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/secuencias/FacturaA/reservar", bearerToken: token);
var resp = await _client.SendAsync(req);
Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
var json = await resp.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal(1, json.GetProperty("numeroReservado").GetInt32());
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
/// <summary>T5.3 — 409 punto_de_venta_inactivo al reservar en PdV inactivo.</summary>
[Fact]
public async Task ReservarNumero_WhenPdvInactive_Returns409PdvInactivo()
{
const string medioCodigo = "ADMS08_MED_RSVI";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Reservar Inactivo", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Reservar Inactivo", token);
// Deactivate PdV
using var deactReq = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/deactivate", bearerToken: token);
await _client.SendAsync(deactReq);
using var req = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/secuencias/FacturaA/reservar", bearerToken: token);
var resp = await _client.SendAsync(req);
Assert.Equal(HttpStatusCode.Conflict, resp.StatusCode);
var json = await resp.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal("punto_de_venta_inactivo", json.GetProperty("error").GetString());
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
/// <summary>T5.3 — 409 medio_inactivo al reservar con Medio inactivo.</summary>
[Fact]
public async Task ReservarNumero_WhenMedioInactive_Returns409MedioInactivo()
{
const string medioCodigo = "ADMS08_MED_RSVMI";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Reservar MedioInactivo", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Reservar MedioInact", token);
// Deactivate medio
using var deactMedioReq = BuildRequest(HttpMethod.Post, $"{MediosEndpoint}/{medioId}/deactivate", bearerToken: token);
await _client.SendAsync(deactMedioReq);
using var req = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/secuencias/FacturaA/reservar", bearerToken: token);
var resp = await _client.SendAsync(req);
Assert.Equal(HttpStatusCode.Conflict, resp.StatusCode);
var json = await resp.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal("medio_inactivo", json.GetProperty("error").GetString());
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
// ── SECUENCIAS: PROXIMO ───────────────────────────────────────────────────
/// <summary>T5.3 — GetProximo es read-only: no modifica UltimoNumero.</summary>
[Fact]
public async Task GetProximoNumero_DoesNotChangeState()
{
const string medioCodigo = "ADMS08_MED_PROX";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Proximo", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Proximo", token);
// Reserve once to establish state
using var rsv = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/secuencias/FacturaA/reservar", bearerToken: token);
await _client.SendAsync(rsv);
// GetProximo twice — should return 2 both times
using var req1 = BuildRequest(HttpMethod.Get, $"{Endpoint}/{pdvId}/secuencias/FacturaA/proximo", bearerToken: token);
var resp1 = await _client.SendAsync(req1);
Assert.Equal(HttpStatusCode.OK, resp1.StatusCode);
var json1 = await resp1.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal(2, json1.GetProperty("proximoNumero").GetInt32());
using var req2 = BuildRequest(HttpMethod.Get, $"{Endpoint}/{pdvId}/secuencias/FacturaA/proximo", bearerToken: token);
var resp2 = await _client.SendAsync(req2);
var json2 = await resp2.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal(2, json2.GetProperty("proximoNumero").GetInt32());
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
/// <summary>T5.3 — GetProximo para fila inexistente devuelve 1.</summary>
[Fact]
public async Task GetProximoNumero_WhenNoSequenceExists_Returns1()
{
const string medioCodigo = "ADMS08_MED_PROX1";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Proximo 1", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Proximo First", token);
using var req = BuildRequest(HttpMethod.Get, $"{Endpoint}/{pdvId}/secuencias/FacturaB/proximo", bearerToken: token);
var resp = await _client.SendAsync(req);
Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
var json = await resp.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal(1, json.GetProperty("proximoNumero").GetInt32());
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
// ── T5.4 — Concurrencia ───────────────────────────────────────────────────
/// <summary>
/// T5.4 — 50 tasks paralelas reservando para mismo PdV + TipoComprobante
/// deben producir 50 números distintos cubriendo {1..50}.
/// </summary>
[Fact]
public async Task ReservarNumero_50ConcurrentReservations_ProducesNoDuplicates()
{
const string medioCodigo = "ADMS08_CONC50";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Concurrencia 50", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Concurrencia 50", token);
const int taskCount = 50;
var tasks = Enumerable.Range(0, taskCount)
.Select(_ => Task.Run(async () =>
{
// Each task creates its own HttpClient to avoid sharing
// the shared _client which is not thread-safe for concurrent requests.
// Use BuildRequest on a shared client IS safe since HttpClient is thread-safe
// for concurrent operations as long as each message is distinct.
using var req = new HttpRequestMessage(HttpMethod.Post, $"{Endpoint}/{pdvId}/secuencias/FacturaA/reservar");
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var resp = await _client.SendAsync(req);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadFromJsonAsync<JsonElement>();
return json.GetProperty("numeroReservado").GetInt32();
}))
.ToList();
var results = await Task.WhenAll(tasks);
// All 50 numbers must be present exactly once
Assert.Equal(taskCount, results.Length);
Assert.Equal(taskCount, results.Distinct().Count());
var expected = Enumerable.Range(1, taskCount).ToHashSet();
var actual = results.ToHashSet();
Assert.Equal(expected, actual);
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
// ── T5.5 — Secuencialidad ─────────────────────────────────────────────────
/// <summary>
/// T5.5 — 100 reservas en serie para mismo PdV + TipoComprobante
/// deben devolver {1, 2, 3, ..., 100} en orden.
/// </summary>
[Fact]
public async Task ReservarNumero_100SerialReservations_ProducesSequentialNumbers()
{
const string medioCodigo = "ADMS08_SEQ100";
var token = await GetAdminTokenAsync();
try
{
var medioId = await CreateMedioAsync(medioCodigo, "Medio PDV Secuencial 100", token);
var pdvId = await CreatePdvAsync(medioId, 1, "PdV Secuencial 100", token);
const int count = 100;
var results = new List<int>(count);
for (int i = 0; i < count; i++)
{
using var req = BuildRequest(HttpMethod.Post, $"{Endpoint}/{pdvId}/secuencias/FacturaA/reservar", bearerToken: token);
var resp = await _client.SendAsync(req);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadFromJsonAsync<JsonElement>();
results.Add(json.GetProperty("numeroReservado").GetInt32());
}
// Verify sequential: {1, 2, 3, ..., 100}
var expected = Enumerable.Range(1, count).ToList();
Assert.Equal(expected, results);
}
finally
{
await DeleteMedioIfExistsAsync(medioCodigo);
}
}
}