v0.4.1 122e784

guestforge_test.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package sandbox
4
5import (
6	"context"
7	"errors"
8	"strings"
9	"testing"
10)
11
12func TestParseGuestForge(t *testing.T) {
13	ok := map[string]struct {
14		insecure     bool
15		host, origin string
16		port         uint16
17	}{
18		"https://ermite.example.org":      {false, "ermite.example.org", "https://ermite.example.org", 443},
19		"https://Ermite.Example.org:8443": {false, "ermite.example.org", "https://ermite.example.org:8443", 8443},
20		"https://ermite.example.org/":     {false, "ermite.example.org", "https://ermite.example.org", 443},
21		"https://ermite.example.org.":     {false, "ermite.example.org", "https://ermite.example.org", 443},
22		"http://ermite.bnema.dev:8088":    {true, "ermite.bnema.dev", "http://ermite.bnema.dev:8088", 8088},
23		"http://forge":                    {true, "forge", "http://forge", 80},
24		"https://a-b.c1.test:65535":       {false, "a-b.c1.test", "https://a-b.c1.test:65535", 65535},
25	}
26	for raw, w := range ok {
27		g, err := ParseGuestForge(raw, w.insecure)
28		if err != nil {
29			t.Errorf("%s: %v", raw, err)
30			continue
31		}
32		if g.Host != w.host || g.Port != w.port || g.Origin != w.origin {
33			t.Errorf("%s: %+v", raw, g)
34		}
35	}
36	bad := map[string]string{
37		"http://ermite.bnema.dev:8088":                 "INSECURE_HTTP", // plain http needs the opt-in
38		"https://10.0.0.1":                             "IP address",
39		"https://10.0.0.1:8443":                        "IP address",
40		"https://[::1]":                                "IP address",
41		"https://[2001:db8::1]:8443":                   "IP address",
42		"https://2130706433":                           "IP address",
43		"https://127.1":                                "IP address",
44		"https://*.example.org":                        "valid host name",
45		"https://exa_mple.org":                         "valid host name",
46		"https://-a.example.org":                       "valid host name",
47		"https://a..example.org":                       "valid host name",
48		"https://bücher.example":                       "valid host name",
49		"https://" + strings.Repeat("a", 64) + ".test": "valid host name",
50		"https://ermite.example.org:0":                 "1-65535",
51		"https://ermite.example.org:65536":             "",
52		"https://ermite.example.org:99999":             "",
53		"https://ermite.example.org/sub":               "bare origin",
54		"https://ermite.example.org/?q=1":              "bare origin",
55		"https://u:p@ermite.example.org":               "bare origin",
56		"ftp://ermite.example.org":                     "http or https",
57		"ermite.example.org":                           "valid URL",
58		"":                                             "valid URL",
59	}
60	for raw, frag := range bad {
61		if _, err := ParseGuestForge(raw, false); err == nil || !strings.Contains(err.Error(), frag) || !strings.Contains(err.Error(), EnvGuestForge) {
62			t.Errorf("%q: %v (want an error with %q)", raw, err, frag)
63		}
64	}
65	// With the opt-in for plain http, the other refusals still apply.
66	for _, raw := range []string{"http://10.0.0.1:8088", "http://localhost:99999", "http://[::1]:8088"} {
67		if _, err := ParseGuestForge(raw, true); err == nil {
68			t.Errorf("%q accepted", raw)
69		}
70	}
71}
72
73func TestResolveGuestForge(t *testing.T) {
74	ctx := context.Background()
75	var asked string
76	lookup := func(_ context.Context, h string) ([]string, error) { asked = h; return []string{"127.0.0.1"}, nil }
77	g, err := ResolveGuestForge(ctx, "http://ermite.bnema.dev:8088", true, lookup)
78	if err != nil || g.Port != 8088 || asked != "ermite.bnema.dev" {
79		t.Fatalf("%+v %v %q", g, err, asked)
80	}
81	_, err = ResolveGuestForge(ctx, "https://nx.example.org", false, func(context.Context, string) ([]string, error) {
82		return nil, errors.New("no such host")
83	})
84	if err == nil || !strings.Contains(err.Error(), "does not resolve") {
85		t.Fatalf("%v", err)
86	}
87	_, err = ResolveGuestForge(ctx, "https://empty.example.org", false, func(context.Context, string) ([]string, error) { return nil, nil })
88	if err == nil || !strings.Contains(err.Error(), "no address") {
89		t.Fatalf("%v", err)
90	}
91	called := false
92	_, err = ResolveGuestForge(ctx, "https://10.0.0.1", false, func(context.Context, string) ([]string, error) { called = true; return nil, nil })
93	if err == nil || called {
94		t.Fatalf("an IP literal was looked up or accepted: %v", err)
95	}
96}
97
98func TestGuestForgeNilIsOff(t *testing.T) {
99	var g *GuestForge
100	if g.DNSName() != "" || g.TCPPort() != 0 {
101		t.Fatal("nil must mean off")
102	}
103	g, _ = ParseGuestForge("https://ermite.example.org:8443", false)
104	if g.Warning() != "guest forge access: sandboxes may reach https://ermite.example.org:8443 on the host" {
105		t.Fatal(g.Warning())
106	}
107}
108
109func TestLoadConfigGuestForge(t *testing.T) {
110	get := func(v string) func(string) string {
111		return func(k string) string {
112			if k == EnvGuestForge {
113				return v
114			}
115			return ""
116		}
117	}
118	for v, want := range map[string]bool{"": false, "0": false, "no": false, "1": true, " true ": true, "YES": true} {
119		c, err := LoadConfig(get(v))
120		if err != nil || c.GuestForgeEnabled != want {
121			t.Errorf("%q: %v %v", v, c.GuestForgeEnabled, err)
122		}
123	}
124	if _, err := LoadConfig(get("on")); err == nil || !strings.Contains(err.Error(), EnvGuestForge) {
125		t.Errorf("a bad value was accepted: %v", err)
126	}
127}

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 4

Tags 11

↑↓ moveenter openesc close