Files
sm-bm-plugin/classes/ics_test.go

116 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package classes_test
import (
"strings"
"testing"
"time"
"unicode/utf8"
"git.golem15.com/jakub/sm-bm-plugin/classes"
"git.golem15.com/jakub/sm-bm-plugin/models"
)
func TestEscapeText(t *testing.T) {
cases := map[string]string{
"zwykły tekst": "zwykły tekst",
`a\b`: `a\\b`,
"a;b,c": `a\;b\,c`,
"pierwsza\ndruga": `pierwsza\ndruga`,
"windows\r\nkoniec\rcr": `windows\nkonieccr`,
`już \n nie jest znakiem`: `już \\n nie jest znakiem`,
}
for in, want := range cases {
if got := classes.EscapeText(in); got != want {
t.Errorf("EscapeText(%q) = %q, want %q", in, got, want)
}
}
}
// checkFolded checks that a folded line has physical lines of at most 75
// octets, each valid UTF-8, continuation lines starting with a space, and
// that unfolding gives the input back.
func checkFolded(t *testing.T, in, folded string) {
t.Helper()
parts := strings.Split(folded, "\r\n")
for i, p := range parts {
if len(p) > 75 {
t.Errorf("physical line %d has %d octets: %q", i, len(p), p)
}
if !utf8.ValidString(p) {
t.Errorf("physical line %d splits a UTF-8 sequence: %q", i, p)
}
if i > 0 && !strings.HasPrefix(p, " ") {
t.Errorf("continuation line %d does not start with a space: %q", i, p)
}
}
if unfolded := strings.ReplaceAll(folded, "\r\n ", ""); unfolded != in {
t.Errorf("unfolded = %q, want %q", unfolded, in)
}
}
func TestFoldLine(t *testing.T) {
short := "SUMMARY:" + strings.Repeat("a", 67) // exactly 75 octets
if got := classes.FoldLine(short); got != short {
t.Errorf("a 75-octet line was folded: %q", got)
}
long := "SUMMARY:" + strings.Repeat("a", 68)
got := classes.FoldLine(long)
if got != "SUMMARY:"+strings.Repeat("a", 67)+"\r\n a" {
t.Errorf("76-octet line folded as %q", got)
}
checkFolded(t, long, got)
// Two-octet Polish letters at every offset, so a boundary falls inside a
// character at some point; and a three-octet en dash.
for pad := 0; pad < 4; pad++ {
in := "DESCRIPTION:" + strings.Repeat("x", pad) + strings.Repeat("zażółć gęślą jaźń – ", 12)
checkFolded(t, in, classes.FoldLine(in))
}
// Four-octet characters.
emoji := "SUMMARY:" + strings.Repeat("😀", 50)
checkFolded(t, emoji, classes.FoldLine(emoji))
}
func TestICSWriterUsesCRLFAndFolds(t *testing.T) {
var w classes.ICS
w.Begin("VCALENDAR")
w.Text("X-WR-CALNAME", "Bezpieczeństwo międzynarodowe; plan, zjazdy\nwiele linii "+strings.Repeat("ąę", 40))
w.UTC("DTSTART", time.Date(2026, 10, 3, 8, 0, 0, 0, models.Location))
w.Date("DTSTART", time.Date(2026, 10, 24, 0, 0, 0, 0, models.Location))
w.End("VCALENDAR")
out := string(w.Bytes())
if !strings.HasSuffix(out, "END:VCALENDAR\r\n") {
t.Errorf("does not end with END:VCALENDAR CRLF: %q", out)
}
if strings.Contains(strings.ReplaceAll(out, "\r\n", ""), "\n") || strings.Contains(strings.ReplaceAll(out, "\r\n", ""), "\r") {
t.Errorf("bare LF or CR in %q", out)
}
for _, line := range strings.Split(strings.TrimSuffix(out, "\r\n"), "\r\n") {
if len(line) > 75 {
t.Errorf("line of %d octets: %q", len(line), line)
}
}
unfolded := strings.ReplaceAll(out, "\r\n ", "")
for _, want := range []string{
`X-WR-CALNAME:Bezpieczeństwo międzynarodowe\; plan\, zjazdy\nwiele linii ąę`,
"DTSTART:20261003T060000Z\r\n",
"DTSTART;VALUE=DATE:20261024\r\n",
} {
if !strings.Contains(unfolded, want) {
t.Errorf("output lacks %q:\n%s", want, unfolded)
}
}
}
func TestFormatUTC(t *testing.T) {
// Saturday before the DST change (+02:00), Sunday after it (+01:00).
sat := time.Date(2026, 10, 24, 8, 0, 0, 0, models.Location)
sun := time.Date(2026, 10, 25, 8, 0, 0, 0, models.Location)
if got := classes.FormatUTC(sat); got != "20261024T060000Z" {
t.Errorf("Saturday = %s", got)
}
if got := classes.FormatUTC(sun); got != "20261025T070000Z" {
t.Errorf("Sunday = %s", got)
}
}