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>
70 lines
2.4 KiB
Go
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)")
|
|
}
|
|
}
|