marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

main_test.go (2271B)


      1 package main
      2 
      3 import (
      4 	"bytes"
      5 	"context"
      6 	"log"
      7 	"net/http"
      8 	"net/http/httptest"
      9 	"os"
     10 	"strings"
     11 	"syscall"
     12 	"testing"
     13 	"time"
     14 )
     15 
     16 // Test that the logging middleware delegates to the next handler
     17 // and emits a log line containing the method and path.
     18 func TestLoggingMiddleware_EmitsAndDelegates(t *testing.T) {
     19 	// Not parallel: modifies global logger output
     20 	called := false
     21 	next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
     22 		called = true
     23 		w.WriteHeader(http.StatusNoContent)
     24 	})
     25 
     26 	var buf bytes.Buffer
     27 	log.SetOutput(&buf)
     28 	defer log.SetOutput(os.Stderr)
     29 
     30 	h := logging(next)
     31 
     32 	rr := httptest.NewRecorder()
     33 	req := httptest.NewRequest(http.MethodGet, "/hello", nil)
     34 
     35 	h.ServeHTTP(rr, req)
     36 
     37 	if !called {
     38 		t.Fatalf("expected next handler to be called")
     39 	}
     40 	if rr.Code != http.StatusNoContent {
     41 		t.Fatalf("expected status %d, got %d", http.StatusNoContent, rr.Code)
     42 	}
     43 
     44 	out := buf.String()
     45 	if !strings.Contains(out, "GET") || !strings.Contains(out, "/hello") {
     46 		t.Fatalf("expected log to contain method and path, got: %q", out)
     47 	}
     48 }
     49 
     50 // TestMainLifecycle starts main() with ephemeral address and temp DB,
     51 // waits for the listening log, then sends SIGTERM to trigger graceful shutdown.
     52 func TestMainLifecycle_StartsAndShutsDown(t *testing.T) {
     53 	// Not parallel: uses Setenv and sends process signals
     54 	var buf bytes.Buffer
     55 	log.SetOutput(&buf)
     56 	defer log.SetOutput(os.Stderr)
     57 
     58 	tempDB := t.TempDir() + "/test.db"
     59 	// Provide flags to main via os.Args.
     60 	oldArgs := os.Args
     61 	os.Args = []string{"marky", "--addr", "127.0.0.1:0", "--db", tempDB}
     62 	defer func() { os.Args = oldArgs }()
     63 
     64 	done := make(chan struct{})
     65 	go func() {
     66 		main()
     67 		close(done)
     68 	}()
     69 
     70 	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
     71 	defer cancel()
     72 	for {
     73 		select {
     74 		case <-ctx.Done():
     75 			t.Fatalf("did not observe listening log: %q", buf.String())
     76 		default:
     77 			if strings.Contains(buf.String(), "marky API listening on") {
     78 				goto running
     79 			}
     80 			time.Sleep(10 * time.Millisecond)
     81 		}
     82 	}
     83 running:
     84 	if err := syscall.Kill(os.Getpid(), syscall.SIGTERM); err != nil {
     85 		t.Fatalf("send SIGTERM: %v", err)
     86 	}
     87 	select {
     88 	case <-done:
     89 	case <-time.After(3 * time.Second):
     90 		t.Fatalf("main did not shut down in time; logs: %q", buf.String())
     91 	}
     92 }