1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package main

import (
	"bytes"
	"os"
	"reflect"
	"strings"
	"testing"
)

// captureStderr redirects the package's stderr (see page.go) for the duration
// of fn and returns what was written to it, so the warn-and-ignore behaviour
// can be asserted on rather than merely tolerated.
func captureStderr(t *testing.T, fn func()) string {
	t.Helper()
	r, w, err := os.Pipe()
	if err != nil {
		t.Fatalf("pipe: %v", err)
	}
	saved := stderr
	stderr = w
	defer func() { stderr = saved }()

	done := make(chan string)
	go func() {
		var buf bytes.Buffer
		buf.ReadFrom(r)
		done <- buf.String()
	}()

	fn()
	w.Close()
	return <-done
}

// Parsing: one "key = value" per line, "#" comments, repeated keys for lists.
func TestParseDirMeta(t *testing.T) {
	for _, tc := range []struct {
		name      string
		dir       string
		in        string
		shortcuts []string
		desc      string
		warn      string // substring expected on stderr ("" = no warning)
	}{
		{
			name:      "shortcuts accumulate in file order",
			in:        "shortcut = users/Profpatsch\nshortcut = users/Profpatsch/git-blimey\n",
			shortcuts: []string{"users/Profpatsch", "users/Profpatsch/git-blimey"},
		},
		{
			name:      "comments and blank lines are ignored",
			in:        "# a comment\n\n   # indented comment\nshortcut = users\n\n",
			shortcuts: []string{"users"},
		},
		{
			name: "whitespace around key and value is trimmed",
			in:   "   shortcut   =   users/Profpatsch   \n",

			shortcuts: []string{"users/Profpatsch"},
		},
		{
			name: "value may contain further equals signs",
			in:   "description = a = b = c\n",
			desc: "a = b = c",
		},
		{
			name: "description is read at the root",
			in:   "description = A *monorepo*.\n",
			desc: "A *monorepo*.",
		},
		{
			name: "description is read in any directory",
			dir:  "users/Profpatsch/git-blimey",
			in:   "description = A git-blame TUI.\n",
			desc: "A git-blame TUI.",
		},
		{
			name: "repeated description keeps the first",
			in:   "description = first\ndescription = second\n",
			desc: "first",
			warn: "repeated 'description'",
		},
		{
			name: "unknown key warns",
			in:   "collapse = users/Profpatsch\n",
			warn: `unknown key "collapse"`,
		},
		{
			name: "line without an equals sign warns",
			in:   "shortcut users/Profpatsch\n",
			warn: "expected 'key = value'",
		},
		{
			name: "empty value warns",
			in:   "shortcut =\n",
			warn: "empty value",
		},
		{
			name:      "a bad line does not affect the good ones",
			in:        "bogus\nshortcut = users\nalso bogus\nshortcut = nix\n",
			shortcuts: []string{"users", "nix"},
			warn:      "expected 'key = value'",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			var m *dirMeta
			out := captureStderr(t, func() {
				m = parseDirMeta(tc.dir, []byte(tc.in))
			})

			if !reflect.DeepEqual(m.shortcuts, tc.shortcuts) {
				t.Errorf("shortcuts = %q, want %q", m.shortcuts, tc.shortcuts)
			}
			if m.description != tc.desc {
				t.Errorf("description = %q, want %q", m.description, tc.desc)
			}
			if tc.warn == "" {
				if out != "" {
					t.Errorf("unexpected warning: %q", out)
				}
			} else if !strings.Contains(out, tc.warn) {
				t.Errorf("stderr = %q, want it to contain %q", out, tc.warn)
			}
		})
	}
}

// Shortcut target validation: must exist, must be a strict descendant, and
// must not be listed twice. Everything else warns and is skipped.
func TestValidShortcuts(t *testing.T) {
	tree := map[string]bool{
		"users":                  true,
		"users/Profpatsch":       true,
		"users/Profpatsch/blog":  true,
		"users/packages.nix":     true,
		"nix":                    true,
		"users/Profpatsch/e.nix": true,
	}
	exists := func(p string) bool { return tree[p] }

	for _, tc := range []struct {
		name string
		dir  string
		in   []string
		want []string
		warn string
	}{
		{
			name: "descendants resolve to full tree paths",
			in:   []string{"users/Profpatsch", "nix"},
			want: []string{"users/Profpatsch", "nix"},
		},
		{
			name: "targets are relative to the declaring directory",
			dir:  "users",
			in:   []string{"Profpatsch/blog"},
			want: []string{"users/Profpatsch/blog"},
		},
		{
			name: "a file is a valid target",
			in:   []string{"users/packages.nix"},
			want: []string{"users/packages.nix"},
		},
		{
			name: "missing target is skipped",
			in:   []string{"users/nope"},
			warn: "no such file or directory",
		},
		{
			name: "'.' is rejected",
			dir:  "users",
			in:   []string{"."},
			warn: "must not contain '.' or '..'",
		},
		{
			name: "escaping the directory is rejected",
			dir:  "users",
			in:   []string{"../nix"},
			warn: "must not contain '.' or '..'",
		},
		{
			name: "an absolute path is rejected",
			in:   []string{"/users/Profpatsch"},
			warn: "not absolute",
		},
		{
			name: "a duplicate is listed once",
			in:   []string{"users/Profpatsch", "users/Profpatsch"},
			want: []string{"users/Profpatsch"},
			warn: "already listed",
		},
		{
			name: "a bad target does not affect the good ones",
			in:   []string{"users/nope", "nix"},
			want: []string{"nix"},
			warn: "no such file or directory",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			m := &dirMeta{dir: tc.dir, shortcuts: tc.in}
			var got []string
			out := captureStderr(t, func() { got = m.validShortcuts(exists) })

			if !reflect.DeepEqual(got, tc.want) {
				t.Errorf("validShortcuts() = %q, want %q", got, tc.want)
			}
			if tc.warn == "" {
				if out != "" {
					t.Errorf("unexpected warning: %q", out)
				}
			} else if !strings.Contains(out, tc.warn) {
				t.Errorf("stderr = %q, want it to contain %q", out, tc.warn)
			}
		})
	}
}