package cli import ( "bytes" "errors" "strings" "testing" "banger/internal/rpc" ) // TestTranslateRPCError pins the user-facing error rendering for // every code the daemon emits today plus the catch-all unknown-code // path. Buffer is non-TTY so style helpers no-op and assertions // stay readable. func TestTranslateRPCError(t *testing.T) { var buf bytes.Buffer cases := []struct { name string code string msg string opID string expect string }{ {"operation_failed strips code", "operation_failed", "vm running", "", "vm running"}, {"empty code drops prefix", "", "raw boom", "", "raw boom"}, {"not_found", "not_found", `vm "x" not found`, "", `not found: vm "x" not found`}, {"not_running", "not_running", "vm is not running", "", "not running: vm is not running"}, {"already_exists", "already_exists", "image foo", "", "already exists: image foo"}, {"bad_request", "bad_request", "missing rootfs", "", "bad request: missing rootfs"}, {"bad_params", "bad_params", "invalid tap name", "", "bad request: invalid tap name"}, {"bad_version", "bad_version", "unsupported version 99", "", "version mismatch: unsupported version 99"}, {"unauthorized", "unauthorized", "uid 1000 not allowed", "", "unauthorized: uid 1000 not allowed"}, {"unknown_method", "unknown_method", "no.such.method", "", "unknown method: no.such.method"}, {"unknown code falls through", "weird_new_code", "boom", "", "weird_new_code: boom"}, {"op_id appended in parens", "operation_failed", "boom", "op-deadbeef00ff", "boom (op-deadbeef00ff)"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { err := &rpc.ErrorResponse{Code: tc.code, Message: tc.msg, OpID: tc.opID} got := translateRPCError(&buf, err) if got != tc.expect { t.Errorf("got %q, want %q", got, tc.expect) } }) } } // TestTranslateRPCErrorPassesThroughNonRPCErrors covers the dial // failure / decode failure paths where rpc.Call returns a plain Go // error rather than *rpc.ErrorResponse. The translator must not // hide the original message — that's the only signal an operator // has when the daemon is down. func TestTranslateRPCErrorPassesThroughNonRPCErrors(t *testing.T) { var buf bytes.Buffer got := translateRPCError(&buf, errors.New("dial unix /run/banger/bangerd.sock: connect: no such file or directory")) if !strings.Contains(got, "no such file or directory") { t.Fatalf("plain error lost: got %q", got) } }