ENRE_WEB/gestor/reglas_test.go
SITO ce3127baff Sección «Pedir evento» con gestor de reservas y calendario legible en móvil
- /pedir/: calendario de fechas pedidas (negro) y confirmadas (verde),
  findes tranqui y con ruido, solo findes por defecto con opción de ver la
  semana entera. Se pincha un día libre y, sin sesión, se pide entrar y se
  vuelve al formulario con la fecha puesta.
- Gestor en Go (gestor/): cuentas por colectivo con argon2id, sesiones y
  CSRF, cupo y ventana de la asamblea, caducidades, registro append-only.
- La cuenta de la asamblea confirma, rechaza, cambia turnos de finde y en
  «Todas las fechas» quita o modifica cualquier evento.
- Orden «cuenta» y «clave» con -clave y -definitiva para fijar contraseñas
  desde el servidor; ninguna contraseña va en el repositorio.
- Botón «Pedir evento» arriba de la barra lateral en todas las páginas.
- Calendario semanal en móvil: lista de días con nombre y hora de cada cita.
- Fuentes servidas desde fuentes/: fuera Google Fonts en todas las páginas.
2026-09-23 11:32:25 +02:00

250 lines
8.3 KiB
Go

package main
import (
"strings"
"testing"
)
const hoy = Dia("2026-09-22") // martes
func solicitud(usuario string, desde, hasta Dia) Solicitud {
return Solicitud{
Solicitante: usuario,
Rol: RolInterno,
Titulo: "Concierto de prueba",
Desde: desde,
Hasta: hasta,
Hora: "20:00",
}
}
func evento(id, usuario string, desde, hasta Dia, estado Estado) Evento {
return Evento{
ID: id,
Titulo: "Evento " + id,
Solicitante: usuario,
Desde: desde,
Hasta: hasta,
Estado: estado,
Creado: hoy.MustTime(),
}
}
func TestVentanaTresMeses(t *testing.T) {
rg := ReglasPorDefecto()
// 2026-10-02 es viernes, dentro de los tres meses.
if err := rg.Validar(solicitud("hacklab", "2026-10-02", "2026-10-02"), nil, hoy); err != nil {
t.Fatalf("una fecha dentro de la ventana deberia valer: %v", err)
}
// 2027-01-01 es viernes y cae fuera: hoy + 3 meses es 2026-12-22.
err := rg.Validar(solicitud("hacklab", "2027-01-01", "2027-01-01"), nil, hoy)
if err == nil || !strings.Contains(err.Error(), "3 meses") {
t.Fatalf("una fecha a mas de 3 meses tiene que fallar por la ventana, dio: %v", err)
}
}
func TestAntelacionMinima(t *testing.T) {
rg := ReglasPorDefecto()
// 2026-09-25 es el viernes de esta semana: a 3 dias, caducaria al instante.
err := rg.Validar(solicitud("hacklab", "2026-09-25", "2026-09-25"), nil, hoy)
if err == nil || !strings.Contains(err.Error(), "antelación") {
t.Fatalf("pedir con menos de 7 dias tiene que fallar, dio: %v", err)
}
}
func TestCupoTresDiasEnLaVentana(t *testing.T) {
rg := ReglasPorDefecto()
// Tres dias ya cogidos por el mismo colectivo: uno confirmado, dos solicitados.
eventos := []Evento{
evento("a", "hacklab", "2026-10-02", "2026-10-02", Confirmado),
evento("b", "hacklab", "2026-10-10", "2026-10-11", Solicitado),
}
err := rg.Validar(solicitud("hacklab", "2026-10-16", "2026-10-16"), eventos, hoy)
if err == nil || !strings.Contains(err.Error(), "cupo") {
t.Fatalf("el cuarto dia tiene que fallar por cupo, dio: %v", err)
}
// El cupo es por colectivo: otro colectivo si puede coger ese dia.
if err := rg.Validar(solicitud("feminismos", "2026-10-16", "2026-10-16"), eventos, hoy); err != nil {
t.Fatalf("otro colectivo no deberia arrastrar el cupo del primero: %v", err)
}
// Los dias rechazados o caducados no cuentan.
eventos[1].Estado = Caducado
if err := rg.Validar(solicitud("hacklab", "2026-10-16", "2026-10-17"), eventos, hoy); err != nil {
t.Fatalf("un evento caducado no deberia gastar cupo: %v", err)
}
}
func TestCupoNoCuentaFechasPasadas(t *testing.T) {
rg := ReglasPorDefecto()
viejos := []Evento{evento("v", "hacklab", "2026-08-07", "2026-08-09", Confirmado)}
if err := rg.Validar(solicitud("hacklab", "2026-10-02", "2026-10-04"), viejos, hoy); err != nil {
t.Fatalf("lo que ya paso no gasta cupo: %v", err)
}
}
func TestSinSolapes(t *testing.T) {
rg := ReglasPorDefecto()
eventos := []Evento{evento("a", "hacklab", "2026-10-03", "2026-10-03", Solicitado)}
err := rg.Validar(solicitud("feminismos", "2026-10-02", "2026-10-04"), eventos, hoy)
if err == nil || !strings.Contains(err.Error(), "ya está cogido") {
t.Fatalf("un dia solicitado por otro tiene que bloquear, dio: %v", err)
}
// Al caducar se libera la fecha.
eventos[0].Estado = Caducado
if err := rg.Validar(solicitud("feminismos", "2026-10-02", "2026-10-04"), eventos, hoy); err != nil {
t.Fatalf("una fecha caducada tiene que quedar libre: %v", err)
}
}
func TestSoloFindesSalvoLaEnre(t *testing.T) {
rg := ReglasPorDefecto()
// 2026-10-06 es martes.
err := rg.Validar(solicitud("hacklab", "2026-10-06", "2026-10-06"), nil, hoy)
if err == nil || !strings.Contains(err.Error(), "no es día reservable") {
t.Fatalf("un martes no es reservable para un colectivo, dio: %v", err)
}
s := solicitud("enre", "2026-10-06", "2026-10-06")
s.Rol = RolEnre
if err := rg.Validar(s, nil, hoy); err != nil {
t.Fatalf("la enre si puede poner cualquier dia: %v", err)
}
}
func TestLaEnreNoTieneCupoNiVentana(t *testing.T) {
rg := ReglasPorDefecto()
eventos := []Evento{
evento("a", "enre", "2026-10-02", "2026-10-04", Confirmado),
evento("b", "enre", "2026-10-09", "2026-10-11", Confirmado),
}
s := solicitud("enre", "2027-06-04", "2027-06-06")
s.Rol = RolEnre
if err := rg.Validar(s, eventos, hoy); err != nil {
t.Fatalf("la enre no deberia topar con cupo ni ventana: %v", err)
}
}
func TestAmadrinadoPideContacto(t *testing.T) {
rg := ReglasPorDefecto()
s := solicitud("hacklab", "2026-10-02", "2026-10-02")
s.Amadrinado = "Colectivo de fuera"
err := rg.Validar(s, nil, hoy)
if err == nil || !strings.Contains(err.Error(), "contacto") {
t.Fatalf("amadrinar sin contacto tiene que fallar, dio: %v", err)
}
s.Contacto = "fuera@riseup.net"
if err := rg.Validar(s, nil, hoy); err != nil {
t.Fatalf("con contacto deberia valer: %v", err)
}
}
func TestCaducidad(t *testing.T) {
rg := ReglasPorDefecto()
ahora := hoy.MustTime()
cerca := evento("a", "hacklab", "2026-09-27", "2026-09-27", Solicitado)
if m := rg.Caducidad(cerca, ahora); m == "" || !strings.Contains(m, "menos de 7") {
t.Fatalf("a 5 dias sin confirmar tiene que caducar, dio %q", m)
}
lejos := evento("b", "hacklab", "2026-11-06", "2026-11-06", Solicitado)
if m := rg.Caducidad(lejos, ahora); m != "" {
t.Fatalf("una solicitud reciente y lejana no caduca, dio %q", m)
}
vieja := lejos
vieja.Creado = ahora.AddDate(0, 0, -31)
if m := rg.Caducidad(vieja, ahora); m == "" || !strings.Contains(m, "esperando") {
t.Fatalf("31 dias esperando tiene que caducar, dio %q", m)
}
confirmada := cerca
confirmada.Estado = Confirmado
if m := rg.Caducidad(confirmada, ahora); m != "" {
t.Fatalf("un evento confirmado no caduca nunca, dio %q", m)
}
}
func TestTurnoDeFinde(t *testing.T) {
rg := ReglasPorDefecto() // ancla 2026-09-25, turno ruido
casos := []struct {
dia Dia
quer Turno
}{
{"2026-09-25", Ruido}, // viernes ancla
{"2026-09-26", Ruido}, // sabado del mismo finde
{"2026-09-27", Ruido}, // domingo del mismo finde
{"2026-10-02", Tranqui}, // finde siguiente
{"2026-10-09", Ruido},
{"2026-10-16", Tranqui},
{"2026-09-18", Tranqui}, // hacia atras tambien alterna
}
for _, c := range casos {
if got := rg.TurnoDe(c.dia, nil); got != c.quer {
t.Errorf("%s: turno %q, esperaba %q", c.dia, got, c.quer)
}
}
// Un dia de entre semana no tiene turno.
if got := rg.TurnoDe("2026-10-06", nil); got != "" {
t.Errorf("un martes no tiene turno de finde, dio %q", got)
}
// La excepcion escrita a mano manda sobre la alternancia.
exc := map[Dia]Turno{"2026-10-02": Ruido}
if got := rg.TurnoDe("2026-10-03", exc); got != Ruido {
t.Errorf("la excepcion del viernes tiene que valer para todo el finde, dio %q", got)
}
}
func TestRangoYDiaISO(t *testing.T) {
dias := Rango("2026-10-02", "2026-10-04")
if len(dias) != 3 || dias[0] != "2026-10-02" || dias[2] != "2026-10-04" {
t.Fatalf("rango mal calculado: %v", dias)
}
if Rango("2026-10-04", "2026-10-02") != nil {
t.Fatal("un rango invertido tiene que dar nada")
}
if got := Dia("2026-10-02").DiaISO(); got != 5 {
t.Fatalf("2026-10-02 es viernes, iso 5, dio %d", got)
}
if got := ViernesDe("2026-10-04"); got != "2026-10-02" {
t.Fatalf("el viernes del domingo 4 es el 2, dio %q", got)
}
}
func TestVentanaDevuelveTresMeses(t *testing.T) {
rg := ReglasPorDefecto()
primero, ultimo := rg.Ventana(hoy)
if primero != "2026-09-29" {
t.Errorf("primera fecha pedible %q, esperaba 2026-09-29", primero)
}
if ultimo != "2026-12-22" {
t.Errorf("ultima fecha pedible %q, esperaba 2026-12-22", ultimo)
}
}
func TestModificarNoChocaConsigoMismo(t *testing.T) {
rg := ReglasPorDefecto()
eventos := []Evento{
evento("a", "hacklab", "2026-10-02", "2026-10-03", Confirmado),
evento("b", "vivienda", "2026-10-09", "2026-10-09", Solicitado),
}
// La asamblea mueve la hora del evento «a» sin cambiarle los días.
s := solicitud("hacklab", "2026-10-02", "2026-10-03")
s.Rol, s.Excluir = RolEnre, "a"
if err := rg.Validar(s, eventos, hoy); err != nil {
t.Fatalf("modificar un evento en sus propios días deberia valer: %v", err)
}
// Pero no puede llevarlo encima del evento «b».
s.Desde, s.Hasta = "2026-10-09", "2026-10-09"
if err := rg.Validar(s, eventos, hoy); err == nil || !strings.Contains(err.Error(), "cogido") {
t.Fatalf("mover «a» encima de «b» deberia fallar por solape, dio %v", err)
}
}