Files
petrbalvin f8ed33df83
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Initial commit
Assisted-by: GLM 5.3
2026-09-29 10:03:32 +02:00

99 lines
2.7 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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)
}
}
}