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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user