7ce227ddea
- migration 009 + columna deadline TEXT en cards - backend: CardPatch.HasDeadline, eventos deadline_set/deadline_cleared - KanbanCard: menu derecho con DatePicker, badge countdown con colores por ratio (azul>=50%, amarillo<50%, rojo<10%, red.9 overdue) - App.tsx: filtro "Con deadline", handleSetCardDeadline optimista, jump-to-card + highlight - CalendarView: popover por dia con seq_num + titulo, click navega a card en tablero - HistoryModal: render eventos deadline_set/deadline_cleared - .gitignore: *.log Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
95 lines
2.8 KiB
Go
95 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
)
|
|
|
|
func TestUpdateStickers_PersistsAndRoundTrips(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
col := executeTool(db, "create_column", mustJSON(t, map[string]string{"name": "X"})).Result.(*Column)
|
|
card := executeTool(db, "create_card", mustJSON(t, map[string]string{"column_id": col.ID, "title": "T"})).Result.(*Card)
|
|
|
|
if card.Stickers == nil || len(card.Stickers) != 0 {
|
|
t.Fatalf("expected empty stickers on new card, got %+v", card.Stickers)
|
|
}
|
|
|
|
stickers := []Sticker{
|
|
{Emoji: "🔥", X: 0.25, Y: 0.5},
|
|
{Emoji: "✅", X: 0.9, Y: 0.1},
|
|
}
|
|
if err := db.UpdateStickers(card.ID, stickers); err != nil {
|
|
t.Fatalf("UpdateStickers: %v", err)
|
|
}
|
|
|
|
cards, err := db.ListCardsWithTime()
|
|
if err != nil {
|
|
t.Fatalf("ListCardsWithTime: %v", err)
|
|
}
|
|
if len(cards) != 1 {
|
|
t.Fatalf("expected 1 card, got %d", len(cards))
|
|
}
|
|
got := cards[0].Stickers
|
|
if len(got) != 2 || got[0].Emoji != "🔥" || got[1].Emoji != "✅" {
|
|
t.Fatalf("sticker round-trip failed: %+v", got)
|
|
}
|
|
if got[0].X != 0.25 || got[0].Y != 0.5 {
|
|
t.Fatalf("coords lost: %+v", got[0])
|
|
}
|
|
}
|
|
|
|
func TestUpdateStickers_ClampAndDropEmpty(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
col := executeTool(db, "create_column", mustJSON(t, map[string]string{"name": "X"})).Result.(*Column)
|
|
card := executeTool(db, "create_card", mustJSON(t, map[string]string{"column_id": col.ID, "title": "T"})).Result.(*Card)
|
|
|
|
in := []Sticker{
|
|
{Emoji: " 🚀 ", X: -0.5, Y: 1.5},
|
|
{Emoji: "", X: 0.5, Y: 0.5},
|
|
{Emoji: "💀", X: 0.3, Y: 0.7},
|
|
}
|
|
if err := db.UpdateStickers(card.ID, in); err != nil {
|
|
t.Fatalf("UpdateStickers: %v", err)
|
|
}
|
|
cards, _ := db.ListCardsWithTime()
|
|
got := cards[0].Stickers
|
|
if len(got) != 2 {
|
|
t.Fatalf("expected empty emoji dropped, got %+v", got)
|
|
}
|
|
if got[0].Emoji != "🚀" || got[0].X != 0 || got[0].Y != 1 {
|
|
t.Fatalf("clamp failed: %+v", got[0])
|
|
}
|
|
if got[1].Emoji != "💀" {
|
|
t.Fatalf("expected 💀 second, got %+v", got[1])
|
|
}
|
|
}
|
|
|
|
func TestUpdateStickers_OverwriteAndClear(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
col := executeTool(db, "create_column", mustJSON(t, map[string]string{"name": "X"})).Result.(*Column)
|
|
card := executeTool(db, "create_card", mustJSON(t, map[string]string{"column_id": col.ID, "title": "T"})).Result.(*Card)
|
|
|
|
if err := db.UpdateStickers(card.ID, []Sticker{{Emoji: "🔥", X: 0.5, Y: 0.5}}); err != nil {
|
|
t.Fatalf("set: %v", err)
|
|
}
|
|
if err := db.UpdateStickers(card.ID, []Sticker{}); err != nil {
|
|
t.Fatalf("clear: %v", err)
|
|
}
|
|
cards, _ := db.ListCardsWithTime()
|
|
if len(cards[0].Stickers) != 0 {
|
|
t.Fatalf("expected cleared, got %+v", cards[0].Stickers)
|
|
}
|
|
}
|
|
|
|
func TestSticker_JSONShape(t *testing.T) {
|
|
s := Sticker{Emoji: "🎯", X: 0.1, Y: 0.2}
|
|
b, err := json.Marshal(s)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
want := `{"emoji":"🎯","x":0.1,"y":0.2}`
|
|
if string(b) != want {
|
|
t.Fatalf("got %s want %s", b, want)
|
|
}
|
|
}
|