-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebhook_test.go
More file actions
138 lines (123 loc) · 4.3 KB
/
Copy pathwebhook_test.go
File metadata and controls
138 lines (123 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package main
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/renezander030/draftcat/internal/config"
)
func TestSchedulerTryStart(t *testing.T) {
sched := newScheduler([]config.PipelineConfig{
{Name: "p1", Schedule: "webhook"},
})
if ok, reason := sched.TryStart("p1"); !ok {
t.Fatalf("first TryStart should succeed, got reason %q", reason)
}
if ok, reason := sched.TryStart("p1"); ok || reason != "pipeline is already running" {
t.Errorf("second TryStart should report already-running, got ok=%v reason=%q", ok, reason)
}
sched.SetRunning("p1", false)
if ok, _ := sched.TryStart("p1"); !ok {
t.Error("TryStart should succeed after SetRunning(false)")
}
sched.SetRunning("p1", false)
sched.Pause("p1")
if ok, reason := sched.TryStart("p1"); ok || reason != "pipeline is paused" {
t.Errorf("paused pipeline should not start, got ok=%v reason=%q", ok, reason)
}
if ok, reason := sched.TryStart("nope"); ok || reason != "unknown pipeline" {
t.Errorf("unknown pipeline should report unknown, got ok=%v reason=%q", ok, reason)
}
}
func TestWebhookPipelineNotAutoScheduled(t *testing.T) {
sched := newScheduler([]config.PipelineConfig{
{Name: "hooked", Schedule: "webhook"},
{Name: "manualp", Schedule: "manual"},
})
// A webhook/manual pipeline must never be returned as timer-due.
if due := sched.GetDue(); len(due) != 0 {
t.Errorf("expected no auto-due pipelines, got %v", due)
}
if isAutoSchedule("webhook") {
t.Error("'webhook' must not be an auto schedule")
}
}
// testHandler builds a webhook handler with one zero-step pipeline ("ping")
// that is webhook-triggerable. A zero-step pipeline lets the 202 path run to
// completion without any LLM or operator-channel call.
func testHandler(t *testing.T) (http.Handler, *Scheduler) {
t.Helper()
cfg := &config.Config{
Pipelines: []config.PipelineConfig{
{Name: "ping", Schedule: "webhook"},
},
}
cfg.Webhook.Enabled = true
cfg.Webhook.SetSecret("s3cret")
sched := newScheduler(cfg.Pipelines)
budget := &BudgetTracker{dayStart: time.Now()}
h := newWebhookHandler(cfg, sched, budget, &TGBot{}, nil)
return h, sched
}
func post(h http.Handler, path, auth, body string) *httptest.ResponseRecorder {
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body))
if auth != "" {
req.Header.Set("Authorization", auth)
}
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
return rr
}
func TestWebhookRejectsBadMethod(t *testing.T) {
h, _ := testHandler(t)
req := httptest.NewRequest(http.MethodGet, "/hooks/ping", nil)
req.Header.Set("Authorization", "Bearer s3cret")
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
if rr.Code != http.StatusMethodNotAllowed {
t.Errorf("GET should be 405, got %d", rr.Code)
}
}
func TestWebhookRejectsBadAuth(t *testing.T) {
h, _ := testHandler(t)
if rr := post(h, "/hooks/ping", "", ""); rr.Code != http.StatusUnauthorized {
t.Errorf("missing auth should be 401, got %d", rr.Code)
}
if rr := post(h, "/hooks/ping", "Bearer wrong", ""); rr.Code != http.StatusUnauthorized {
t.Errorf("wrong token should be 401, got %d", rr.Code)
}
}
func TestWebhookUnknownPipeline(t *testing.T) {
h, _ := testHandler(t)
if rr := post(h, "/hooks/missing", "Bearer s3cret", ""); rr.Code != http.StatusNotFound {
t.Errorf("unknown pipeline should be 404, got %d", rr.Code)
}
}
func TestWebhookConflictWhenRunning(t *testing.T) {
h, sched := testHandler(t)
// Claim the pipeline so the next trigger sees it as already running.
if ok, _ := sched.TryStart("ping"); !ok {
t.Fatal("could not pre-claim pipeline")
}
if rr := post(h, "/hooks/ping", "Bearer s3cret", ""); rr.Code != http.StatusConflict {
t.Errorf("running pipeline should be 409, got %d", rr.Code)
}
}
func TestWebhookAcceptsValid(t *testing.T) {
h, sched := testHandler(t)
rr := post(h, "/hooks/ping", "Bearer s3cret", `{"hello":"world"}`)
if rr.Code != http.StatusAccepted {
t.Fatalf("valid trigger should be 202, got %d (%s)", rr.Code, rr.Body.String())
}
// The zero-step pipeline runs in a goroutine; wait for it to release.
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
if ok, _ := sched.TryStart("ping"); ok {
sched.SetRunning("ping", false)
return
}
time.Sleep(10 * time.Millisecond)
}
t.Error("pipeline did not finish / release running flag within timeout")
}