feat(soporte): leer solo los correos recientes, no todo el buzón

Sin ventana, la primera corrida convierte en tickets todo lo que haya sin leer,
que en una casilla de años es una avalancha. Ahora hay un desplegable de
antigüedad máxima —6h, 12h, 24h, 3 días o todo— y por defecto 12 horas.

El corte se hace en dos pasos porque IMAP no da para más: al servidor se le
pide SINCE con un día de margen (SINCE compara solo la fecha, no la hora) y el
corte fino por hora se aplica contra la fecha real de cada mensaje. Afinar el
SINCE sería perder los correos del borde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-17 19:39:00 -05:00
co-authored by Claude Opus 5
parent d7c266f111
commit 6afad025c2
5 changed files with 99 additions and 15 deletions
+36 -5
View File
@@ -5,6 +5,7 @@ import (
"net"
"strings"
"testing"
"time"
"github.com/emersion/go-imap/v2"
"github.com/emersion/go-imap/v2/imapclient"
@@ -71,7 +72,7 @@ func TestBajarNoLeidosDevuelveLosCorreos(t *testing.T) {
defer ln.Close()
defer c.Close()
correos, err := BajarNoLeidos(c, "INBOX")
correos, err := BajarNoLeidos(c, "INBOX", 0)
if err != nil {
t.Fatalf("BajarNoLeidos: %v", err)
}
@@ -96,7 +97,7 @@ func TestMarcarLeidoSacaElCorreoDeLaProximaCorrida(t *testing.T) {
defer ln.Close()
defer c.Close()
correos, err := BajarNoLeidos(c, "INBOX")
correos, err := BajarNoLeidos(c, "INBOX", 0)
if err != nil || len(correos) != 1 {
t.Fatalf("BajarNoLeidos: %v (%d correos)", err, len(correos))
}
@@ -104,7 +105,7 @@ func TestMarcarLeidoSacaElCorreoDeLaProximaCorrida(t *testing.T) {
t.Fatalf("MarcarLeido: %v", err)
}
otra, err := BajarNoLeidos(c, "INBOX")
otra, err := BajarNoLeidos(c, "INBOX", 0)
if err != nil {
t.Fatalf("segunda pasada: %v", err)
}
@@ -120,10 +121,10 @@ func TestBajarNoLeidosNoMarcaSolo(t *testing.T) {
defer ln.Close()
defer c.Close()
if _, err := BajarNoLeidos(c, "INBOX"); err != nil {
if _, err := BajarNoLeidos(c, "INBOX", 0); err != nil {
t.Fatal(err)
}
otra, err := BajarNoLeidos(c, "INBOX")
otra, err := BajarNoLeidos(c, "INBOX", 0)
if err != nil {
t.Fatal(err)
}
@@ -133,3 +134,33 @@ func TestBajarNoLeidosNoMarcaSolo(t *testing.T) {
}
var _ = imap.FlagSeen
func TestVentanaDeAntiguedad(t *testing.T) {
ahora := time.Now()
casos := []struct {
nombre string
recibido time.Time
horas int
want bool
}{
{"de hace una hora, ventana 12", ahora.Add(-1 * time.Hour), 12, true},
{"de hace 13 horas, ventana 12", ahora.Add(-13 * time.Hour), 12, false},
{"viejísimo, sin ventana", ahora.AddDate(-2, 0, 0), 0, true},
{"sin fecha del servidor", time.Time{}, 12, true},
}
for _, c := range casos {
if got := dentroDeLaVentana(c.recibido, c.horas); got != c.want {
t.Errorf("%s: dentroDeLaVentana = %v, want %v", c.nombre, got, c.want)
}
}
// El SINCE que se le pide al servidor tiene que ser MÁS amplio que la
// ventana real: IMAP compara solo la fecha, y afinar de más pierde correos.
c := criterioNoLeidos(12)
if !c.Since.Before(ahora.Add(-12 * time.Hour)) {
t.Errorf("Since = %v, debería ser anterior al corte real", c.Since)
}
if !criterioNoLeidos(0).Since.IsZero() {
t.Error("sin ventana no debería mandarse SINCE")
}
}