116 lines
3.8 KiB
Go
116 lines
3.8 KiB
Go
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)
|
||
}
|
||
}
|