d35e1f2832
Render(v) treats JSON as the model: in -json mode it prints v as JSON; otherwise it paints it as text — object scalars on one line as key=value (keys blue, values bright white: a structural key/value distinction, not semantic color), nested objects indented, arrays one block per element, field order preserved via an order-keeping JSON decode. EmitJSON(v) is the JSON-only arm for commands that paint their own text. Operates on JSON only, so core stays protobuf-free (NFR-4). Adds White to the palette. The example gains an `inspect` command demoing Render (text vs -json). design.md FR-4.5/4.6 document the renderer and the "JSON is always the full record; synopsis-vs-detail is text-only" principle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
// SPDX-FileCopyrightText: (C) Copyright 2026 Pim van Pelt <pim@ipng.ch>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestRenderTextNoColor checks the plain text shape: object scalars on one line
|
|
// as key=value, nested objects indented under "key:", arrays as one block per
|
|
// element, and source field order preserved (not sorted).
|
|
func TestRenderTextNoColor(t *testing.T) {
|
|
SetColor(false)
|
|
raw := json.RawMessage(`{
|
|
"instanceId": "host1",
|
|
"connected": true,
|
|
"version": "1.2.3",
|
|
"labels": {"site": "ams", "rack": "b3"},
|
|
"bvis": [
|
|
{"evpnId": "blue", "installed": true},
|
|
{"evpnId": "red", "installed": false}
|
|
]
|
|
}`)
|
|
got, err := renderText(raw)
|
|
if err != nil {
|
|
t.Fatalf("renderText: %v", err)
|
|
}
|
|
want := "instanceId=host1 connected=true version=1.2.3\n" +
|
|
"labels:\n" +
|
|
" site=ams rack=b3\n" +
|
|
"bvis:\n" +
|
|
" evpnId=blue installed=true\n" +
|
|
" evpnId=red installed=false\n"
|
|
if got != want {
|
|
t.Errorf("renderText mismatch:\n--- got ---\n%s\n--- want ---\n%s", got, want)
|
|
}
|
|
}
|
|
|
|
// TestRenderTextColor checks keys are blue and values bright white when color is
|
|
// on.
|
|
func TestRenderTextColor(t *testing.T) {
|
|
SetColor(true)
|
|
defer SetColor(false)
|
|
got, err := renderText(json.RawMessage(`{"a": "x"}`))
|
|
if err != nil {
|
|
t.Fatalf("renderText: %v", err)
|
|
}
|
|
want := Blue + "a" + Reset + "=" + White + "x" + Reset + "\n"
|
|
if got != want {
|
|
t.Errorf("colored render = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
// TestRenderPreservesNumberAndNull checks json.Number passes through verbatim
|
|
// (no float reformatting) and null renders as "null".
|
|
func TestRenderPreservesNumberAndNull(t *testing.T) {
|
|
SetColor(false)
|
|
got, err := renderText(json.RawMessage(`{"vni": 10000000, "primary": null}`))
|
|
if err != nil {
|
|
t.Fatalf("renderText: %v", err)
|
|
}
|
|
if !strings.Contains(got, "vni=10000000") {
|
|
t.Errorf("number not verbatim: %q", got)
|
|
}
|
|
if !strings.Contains(got, "primary=null") {
|
|
t.Errorf("null not rendered: %q", got)
|
|
}
|
|
}
|
|
|
|
// TestRenderTextFromStruct checks a Go struct (not RawMessage) is accepted and
|
|
// keeps struct field order.
|
|
func TestRenderTextFromStruct(t *testing.T) {
|
|
SetColor(false)
|
|
v := struct {
|
|
Name string `json:"name"`
|
|
Count int `json:"count"`
|
|
}{Name: "web1", Count: 3}
|
|
raw, err := toRawJSON(v)
|
|
if err != nil {
|
|
t.Fatalf("toRawJSON: %v", err)
|
|
}
|
|
got, err := renderText(raw)
|
|
if err != nil {
|
|
t.Fatalf("renderText: %v", err)
|
|
}
|
|
if got != "name=web1 count=3\n" {
|
|
t.Errorf("struct render = %q", got)
|
|
}
|
|
}
|