package main import ( "os" "strings" "testing" ) // The --device flag accepts the 1-based number that `keyboard info` prints. // Its help text is the only place a user learns that, so it must not // describe the HID path the flag rejects. func TestDeviceFlagUsageDescribesANumberNotAPath(t *testing.T) { flag := newRootCommand().PersistentFlags().Lookup("device") if flag == nil { t.Fatal("--device flag not found") } if strings.Contains(flag.Usage, "path") { t.Errorf("--device usage = %q, must not mention a path; the flag rejects HID paths", flag.Usage) } if !strings.Contains(flag.Usage, "number") { t.Errorf("--device usage = %q, want it to say the argument is a number", flag.Usage) } } // Cobra reads a back-quoted word in a flag's usage as the value placeholder // and renders it where the type name would go, turning `--device into // `--device keyboard info`. The help must therefore contain no backticks. func TestFlagUsageHasNoValuePlaceholder(t *testing.T) { for _, name := range []string{"device", "zone"} { flag := newRootCommand().PersistentFlags().Lookup(name) if flag == nil { t.Fatalf("--%s flag not found", name) } if strings.Contains(flag.Usage, "`") { t.Errorf("--%s usage = %q, backticks make cobra render a bogus value placeholder", name, flag.Usage) } } } // The flag usage is not the only place a user learns what --device takes. The // same promise is repeated in three documentation files, and it was wrong in // all of them at once. Pin the docs to the flag's actual contract by looking // for a value that looks like a HID path, not for the word "path" — prose // about why paths are unstable, and the rule that forbids them, must pass. func TestDocsDoNotOfferAPathValueForDevice(t *testing.T) { pathLike := []string{"/dev/", "hidraw", `\\?\hid`, "IO/HIDDevice"} for _, path := range []string{ "../../README.md", "../../AGENTS.md", "../../.claude/skills/qmk-rgb/SKILL.md", } { data, err := os.ReadFile(path) if err != nil { t.Errorf("read %s: %v", path, err) continue } for _, line := range strings.Split(string(data), "\n") { if !strings.Contains(line, "--device") { continue } for _, needle := range pathLike { if strings.Contains(line, needle) { t.Errorf("%s offers a path value for --device: %q", path, strings.TrimSpace(line)) } } } } } // The docs must not teach a zone name the resolver rejects. The subsystem // names are the vocabulary; a board's own channel names come from its definition file. func TestDocsDoNotTeachAZoneNameTheResolverRejects(t *testing.T) { for _, path := range []string{ "../../README.md", "../../AGENTS.md", "../../.claude/skills/qmk-rgb/SKILL.md", } { data, err := os.ReadFile(path) if err != nil { t.Errorf("read %s: %v", path, err) continue } body := string(data) for _, wrong := range []string{"--zone logo|backlight", "--zone matrix", "zone=matrix"} { if strings.Contains(body, wrong) { t.Errorf("%s contains %q, want only the canonical zone names", path, wrong) } } } } func TestZoneFlagUsageNamesTheChannelVocabulary(t *testing.T) { flag := newRootCommand().PersistentFlags().Lookup("zone") if flag == nil { t.Fatal("--zone flag not found") } // Every subsystem name must be discoverable from the help text. for _, zone := range []string{"backlight", "rgblight", "rgb_matrix", "audio", "led_matrix"} { if !strings.Contains(flag.Usage, zone) { t.Errorf("--zone usage = %q, want it to list %q", flag.Usage, zone) } } // The board-supplied names are not a fixed vocabulary, so the help must not // claim they are. if strings.Contains(flag.Usage, "logo") || strings.Contains(flag.Usage, "side") { t.Errorf("--zone usage = %q, must not name channels a board supplies", flag.Usage) } }