Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions internal/tools/list_devices_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

package tools

import (
"context"
"strings"
"testing"

"github.com/xunholy/promptzero/internal/config"
)

// TestListDevices_OutputStableAndOmitsEmptyType verifies the list_devices
// view is deterministic (sorted by name, not random map order) and omits
// the "type:" field for shorthand devices that set only a file — the
// bare-string form added in v0.749.
func TestListDevices_OutputStableAndOmitsEmptyType(t *testing.T) {
spec, ok := Get("list_devices")
if !ok {
t.Fatal("list_devices not registered")
}
cfg := &config.Config{Devices: map[string]config.Device{
// Shorthand: only File set.
"garage": {File: "/ext/subghz/garage.sub"},
// Full mapping with multiple commands.
"tv": {Type: "infrared", File: "/ext/infrared/tv.ir", Commands: map[string]string{
"volume_up": "/ext/infrared/tv_volup.ir",
"power": "/ext/infrared/tv_power.ir",
}},
}}

out, err := spec.Handler(context.Background(), &Deps{Config: cfg}, nil)
if err != nil {
t.Fatalf("handler: %v", err)
}

want := "- garage (file: /ext/subghz/garage.sub)\n" +
"- tv (type: infrared, file: /ext/infrared/tv.ir)\n" +
" command: power -> /ext/infrared/tv_power.ir\n" +
" command: volume_up -> /ext/infrared/tv_volup.ir\n"
if out != want {
t.Errorf("list_devices output mismatch:\n got:\n%s\nwant:\n%s", out, want)
}

// Shorthand device must NOT print an empty "type:".
if strings.Contains(out, "type: ,") || strings.Contains(out, "(type: )") {
t.Errorf("shorthand device printed an empty type:\n%s", out)
}
}
30 changes: 26 additions & 4 deletions internal/tools/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"sort"
"time"

"github.com/xunholy/promptzero/internal/risk"
Expand Down Expand Up @@ -103,11 +104,32 @@ func init() {
if d.Config == nil || len(d.Config.Devices) == 0 {
return "No devices configured. Add devices to config.yaml.", nil
}
// Sort by name so the listing is stable across calls — map
// iteration order is random, which makes the output churn and
// is unpleasant to read or test.
names := make([]string, 0, len(d.Config.Devices))
for name := range d.Config.Devices {
names = append(names, name)
}
sort.Strings(names)

var out string
for name, dev := range d.Config.Devices {
out += fmt.Sprintf("- %s (type: %s, file: %s)\n", name, dev.Type, dev.File)
for cmd, signal := range dev.Commands {
out += fmt.Sprintf(" command: %s -> %s\n", cmd, signal)
for _, name := range names {
dev := d.Config.Devices[name]
// Type is optional — the bare-string shorthand sets only
// File — so omit it rather than printing an empty "type: ".
if dev.Type != "" {
out += fmt.Sprintf("- %s (type: %s, file: %s)\n", name, dev.Type, dev.File)
} else {
out += fmt.Sprintf("- %s (file: %s)\n", name, dev.File)
}
cmds := make([]string, 0, len(dev.Commands))
for cmd := range dev.Commands {
cmds = append(cmds, cmd)
}
sort.Strings(cmds)
for _, cmd := range cmds {
out += fmt.Sprintf(" command: %s -> %s\n", cmd, dev.Commands[cmd])
}
}
return out, nil
Expand Down
Loading