11
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
package webhook_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"superwork-tui/internal/webhook"
|
||||
)
|
||||
|
||||
// freePort returns a random available TCP port on localhost.
|
||||
func freePort(t *testing.T) int {
|
||||
t.Helper()
|
||||
l, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("freePort: %v", err)
|
||||
}
|
||||
port := l.Addr().(*net.TCPAddr).Port
|
||||
l.Close()
|
||||
return port
|
||||
}
|
||||
|
||||
func TestServerLifecycle_EventArrivesViaChannel(t *testing.T) {
|
||||
ch := make(chan webhook.Event, 4)
|
||||
srv := webhook.NewServer(func(ev webhook.Event) {
|
||||
ch <- ev
|
||||
})
|
||||
|
||||
port := freePort(t)
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
errCh <- srv.Start(ctx, addr)
|
||||
}()
|
||||
|
||||
// Wait until the server accepts connections.
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
|
||||
// POST a push event.
|
||||
payload := map[string]any{
|
||||
"ref": "refs/heads/main",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
fmt.Sprintf("http://localhost%s/webhook", addr), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatalf("build request: %v", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Gitea-Event", "push")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("POST /webhook: %v", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("want 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Expect a PushEvent on the channel.
|
||||
select {
|
||||
case ev := <-ch:
|
||||
pushEv, ok := ev.(webhook.PushEvent)
|
||||
if !ok {
|
||||
t.Fatalf("want PushEvent, got %T", ev)
|
||||
}
|
||||
if pushEv.Branch != "main" {
|
||||
t.Errorf("want Branch=main, got %q", pushEv.Branch)
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for PushEvent")
|
||||
}
|
||||
|
||||
// Cancel context → server shuts down; Start should return.
|
||||
cancel()
|
||||
select {
|
||||
case <-errCh:
|
||||
// OK — server stopped
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for server shutdown")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerLifecycle_IssueCommentEvent(t *testing.T) {
|
||||
ch := make(chan webhook.Event, 4)
|
||||
srv := webhook.NewServer(func(ev webhook.Event) {
|
||||
ch <- ev
|
||||
})
|
||||
|
||||
port := freePort(t)
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
go srv.Start(ctx, addr) //nolint:errcheck
|
||||
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
|
||||
payload := map[string]any{
|
||||
"action": "created",
|
||||
"issue": map[string]any{
|
||||
"number": float64(42),
|
||||
},
|
||||
"comment": map[string]any{
|
||||
"body": "<!-- spx:review=1 -->\nLooks good",
|
||||
"html_url": "https://example.com/comment/1",
|
||||
},
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
fmt.Sprintf("http://localhost%s/webhook", addr), bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Gitea-Event", "issue_comment")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("POST /webhook: %v", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
select {
|
||||
case ev := <-ch:
|
||||
ce, ok := ev.(webhook.IssueCommentEvent)
|
||||
if !ok {
|
||||
t.Fatalf("want IssueCommentEvent, got %T", ev)
|
||||
}
|
||||
if ce.IssueNumber != 42 {
|
||||
t.Errorf("want IssueNumber=42, got %d", ce.IssueNumber)
|
||||
}
|
||||
if !webhook.IsReviewMarker(ce.CommentBody) {
|
||||
t.Errorf("expected comment to contain review marker")
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for IssueCommentEvent")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user