platform/app/webui: ship the power-watch scenario baked into the image
platform.LocalScenariosDir (/usr/share/bee/scenarios, populated from iso/overlay/usr/share/bee/scenarios/ by build.sh's overlay rsync) is now checked before removable media for both `bee run <name>` and the "6. Scenario" page — a scenario shipped with the image works with no USB stick required. ReadScenario/ListAvailableScenarios merge local + USB; the removable-media-only functions from the previous commit are kept as-is (still used directly where that's actually what's wanted) rather than renamed out from under existing callers/tests. iso/overlay/usr/share/bee/scenarios/nvbandwidth-all-gpu-power-watch.json is a copy of scenarios/nvbandwidth-all-gpu-power-watch.json — the two aren't auto-synced (documented in scenarios/README.md), so shipping a scenario baked-in means checking it into both places. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bcf02e0515
commit
5a780e96b4
@@ -157,3 +157,115 @@ func TestReadScenarioFromRemovableMediaNotFound(t *testing.T) {
|
||||
t.Fatal("expected error for missing scenario file")
|
||||
}
|
||||
}
|
||||
|
||||
func withLocalScenariosDir(t *testing.T) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
old := LocalScenariosDir
|
||||
LocalScenariosDir = dir
|
||||
t.Cleanup(func() { LocalScenariosDir = old })
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestReadScenarioPrefersLocalOverRemovableMedia(t *testing.T) {
|
||||
localDir := withLocalScenariosDir(t)
|
||||
localData := []byte(`{"name":"local-copy"}`)
|
||||
if err := os.WriteFile(filepath.Join(localDir, "power-watch.json"), localData, 0644); err != nil {
|
||||
t.Fatalf("write local scenario: %v", err)
|
||||
}
|
||||
|
||||
// A removable target also has a same-named file with different content
|
||||
// — ReadScenario must prefer the local (always-available) copy.
|
||||
mountpoint := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(mountpoint, "scenarios"), 0755); err != nil {
|
||||
t.Fatalf("mkdir scenarios: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(mountpoint, "scenarios", "power-watch.json"), []byte(`{"name":"usb-copy"}`), 0644); err != nil {
|
||||
t.Fatalf("write usb scenario: %v", err)
|
||||
}
|
||||
oldExec := exportExecCommand
|
||||
lsblkOut := `NAME="sdb1" TYPE="part" PKNAME="sdb" RM="1" RO="0" FSTYPE="vfat" MOUNTPOINT="` + mountpoint + `" SIZE="29.8G" LABEL="USB" MODEL=""`
|
||||
exportExecCommand = func(name string, args ...string) *exec.Cmd {
|
||||
cmd := exec.Command("sh", "-c", "printf '%s\n' \"$LSBLK_OUT\"")
|
||||
cmd.Env = append(os.Environ(), "LSBLK_OUT="+lsblkOut)
|
||||
return cmd
|
||||
}
|
||||
t.Cleanup(func() { exportExecCommand = oldExec })
|
||||
|
||||
s := &System{}
|
||||
got, err := s.ReadScenario("power-watch")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadScenario error: %v", err)
|
||||
}
|
||||
if string(got) != string(localData) {
|
||||
t.Fatalf("got=%q want local copy %q", got, localData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadScenarioFallsBackToRemovableMediaWhenNotLocal(t *testing.T) {
|
||||
withLocalScenariosDir(t) // empty — nothing local
|
||||
|
||||
mountpoint := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(mountpoint, "scenarios"), 0755); err != nil {
|
||||
t.Fatalf("mkdir scenarios: %v", err)
|
||||
}
|
||||
want := []byte(`{"name":"usb-only"}`)
|
||||
if err := os.WriteFile(filepath.Join(mountpoint, "scenarios", "usb-only.json"), want, 0644); err != nil {
|
||||
t.Fatalf("write usb scenario: %v", err)
|
||||
}
|
||||
oldExec := exportExecCommand
|
||||
lsblkOut := `NAME="sdb1" TYPE="part" PKNAME="sdb" RM="1" RO="0" FSTYPE="vfat" MOUNTPOINT="` + mountpoint + `" SIZE="29.8G" LABEL="USB" MODEL=""`
|
||||
exportExecCommand = func(name string, args ...string) *exec.Cmd {
|
||||
cmd := exec.Command("sh", "-c", "printf '%s\n' \"$LSBLK_OUT\"")
|
||||
cmd.Env = append(os.Environ(), "LSBLK_OUT="+lsblkOut)
|
||||
return cmd
|
||||
}
|
||||
t.Cleanup(func() { exportExecCommand = oldExec })
|
||||
|
||||
s := &System{}
|
||||
got, err := s.ReadScenario("usb-only")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadScenario error: %v", err)
|
||||
}
|
||||
if string(got) != string(want) {
|
||||
t.Fatalf("got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListAvailableScenariosMergesLocalAndRemovable(t *testing.T) {
|
||||
localDir := withLocalScenariosDir(t)
|
||||
if err := os.WriteFile(filepath.Join(localDir, "shipped.json"), []byte(`{}`), 0644); err != nil {
|
||||
t.Fatalf("write local scenario: %v", err)
|
||||
}
|
||||
|
||||
mountpoint := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(mountpoint, "scenarios"), 0755); err != nil {
|
||||
t.Fatalf("mkdir scenarios: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(mountpoint, "scenarios", "from-usb.json"), []byte(`{}`), 0644); err != nil {
|
||||
t.Fatalf("write usb scenario: %v", err)
|
||||
}
|
||||
oldExec := exportExecCommand
|
||||
lsblkOut := `NAME="sdb1" TYPE="part" PKNAME="sdb" RM="1" RO="0" FSTYPE="vfat" MOUNTPOINT="` + mountpoint + `" SIZE="29.8G" LABEL="USB" MODEL=""`
|
||||
exportExecCommand = func(name string, args ...string) *exec.Cmd {
|
||||
cmd := exec.Command("sh", "-c", "printf '%s\n' \"$LSBLK_OUT\"")
|
||||
cmd.Env = append(os.Environ(), "LSBLK_OUT="+lsblkOut)
|
||||
return cmd
|
||||
}
|
||||
t.Cleanup(func() { exportExecCommand = oldExec })
|
||||
|
||||
s := &System{}
|
||||
got, err := s.ListAvailableScenarios()
|
||||
if err != nil {
|
||||
t.Fatalf("ListAvailableScenarios error: %v", err)
|
||||
}
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("got=%v want 2 entries", got)
|
||||
}
|
||||
if got[0].Name != "shipped" || got[0].Device != "local (shipped with image)" {
|
||||
t.Fatalf("got[0]=%+v want local shipped entry first", got[0])
|
||||
}
|
||||
if got[1].Name != "from-usb" {
|
||||
t.Fatalf("got[1]=%+v want from-usb", got[1])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user