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) } } func TestExternasSinCupoPeroConColectivo(t *testing.T) { rg := ReglasPorDefecto() eventos := []Evento{ evento("a", "externas", "2026-10-02", "2026-10-04", Solicitado), evento("b", "externas", "2026-10-09", "2026-10-11", Solicitado), } s := solicitud("externas", "2026-10-16", "2026-10-16") s.Rol = RolExternas if err := rg.Validar(s, eventos, hoy); err == nil || !strings.Contains(err.Error(), "colectivo de fuera") { t.Fatalf("sin decir de qué colectivo es deberia fallar, dio %v", err) } // Con seis días ya apuntados, un séptimo vale: externas no tiene cupo. s.Amadrinado, s.Contacto = "Colectivo Sin Techo", "sintecho@example.org" if err := rg.Validar(s, eventos, hoy); err != nil { t.Fatalf("externas no deberia tener cupo: %v", err) } // Pero sigue sin poder pedir entre semana. s.Desde, s.Hasta = "2026-10-14", "2026-10-14" if err := rg.Validar(s, eventos, hoy); err == nil { t.Fatal("externas no deberia poder pedir un miércoles") } }