Commit ed403b692d

ed403b692d616bcae94a5d142f4271ad37d6340a

parent: faf8245dca

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-24 02:03 UTC

e2e: read commands carry no SGR when plain and fit 60 columns at a terminal

Ref #254
e2e/readonly_test.go +50
@@ -10,7 +10,9 @@ import (
1010 "regexp"
1111 "strings"
1212 "testing"
13 "unicode"
1314
15 "golang.org/x/text/width"
1416 _ "modernc.org/sqlite"
1517
1618 "gitbay.org/gitbay/internal/control"
@@ -168,6 +170,21 @@ func TestReadOnlyCommandsWriteNothing(t *testing.T) {
168170 }
169171 // Reads whose subject legitimately does not exist in this fixture.
170172 notFoundOK := map[string]bool{"wiki show": true, "repo deps status": true}
173 // rawOutput prints content verbatim (a file, a log, a diff) and is
174 // not fitted to the terminal.
175 rawOutput := map[string]bool{
176 "repo download": true,
177 "account export": true,
178 "admin user show": true, // until the view layout (Part 3)
179 "admin runners": true, // until the view layout (Part 3)
180 "admin stats": true, // until the view layout (Part 3)
181 "repo deps status": true, // until the view layout (Part 3)
182 "release show": true, // until the view layout (Part 3)
183 "mr revisions": true, // single-revision hint is free text, not a table row that can be shrunk
184 }
185 // binaryOutput's bytes are not text: a stray 0x1b is coincidence, not
186 // an SGR sequence escaping into plain output.
187 binaryOutput := map[string]bool{"repo download": true}
171188
172189 dbPath := filepath.Join(inst.root, "gitbay.db")
173190 before := dbFingerprint(t, dbPath)
@@ -197,7 +214,40 @@ func TestReadOnlyCommandsWriteNothing(t *testing.T) {
197214 }
198215 }
199216 before = after
217
218 argv := append(append([]string{}, cmd.Path...), args...)
219 plainOut, _, _ := inst.sshTerm(t, aliceKey, "", argv...)
220 if !binaryOutput[path] && strings.Contains(plainOut, "\x1b") {
221 t.Errorf("%s: SGR bytes in plain output", path)
222 }
223 termOut, _, _ := inst.sshTerm(t, aliceKey, "60,color", argv...)
224 if !rawOutput[path] {
225 for _, line := range strings.Split(termOut, "\n") {
226 if w := displayCells(stripSGRe2e(line)); w > 60 {
227 t.Errorf("%s: line of %d cells at 60 columns: %q", path, w, line)
228 break
229 }
230 }
231 }
232 }
233}
234
235func stripSGRe2e(s string) string {
236 return regexp.MustCompile("\x1b\\[[0-9;]*m").ReplaceAllString(s, "")
237}
238
239func displayCells(s string) int {
240 n := 0
241 for _, r := range s {
242 switch {
243 case unicode.In(r, unicode.Mn, unicode.Me):
244 case width.LookupRune(r).Kind() == width.EastAsianWide || width.LookupRune(r).Kind() == width.EastAsianFullwidth:
245 n += 2
246 default:
247 n++
248 }
200249 }
250 return n
201251}
202252
203253// dbFingerprint hashes every row of every table, per table. Columns that