aboutsummaryrefslogtreecommitdiff
path: root/logs_test.go
diff options
context:
space:
mode:
authorLena <lena@omega>2026-08-01 00:00:00 +0000
committerLena <lena@omega>2026-08-01 00:00:00 +0000
commit3284b1c4906b90ca2e3864937c66d0fa30772ebf (patch)
treead799407ac7b0a2589a344751f4c5b3cf575d5c5 /logs_test.go
downloadgit-forge-cloudflare-3284b1c4906b90ca2e3864937c66d0fa30772ebf.tar.gz
Enter git-forge-cloudflareHEADmaster
Runs git-forge on Cloudflare Containers with R2 for durable storage. A Worker routes every request to one container through a Durable Object. Inside it, syncd mirrors the data directory to the bucket and restores it at boot, holding a write until it is durable. git-forge is neither modified nor vendored: the image builds it from its own repository at the commit named in forge.rev.
Diffstat (limited to 'logs_test.go')
-rw-r--r--logs_test.go66
1 files changed, 66 insertions, 0 deletions
diff --git a/logs_test.go b/logs_test.go
new file mode 100644
index 0000000..abef7eb
--- /dev/null
+++ b/logs_test.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "sync"
+ "testing"
+)
+
+// A gap in the log reads as nothing having happened, so a line that never
+// reaches the worker has to be counted and reported by one that does. The two
+// ways it fails are a send that errors and a send that is answered with an
+// error, and only the first of them looks like a failure to the client.
+func TestShipperReportsWhatDidNotLand(t *testing.T) {
+ var mu sync.Mutex
+ mode := "refuse"
+ var landed []string
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ mu.Lock()
+ defer mu.Unlock()
+ switch mode {
+ case "refuse":
+ http.Error(w, "no", http.StatusInternalServerError)
+ case "drop":
+ // A send that errors rather than answering.
+ if conn, _, err := w.(http.Hijacker).Hijack(); err == nil {
+ conn.Close()
+ }
+ default:
+ body, _ := io.ReadAll(r.Body)
+ landed = append(landed, string(body))
+ w.WriteHeader(http.StatusNoContent)
+ }
+ }))
+ defer srv.Close()
+
+ s := newShipper(srv.URL)
+ // close sends what is queued and waits, so each of these is one send.
+ for _, step := range []struct{ mode, line string }{
+ {"refuse", "answered with a 500"},
+ {"drop", "the connection went away"},
+ {"take", "this one lands"},
+ } {
+ mu.Lock()
+ mode = step.mode
+ mu.Unlock()
+ if _, err := s.Write([]byte(step.line + "\n")); err != nil {
+ t.Fatal(err)
+ }
+ s.close()
+ }
+
+ mu.Lock()
+ defer mu.Unlock()
+ if len(landed) != 1 {
+ t.Fatalf("the worker was handed %d bodies, want the one it accepted: %q", len(landed), landed)
+ }
+ if !strings.Contains(landed[0], "this one lands") {
+ t.Errorf("the line that should have landed is missing from %q", landed[0])
+ }
+ if !strings.Contains(landed[0], "2 log lines did not reach the worker") {
+ t.Errorf("the two lines that did not land are not reported in %q", landed[0])
+ }
+}