test: cron schedule + webhook URL + canvas layout coverage (#73)

- calculateNextRun: field-count, garbage, bad-zone errors; CRON_TZ
  actually shifts the instant (UTC vs Asia/Tokyo)
- validWebhookURL: http/https only, host required, whitespace trimmed
- canvas layout math extracted to canvas/layout.ts (component files may
  only export components) and covered: rectsOverlap gap zone,
  resolveFreeSpot grid-snap/collision/bounds-fallback, groupSlot
  fit/append/grow/ignore-dragged, buildMetadataFromFlow serialization

Generated with [Devin](https://devin.ai)

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomáš DvořákandDevin authored and GitHub committed 2026-10-10 02:34:51 +02:00
1 parent c4307034ec
commit a6b8397dd8
5 files changed
+408 -159

No files matched your search

+30
View File
@@ -31,3 +31,33 @@ func TestWebhookEventMatches(t *testing.T) {
}
}
}
func TestValidWebhookURL(t *testing.T) {
cases := []struct {
name string
url string
wantErr bool
}{
{"https", "https://hooks.example.com/wh/abc", false},
{"http", "http://internal.lan:8080/notify", false},
{"whitespace trimmed", " https://example.com/hook ", false},
{"with query", "https://example.com/hook?token=x", false},
{"ftp rejected", "ftp://files.example.com/x", true},
{"no scheme", "example.com/hook", true},
{"bare scheme", "https://", true},
{"empty", "", true},
{"javascript rejected", "javascript:alert(1)", true},
{"host empty", "http:///path-only", true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := validWebhookURL(tc.url)
if tc.wantErr && err == nil {
t.Errorf("validWebhookURL(%q) = nil, want error", tc.url)
}
if !tc.wantErr && err != nil {
t.Errorf("validWebhookURL(%q) = %v, want nil", tc.url, err)
}
})
}
}