carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "flag"
5 "io"
6 "strings"
7 "testing"
8)
9
10func quietFlagSet(name string) *flag.FlagSet {
11 fs := flag.NewFlagSet(name, flag.ContinueOnError)
12 fs.SetOutput(io.Discard)
13 return fs
14}
15
16// Every spelling of the app flag lands in the same variable.
17func TestAppFlagParsesAllSpellings(t *testing.T) {
18 for _, args := range [][]string{
19 {"-app", "blah"}, {"--app", "blah"}, {"-app=blah"},
20 {"--app=blah"}, {"-a", "blah"}, {"-a=blah"},
21 } {
22 fs := quietFlagSet("t")
23 app := appFlag(fs, "")
24 if err := fs.Parse(args); err != nil {
25 t.Fatalf("%v: %v", args, err)
26 }
27 if *app != "blah" {
28 t.Fatalf("%v: app=%q, want blah", args, *app)
29 }
30 }
31}
32
33// The four helpers each claim exactly their ruled letter.
34func TestClaimedLetters(t *testing.T) {
35 fs := quietFlagSet("t")
36 appFlag(fs, "")
37 channelFlag(fs, "")
38 environmentFlag(fs, "")
39 versionFlag(fs, "")
40 for primary, short := range map[string]string{
41 "app": "a", "channel": "c", "environment": "e", "version": "v",
42 } {
43 if fs.Lookup(short) == nil {
44 t.Errorf("-%s not registered for --%s", short, primary)
45 }
46 }
47}
48
49// The default-value trap: registering the alias must not reset a
50// non-empty primary default (StringVar assigns through the pointer).
51func TestAliasKeepsThePrimaryDefault(t *testing.T) {
52 fs := quietFlagSet("t")
53 ch := aliasedString(fs, "channel", "stable", "release channel to follow")
54 if err := fs.Parse(nil); err != nil {
55 t.Fatal(err)
56 }
57 if *ch != "stable" {
58 t.Fatalf("default clobbered by alias registration: %q", *ch)
59 }
60 fs2 := quietFlagSet("t")
61 ch2 := aliasedString(fs2, "channel", "stable", "")
62 if err := fs2.Parse([]string{"-c", "edge"}); err != nil {
63 t.Fatal(err)
64 }
65 if *ch2 != "edge" {
66 t.Fatalf("-c did not assign: %q", *ch2)
67 }
68}
69
70// An empty usage argument takes the flag's common wording.
71func TestHelperDefaultUsage(t *testing.T) {
72 fs := quietFlagSet("t")
73 appFlag(fs, "")
74 if got := fs.Lookup("app").Usage; got != "app name" {
75 t.Fatalf("default usage = %q", got)
76 }
77 fs2 := quietFlagSet("t")
78 appFlag(fs2, "app the instance belongs to")
79 if got := fs2.Lookup("app").Usage; got != "app the instance belongs to" {
80 t.Fatalf("bespoke usage lost: %q", got)
81 }
82}
83
84// fs.Visit reports the spelling actually typed, so asking about the
85// primary alone goes false when -a was given. flagWasSet must be true
86// for every spelling — the deploy wizard's call sites depend on it.
87func TestFlagWasSetSeesEverySpelling(t *testing.T) {
88 for _, args := range [][]string{
89 {"-app", "blah"}, {"--app", "blah"}, {"-a", "blah"},
90 } {
91 fs := quietFlagSet("t")
92 appFlag(fs, "")
93 if err := fs.Parse(args); err != nil {
94 t.Fatal(err)
95 }
96 if !flagWasSet(fs, "app") {
97 t.Errorf("%v: flagWasSet(app) = false", args)
98 }
99 }
100 fs := quietFlagSet("t")
101 appFlag(fs, "")
102 fs.String("kind", "", "artifact kind")
103 if err := fs.Parse([]string{"-a", "blah"}); err != nil {
104 t.Fatal(err)
105 }
106 if flagWasSet(fs, "kind") {
107 t.Error("kind reported set when only -a was typed")
108 }
109}
110
111func printedUsage(fs *flag.FlagSet, header string) string {
112 var b strings.Builder
113 fs.SetOutput(&b)
114 flagUsage(fs, header)()
115 return b.String()
116}
117
118// One row per flag: the alias folds into its primary, long names render
119// with --, claimed single-letter primaries (-f, -n, -y) keep one dash,
120// and the alias never gets a row of its own.
121func TestFlagUsageFoldsAliases(t *testing.T) {
122 fs := quietFlagSet("t")
123 appFlag(fs, "")
124 fs.Bool("f", false, "follow: poll for new lines until interrupted")
125 fs.String("console", "", "console to work through")
126 out := printedUsage(fs, "usage: carlos logs [flags] <app>")
127 for _, want := range []string{
128 "usage: carlos logs [flags] <app>",
129 "--app, -a <string>",
130 "app name",
131 "--console <string>",
132 "\n -f ",
133 } {
134 if !strings.Contains(out, want) {
135 t.Errorf("missing %q in:\n%s", want, out)
136 }
137 }
138 if strings.Contains(out, "alias for") {
139 t.Errorf("alias got its own row:\n%s", out)
140 }
141 if strings.Contains(out, "--f") {
142 t.Errorf("single-letter primary rendered double-dash:\n%s", out)
143 }
144}
145
146// Non-zero defaults are shown; zero-value defaults ("" / false / 0) are
147// noise and are not.
148func TestFlagUsageShowsRealDefaultsOnly(t *testing.T) {
149 fs := quietFlagSet("t")
150 aliasedString(fs, "channel", "stable", "release channel to follow")
151 fs.Int("n", 0, "at most this many lines")
152 var fields multiFlag
153 fs.Var(&fields, "field", "only structured lines with this k=v field (repeatable)")
154 out := printedUsage(fs, "")
155 if !strings.Contains(out, "(default: stable)") {
156 t.Errorf("stable default not shown:\n%s", out)
157 }
158 if strings.Contains(out, "default: 0") {
159 t.Errorf("zero default shown:\n%s", out)
160 }
161 if strings.Contains(out, "default: []") {
162 t.Errorf("empty repeatable default shown:\n%s", out)
163 }
164}
165
166// A command that registers its own single-letter flag colliding with a
167// claimed letter — without the registrar — must get honest help: no fold
168// (the pair shares no value), and both rows printed. The fold is
169// recognised by the alias's marker usage, not by name alone.
170func TestFlagUsageDoesNotFoldAnImpostorLetter(t *testing.T) {
171 fs := quietFlagSet("t")
172 fs.String("version", "", "version to act on")
173 fs.Bool("v", false, "chatty output")
174 out := printedUsage(fs, "")
175 if !strings.Contains(out, "--version <string>") {
176 t.Errorf("--version row missing or wrongly folded:\n%s", out)
177 }
178 if strings.Contains(out, "--version, -v") {
179 t.Errorf("folded -v into --version though they share no value:\n%s", out)
180 }
181 if !strings.Contains(out, "\n -v ") {
182 t.Errorf("-v row hidden:\n%s", out)
183 }
184}
185