calcomblock/block_resolver_test.go
Alex Dunmow 7c7bceb9d7 fix!: absolute routes + live captcha requirement — v2.0.3
Two wasm-era regressions fixed:

Routes: the wasm host forwards the FULL request path with no prefix
strip, but every route was registered relative — the plugin's entire
HTTP surface (slots/book/cancel/date-grid/admin) 404'd at runtime on
every instance since 2.0.0. All routes now register under
/api/plugins/calcomblock (testplugin's httpBase convention), pinned by
a route-matching test.

Captcha requirement: blockConfig was hardcoded nil (no capability
exposed published block content), so bookingRequiresCaptcha always
reported false — the widget never rendered and the gate never ran.
publishedBlockResolver now answers it via pluginsdk v0.2.4's
content.published_block_configs: any currently-published calcom:booking
block matching the posted username+eventTypeSlug with
captchaEnabled=true demands the host-stamped X-Bn-Verified-Captcha
header; an empty config username applies to any posted username.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:47:59 +08:00

70 lines
2.4 KiB
Go

package main
import (
"context"
"errors"
"testing"
)
type fakeConfigsSource struct {
configs []map[string]any
err error
gotKey string
}
func (f *fakeConfigsSource) PublishedBlockConfigs(_ context.Context, blockKey string) ([]map[string]any, error) {
f.gotKey = blockKey
return f.configs, f.err
}
func TestPublishedBlockResolverRequiresCaptchaOnMatch(t *testing.T) {
src := &fakeConfigsSource{configs: []map[string]any{
{"username": "other", "eventTypeSlug": "30min", "captchaEnabled": true},
{"username": "Alice", "eventTypeSlug": "30min", "captchaEnabled": true},
}}
r := publishedBlockResolver{source: src}
got, err := r.CalcomBookingRequiresCaptcha(context.Background(), "alice", "30min")
if err != nil || !got {
t.Fatalf("RequiresCaptcha = %v, %v; want true (case-insensitive username match)", got, err)
}
if src.gotKey != "calcom:booking" {
t.Fatalf("queried block key %q, want calcom:booking", src.gotKey)
}
}
func TestPublishedBlockResolverNoRequirementWhenDisabledOrUnmatched(t *testing.T) {
src := &fakeConfigsSource{configs: []map[string]any{
{"username": "alice", "eventTypeSlug": "30min", "captchaEnabled": false},
{"username": "alice", "eventTypeSlug": "60min", "captchaEnabled": true},
}}
r := publishedBlockResolver{source: src}
if got, _ := r.CalcomBookingRequiresCaptcha(context.Background(), "alice", "30min"); got {
t.Fatal("RequiresCaptcha = true; want false (disabled for 30min, 60min doesn't match)")
}
}
func TestPublishedBlockResolverEmptyConfigUsernameMatchesAny(t *testing.T) {
// A block may omit username (falls back to the plugin's default account);
// the requirement then applies to the event type for any posted username —
// erring toward requiring captcha, never stripping it.
src := &fakeConfigsSource{configs: []map[string]any{
{"eventTypeSlug": "30min", "captchaEnabled": true},
}}
r := publishedBlockResolver{source: src}
if got, _ := r.CalcomBookingRequiresCaptcha(context.Background(), "whoever", "30min"); !got {
t.Fatal("RequiresCaptcha = false; want true (config without username applies to any)")
}
}
func TestPublishedBlockResolverPropagatesError(t *testing.T) {
src := &fakeConfigsSource{err: errors.New("hostcall failed")}
r := publishedBlockResolver{source: src}
if _, err := r.CalcomBookingRequiresCaptcha(context.Background(), "alice", "30min"); err == nil {
t.Fatal("expected error to propagate (caller decides fail-open policy)")
}
}