package server_test import ( "net/http" "net/http/httptest" "regexp" "strings" "syscall" "testing" "time" "github.com/coder/websocket" "termd/internal/api" "termd/internal/client" "termd/internal/server" "termd/internal/session" ) // Timestamp, method, path, status, latency. var logLine = regexp.MustCompile( `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}(Z|[+-]\d{2}:\d{2}) (GET|POST|DELETE) \S+ \d{3} \d+(\.\d+)?(µs|ms|s)$`) func TestAccessLog(t *testing.T) { reg := session.NewRegistry() var logs syncBuf srv := httptest.NewServer(server.AccessLog(server.New(reg, ""), &logs)) t.Cleanup(func() { for _, s := range reg.List() { _ = s.Kill(syscall.SIGKILL) } srv.Close() }) c := client.New(srv.URL, "") // A 200, a 404, and a 101 websocket upgrade through the middleware. if _, err := c.List(ctx()); err != nil { t.Fatal(err) } resp, err := http.Get(srv.URL + "/sessions/nope/screen") if err != nil { t.Fatal(err) } resp.Body.Close() info, err := c.Create(ctx(), api.CreateSessionRequest{Command: []string{"sh"}}) if err != nil { t.Fatal(err) } conn, _, err := websocket.Dial(ctx(), c.AttachURL(info.ID), nil) if err != nil { t.Fatal(err) } conn.SetReadLimit(16 << 20) _ = conn.Close(websocket.StatusNormalClosure, "done") // The attach line only appears once its handler returns. var lines []string deadline := time.Now().Add(10 * time.Second) for len(lines) < 4 && time.Now().Before(deadline) { lines = nonEmptyLines(logs.String()) time.Sleep(10 * time.Millisecond) } if len(lines) < 4 { t.Fatalf("want at least 4 log lines, got %d:\n%s", len(lines), logs.String()) } for _, line := range lines { if !logLine.MatchString(line) { t.Errorf("malformed log line: %q", line) } } all := logs.String() for _, want := range []string{ "GET /sessions 200", "GET /sessions/nope/screen 404", "POST /sessions 201", "/attach 101", } { if !strings.Contains(all, want) { t.Errorf("log missing %q:\n%s", want, all) } } } func nonEmptyLines(s string) []string { var out []string for _, l := range strings.Split(s, "\n") { if strings.TrimSpace(l) != "" { out = append(out, l) } } return out }