feat(main): extract testable runCommand and add debug log for blocked WhatsApp senders

- Extract runCommand(w, args) from main() so the command tree can be
  tested without spawning a subprocess; fatal errors are printed to w
  so they surface even when stderr is redirected (e.g. docker).
- Add TestRunCommandFatalPrintsToStdout to verify the FATAL message
  reaches stdout on cobra errors.
- Log a DebugCF message when an incoming WhatsApp message is silently
  dropped because the sender is not in allow_from.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sushi30 2026-03-26 08:06:09 +01:00 committed by github-actions[bot]
parent d392ef8da9
commit 32433f85d2
3 changed files with 32 additions and 2 deletions

View file

@ -8,6 +8,7 @@ package main
import (
"fmt"
"io"
"os"
"time"
@ -67,6 +68,21 @@ const (
"\033[0m\r\n"
)
// runCommand executes picoclaw with the given args, writing output to w.
// On failure it prints a prominent error to w so the message is visible
// even when os.Stderr has been redirected (e.g. to a panic-log file).
func runCommand(w io.Writer, args []string) error {
cmd := NewPicoclawCommand()
cmd.SetOut(w)
cmd.SetErr(w)
cmd.SetArgs(args)
if err := cmd.Execute(); err != nil {
fmt.Fprintf(w, "\n\033[1;31m✗ FATAL: %v\033[0m\n", err)
return err
}
return nil
}
func main() {
fmt.Printf("%s", banner)
@ -84,8 +100,7 @@ func main() {
}
}
cmd := NewPicoclawCommand()
if err := cmd.Execute(); err != nil {
if err := runCommand(os.Stdout, os.Args[1:]); err != nil {
os.Exit(1)
}
}

View file

@ -1,6 +1,7 @@
package main
import (
"bytes"
"fmt"
"slices"
"testing"
@ -12,6 +13,19 @@ import (
"github.com/sipeed/picoclaw/pkg/config"
)
// TestRunCommandFatalPrintsToStdout verifies that when a subcommand fails,
// runCommand prints a prominent error to the provided writer (stdout in production),
// so the error is visible in environments where stderr is redirected (e.g. docker).
func TestRunCommandFatalPrintsToStdout(t *testing.T) {
var buf bytes.Buffer
// Pass an unknown flag to trigger a cobra error without any real I/O.
err := runCommand(&buf, []string{"gateway", "--no-such-flag"})
require.Error(t, err)
out := buf.String()
assert.Contains(t, out, "FATAL", "fatal error must be printed to stdout")
assert.Contains(t, out, err.Error(), "error message must appear in stdout output")
}
func TestNewPicoclawCommand(t *testing.T) {
cmd := NewPicoclawCommand()

View file

@ -385,6 +385,7 @@ func (c *WhatsAppNativeChannel) handleIncoming(evt *events.Message) {
}
if !c.IsAllowedSender(sender) {
logger.DebugCF("whatsapp", "WhatsApp message blocked (not in allow_from)", map[string]any{"sender_id": senderID})
return
}