detached 3865a9d

fix: correct docstring reference, wrap init errors, add control char escape tests

commit
3865a9d1710ea443e8be9f76261b461ae5e519e3
author
brice · · 2026-04-03 17:52 UTC
parent
6b418bd
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +22 -3
M internal/adapters/in/http/middleware/accesslog.go +1 -1
@@ -15,7 +15,7 @@ import (
1515 // to the provided writer. It runs alongside (not instead of) RequestLogger.
1616 //
1717 // When excludeHealthChecks is true, requests from the Gordon health prober
18-// (UA prefix domain.HealthCheckUserAgentPrefix) or from loopback IPs are not logged.
18+// (UA prefix out.HealthCheckUserAgentPrefix) or from loopback IPs are not logged.
1919 //
2020 // Write failures are reported as warnings through the application logger and
2121 // never fail the HTTP response.
@@ -15,7 +15,7 @@ import (
15 // to the provided writer. It runs alongside (not instead of) RequestLogger.15 // to the provided writer. It runs alongside (not instead of) RequestLogger.
16 //16 //
17 // When excludeHealthChecks is true, requests from the Gordon health prober17 // When excludeHealthChecks is true, requests from the Gordon health prober
18-// (UA prefix domain.HealthCheckUserAgentPrefix) or from loopback IPs are not logged.18+// (UA prefix out.HealthCheckUserAgentPrefix) or from loopback IPs are not logged.
19 //19 //
20 // Write failures are reported as warnings through the application logger and20 // Write failures are reported as warnings through the application logger and
21 // never fail the HTTP response.21 // never fail the HTTP response.
M internal/adapters/out/accesslog/formatter_test.go +19 -0
@@ -193,3 +193,22 @@ func TestEscapeQuoted(t *testing.T) {
193193 assert.Equal(t, `back\\slash`, escapeQuoted(`back\slash`))
194194 assert.Equal(t, `\"\\\"`, escapeQuoted(`"\"`))
195195 }
196+
197+func TestEscapeQuoted_ControlCharacters(t *testing.T) {
198+ // Named control characters with dedicated escapes
199+ assert.Equal(t, `line\nbreak`, escapeQuoted("line\nbreak"))
200+ assert.Equal(t, `cr\rreturn`, escapeQuoted("cr\rreturn"))
201+ assert.Equal(t, `a\ttab`, escapeQuoted("a\ttab"))
202+
203+ // NUL, BEL, ESC — hex-escaped
204+ assert.Equal(t, `\x00`, escapeQuoted("\x00"))
205+ assert.Equal(t, `\x07`, escapeQuoted("\x07"))
206+ assert.Equal(t, `\x1b`, escapeQuoted("\x1b"))
207+
208+ // All control characters 0x00-0x1F should be escaped (none pass through raw)
209+ for b := byte(0); b < 0x20; b++ {
210+ result := escapeQuoted(string([]byte{b}))
211+ assert.NotEqual(t, string([]byte{b}), result,
212+ "control byte 0x%02x should be escaped", b)
213+ }
214+}
@@ -193,3 +193,22 @@ func TestEscapeQuoted(t *testing.T) {
193 assert.Equal(t, `back\\slash`, escapeQuoted(`back\slash`))193 assert.Equal(t, `back\\slash`, escapeQuoted(`back\slash`))
194 assert.Equal(t, `\"\\\"`, escapeQuoted(`"\"`))194 assert.Equal(t, `\"\\\"`, escapeQuoted(`"\"`))
195 }195 }
196+
197+func TestEscapeQuoted_ControlCharacters(t *testing.T) {
198+ // Named control characters with dedicated escapes
199+ assert.Equal(t, `line\nbreak`, escapeQuoted("line\nbreak"))
200+ assert.Equal(t, `cr\rreturn`, escapeQuoted("cr\rreturn"))
201+ assert.Equal(t, `a\ttab`, escapeQuoted("a\ttab"))
202+
203+ // NUL, BEL, ESC — hex-escaped
204+ assert.Equal(t, `\x00`, escapeQuoted("\x00"))
205+ assert.Equal(t, `\x07`, escapeQuoted("\x07"))
206+ assert.Equal(t, `\x1b`, escapeQuoted("\x1b"))
207+
208+ // All control characters 0x00-0x1F should be escaped (none pass through raw)
209+ for b := byte(0); b < 0x20; b++ {
210+ result := escapeQuoted(string([]byte{b}))
211+ assert.NotEqual(t, string([]byte{b}), result,
212+ "control byte 0x%02x should be escaped", b)
213+ }
214+}
M internal/adapters/out/accesslog/writer.go +2 -2
@@ -52,12 +52,12 @@ type Writer struct {
5252 func New(cfg Config) (*Writer, error) {
5353 formatter, err := newFormatter(cfg.Format)
5454 if err != nil {
55- return nil, err
55+ return nil, fmt.Errorf("failed to create formatter: %w", err)
5656 }
5757
5858 s, err := newSink(cfg)
5959 if err != nil {
60- return nil, err
60+ return nil, fmt.Errorf("failed to create sink: %w", err)
6161 }
6262
6363 return &Writer{
@@ -52,12 +52,12 @@ type Writer struct {
52 func New(cfg Config) (*Writer, error) {52 func New(cfg Config) (*Writer, error) {
53 formatter, err := newFormatter(cfg.Format)53 formatter, err := newFormatter(cfg.Format)
54 if err != nil {54 if err != nil {
55- return nil, err55+ return nil, fmt.Errorf("failed to create formatter: %w", err)
56 }56 }
57 57
58 s, err := newSink(cfg)58 s, err := newSink(cfg)
59 if err != nil {59 if err != nil {
60- return nil, err60+ return nil, fmt.Errorf("failed to create sink: %w", err)
61 }61 }
62 62
63 return &Writer{63 return &Writer{

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close