~bigbes/sr-ht-spec

ref: d4b6373d153470b30c9bb6882f71d9bee0a8f416 sr-ht-spec/cmd/specsrht/doc_test.go -rw-r--r-- 6.4 KiB
d4b6373d — Eugene Blikh graph: seed the test keyset from ecoretest 9 days ago
                                                                                
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
package main

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"sourcecraft.dev/bigbes/sr-ht-spec/core"
	"sourcecraft.dev/bigbes/sr-ht-spec/service"
)

// TestParseDocProposeTakesFlagsAfterThePositionals is the reason
// parseFlagsAnywhere exists: the natural way to type this command puts the
// space and the file first, and Go's flag package would stop there and drop
// every flag that follows.
func TestParseDocProposeTakesFlagsAfterThePositionals(t *testing.T) {
	o, err := parseDocPropose([]string{
		"~bigbes/rfcs", "rfc-0001.md",
		"--title", "RFC-0001",
		"--rationale", "because",
		"--as", "rfcs/rfc-0001.md",
	})
	if err != nil {
		t.Fatalf("parseDocPropose: %v", err)
	}
	if o.space.Owner != "bigbes" || o.space.Name != "rfcs" {
		t.Errorf("space = %+v want ~bigbes/rfcs", o.space)
	}
	if len(o.files) != 1 || o.files[0] != "rfc-0001.md" {
		t.Errorf("files = %v want [rfc-0001.md]", o.files)
	}
	if o.title != "RFC-0001" || o.rationale != "because" || o.as != "rfcs/rfc-0001.md" {
		t.Errorf("flags after the positionals were dropped: %+v", o)
	}
}

func TestParseDocProposeTakesFlagsBetweenPositionals(t *testing.T) {
	o, err := parseDocPropose([]string{
		"--proposal", "7", "~bigbes/rfcs", "--message", "add two", "a.md", "b.md",
	})
	if err != nil {
		t.Fatalf("parseDocPropose: %v", err)
	}
	if o.proposalID != 7 || o.message != "add two" {
		t.Errorf("flags lost: %+v", o)
	}
	if len(o.files) != 2 || o.files[0] != "a.md" || o.files[1] != "b.md" {
		t.Errorf("files = %v want [a.md b.md] in order", o.files)
	}
}

func TestParseDocProposeMintsASessionWhenNoneIsGiven(t *testing.T) {
	first, err := parseDocPropose([]string{"~bigbes/rfcs", "a.md"})
	if err != nil {
		t.Fatalf("parseDocPropose: %v", err)
	}
	second, err := parseDocPropose([]string{"~bigbes/rfcs", "a.md"})
	if err != nil {
		t.Fatalf("parseDocPropose: %v", err)
	}
	if first.session == "" || second.session == "" {
		t.Fatal("no session id was minted; the write would be refused for missing provenance")
	}
	if first.session == second.session {
		t.Errorf("two invocations reported the same session %q", first.session)
	}
	if !strings.HasPrefix(first.session, "cli-") {
		t.Errorf("session %q is not marked as coming from this command", first.session)
	}
	if first.agent != "specsrht-cli" {
		t.Errorf("agent = %q want the default specsrht-cli", first.agent)
	}
}

func TestParseDocProposeRejectsBadInvocations(t *testing.T) {
	tests := map[string][]string{
		"no arguments at all":     {},
		"a space but no file":     {"~bigbes/rfcs"},
		"a space with no name":    {"~bigbes", "a.md"},
		"--as with two files":     {"~bigbes/rfcs", "a.md", "b.md", "--as", "one.md"},
		"a negative proposal id":  {"~bigbes/rfcs", "a.md", "--proposal", "-1"},
		"a flag that is not ours": {"~bigbes/rfcs", "a.md", "--titel", "typo"},
	}
	for name, args := range tests {
		if _, err := parseDocPropose(args); err == nil {
			t.Errorf("parseDocPropose accepted %s: %v", name, args)
		}
	}
}

func TestLoadWritesDefaultsThePathToTheBaseName(t *testing.T) {
	dir := t.TempDir()
	local := filepath.Join(dir, "rfc-0001-spec-sr-ht.md")
	if err := os.WriteFile(local, []byte("# hello\n"), 0o644); err != nil {
		t.Fatal(err)
	}

	writes, err := loadWrites(docProposeOpts{files: []string{local}})
	if err != nil {
		t.Fatalf("loadWrites: %v", err)
	}
	if len(writes) != 1 {
		t.Fatalf("got %d writes want 1", len(writes))
	}
	if writes[0].Path != "rfc-0001-spec-sr-ht.md" {
		t.Errorf("path = %q want the base name, not the local directory", writes[0].Path)
	}
	if string(writes[0].Content) != "# hello\n" {
		t.Errorf("content = %q", writes[0].Content)
	}
}

// TestLoadWritesRefusesAPathThisCommandInvented guards the one path this layer
// makes up rather than receives: a base name that is not a document path must
// be refused by name, before a proposal row exists to leave behind.
func TestLoadWritesRefusesAPathThisCommandInvented(t *testing.T) {
	dir := t.TempDir()
	local := filepath.Join(dir, "notes.txt")
	if err := os.WriteFile(local, []byte("x"), 0o644); err != nil {
		t.Fatal(err)
	}
	if _, err := loadWrites(docProposeOpts{files: []string{local}}); err == nil {
		t.Error("loadWrites accepted a non-.md base name")
	}
	if _, err := loadWrites(docProposeOpts{files: []string{local}, as: "../escape.md"}); err == nil {
		t.Error("loadWrites accepted a path that escapes the space")
	}
}

func TestLoadWritesNamesTheFileItCouldNotRead(t *testing.T) {
	missing := filepath.Join(t.TempDir(), "gone.md")
	_, err := loadWrites(docProposeOpts{files: []string{missing}})
	if err == nil {
		t.Fatal("loadWrites accepted a missing file")
	}
	if !strings.Contains(err.Error(), "gone.md") {
		t.Errorf("the error does not name the file:\n%v", err)
	}
}

func TestPrintProposeResultLeadsToTheURL(t *testing.T) {
	var buf bytes.Buffer
	res := service.ProposeResult{
		Proposal: service.Proposal{
			ID:      7,
			State:   core.StateOpen,
			Branch:  "proposals/7",
			BaseRev: "0123456789abcdef0123456789abcdef01234567",
		},
		URL: "https://spec.srht.bigb.es/~bigbes/rfcs/p/7",
	}
	writes := []service.DocumentWrite{{Path: "rfc-0001.md", Content: []byte("abc")}}
	if err := printProposeResult(&buf, res, writes); err != nil {
		t.Fatalf("printProposeResult: %v", err)
	}
	out := buf.String()
	for _, want := range []string{"proposal 7", "rfc-0001.md (3 bytes)", "proposals/7", res.URL} {
		if !strings.Contains(out, want) {
			t.Errorf("output does not mention %q:\n%s", want, out)
		}
	}
	if !strings.HasSuffix(strings.TrimRight(out, "\n"), res.URL) {
		t.Errorf("the URL is not the last line an operator sees:\n%s", out)
	}
}

func TestPrintProposeResultSaysWhenPolicyLandedIt(t *testing.T) {
	var buf bytes.Buffer
	res := service.ProposeResult{
		Proposal: service.Proposal{ID: 8, State: core.StateMerged, Branch: "proposals/8"},
		URL:      "https://spec.srht.bigb.es/~bigbes/rfcs/p/8",
		Merged:   true,
	}
	if err := printProposeResult(&buf, res, nil); err != nil {
		t.Fatalf("printProposeResult: %v", err)
	}
	if !strings.Contains(buf.String(), "auto-merged") {
		t.Errorf("a policy-merged write reads as if it is waiting for review:\n%s", buf.String())
	}
}

func TestRunDocRejectsAnUnknownSubcommandBeforeTheDatabase(t *testing.T) {
	if err := runDoc(nil); err == nil || err.Error() != docUsage {
		t.Errorf("empty invocation did not print the usage line: %v", err)
	}
	err := runDoc([]string{"upload", "~bigbes/rfcs", "a.md"})
	if err == nil || !strings.Contains(err.Error(), "propose") {
		t.Errorf("unknown subcommand error does not name the real one: %v", err)
	}
}