// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 package app import ( "fmt" "io" "log/slog" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "sourcedock.dev/petrbalvin/volumen/internal/config" "sourcedock.dev/petrbalvin/volumen/internal/store" ) // BenchmarkServer drives the wired handler over real HTTP: a list, a // single post, a tag feed and the sitemap, against a corpus the size of a // site that has been running for a few years. It is also the workload the // PGO profile is recorded from, which is why it exercises the whole chain // rather than one function. func BenchmarkServer(b *testing.B) { const posts = 500 dir := b.TempDir() content := filepath.Join(dir, "posts") if err := os.MkdirAll(content, 0o755); err != nil { b.Fatalf("mkdir: %v", err) } for i := range posts { body := fmt.Sprintf(`+++ title = "Post %d" slug = "post-%d" date = 2026-01-%02d lang = "en" tags = ["go", "bench"] series = "Bench" series_order = %d +++ ## Section A paragraph with **markup**, a [link](https://example.com) and enough words to make the renderer do real work: %s - one - two - three `, i, i, i%28+1, i, strings.Repeat("lorem ipsum dolor sit amet ", 40)) path := filepath.Join(content, fmt.Sprintf("post-%d.md", i)) if err := os.WriteFile(path, []byte(body), 0o644); err != nil { b.Fatalf("write: %v", err) } } cfg := config.Defaults() cfg.ContentDir = content cfg.UsersFile = filepath.Join(dir, "users.toml") cfg.Admin.SessionKey = strings.Repeat("k", 64) cfg.Site.BaseURL = "https://site.example" cfg.API.RateLimit = 0 srv, err := New(cfg, store.New(store.Options{ContentDir: content, DefaultLang: "en", RevisionLimit: 10})) if err != nil { b.Fatalf("New: %v", err) } // The access line each request writes is noise inside a benchmark. previous := slog.Default() slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil))) b.Cleanup(func() { slog.SetDefault(previous) }) server := httptest.NewServer(srv.Handler()) defer server.Close() targets := []string{ "/api/volumen/posts?limit=20", "/api/volumen/posts/post-250", "/api/volumen/tags", "/api/volumen/tags/go/feed.json", "/api/volumen/sitemap.xml", } client := server.Client() b.ResetTimer() for i := 0; b.Loop(); i++ { resp, err := client.Get(server.URL + targets[i%len(targets)]) if err != nil { b.Fatalf("get: %v", err) } if _, err := io.Copy(io.Discard, resp.Body); err != nil { b.Fatalf("read: %v", err) } resp.Body.Close() if resp.StatusCode != http.StatusOK { b.Fatalf("%s: status %d", targets[i%len(targets)], resp.StatusCode) } } }