Files
dingtalk-workspace-cli/internal/output/coverage_edges_test.go

368 lines
12 KiB
Go

package output
import (
"bytes"
"encoding/csv"
"encoding/json"
"errors"
"io"
"strings"
"testing"
"github.com/spf13/cobra"
)
var errOutputInjected = errors.New("injected output failure")
type failNthWriter struct {
nth int
calls int
}
func (w *failNthWriter) Write(p []byte) (int, error) {
w.calls++
if w.calls >= w.nth {
return 0, errOutputInjected
}
return len(p), nil
}
func TestCrossPlatformCoverageOutputJSONFailureInjection(t *testing.T) {
origMarshal := marshalJSON
origUnmarshal := unmarshalJSON
origOutput := marshalJSONOutput
origIndent := marshalJSONIndent
t.Cleanup(func() {
marshalJSON = origMarshal
unmarshalJSON = origUnmarshal
marshalJSONOutput = origOutput
marshalJSONIndent = origIndent
})
marshalJSON = func(any) ([]byte, error) { return nil, errOutputInjected }
if _, err := normalizePayload(struct{}{}); err == nil {
t.Fatal("normalizePayload should report marshal failure")
}
for name, call := range map[string]func() error{
"csv": func() error { return writeCSV(io.Discard, struct{}{}) },
"ndjson": func() error { return writeNDJSON(io.Discard, struct{}{}) },
"pretty": func() error { return writePretty(io.Discard, struct{}{}) },
"table": func() error { return writeTableish(io.Discard, struct{}{}) },
} {
if err := call(); err == nil {
t.Fatalf("%s should report normalization failure", name)
}
}
if _, err := roundTripJSON(struct{}{}); err == nil {
t.Fatal("roundTripJSON should report marshal failure")
}
if got := toGeneric(make(chan int)); got == nil {
t.Fatal("toGeneric should preserve an unmarshalable value")
}
if got := toGeneric(nil); got != nil {
t.Fatalf("toGeneric(nil) = %v", got)
}
marshalJSON = func(any) ([]byte, error) { return []byte("{"), nil }
if _, err := normalizePayload(struct{}{}); err == nil {
t.Fatal("normalizePayload should report unmarshal failure")
}
if _, err := roundTripJSON(struct{}{}); err == nil {
t.Fatal("roundTripJSON should report unmarshal failure")
}
if got := toGeneric("original"); got != "original" {
t.Fatalf("toGeneric fallback = %v", got)
}
marshalJSON = origMarshal
marshalJSONIndent = func(any, string, string) ([]byte, error) { return nil, errOutputInjected }
if err := WriteJSON(io.Discard, map[string]any{}); err == nil {
t.Fatal("WriteJSON should report marshal failure")
}
if err := ApplyJQ(io.Discard, map[string]any{"x": 1}, "."); err == nil {
t.Fatal("ApplyJQ should report marshal failure")
}
marshalJSONIndent = origIndent
marshalJSONOutput = func(any) ([]byte, error) { return nil, errOutputInjected }
if err := writeRaw(io.Discard, map[string]any{}); err == nil {
t.Fatal("writeRaw should report marshal failure")
}
if got := formatValue(make(chan int)); got == "" {
t.Fatalf("formatValue fallback = %q", got)
}
}
func TestCrossPlatformCoverageOutputFlagEdges(t *testing.T) {
if _, ok := formatFromFlagSet(nil, FormatRaw); ok {
t.Fatal("nil flag set should not resolve")
}
cmd := &cobra.Command{Use: "cmd"}
if _, ok := formatFromFlagSet(cmd.Flags(), FormatRaw); ok {
t.Fatal("missing flag should not resolve")
}
cmd.Flags().Int("format", 1, "wrong type")
if _, ok := formatFromFlagSet(cmd.Flags(), FormatRaw); ok {
t.Fatal("non-string format should not resolve")
}
for _, raw := range []string{"", "json", "raw", "table", "pretty", "ndjson", "csv", "unknown"} {
_ = normalizeFormat(raw, FormatRaw)
}
if rootPersistentFlags(nil) != nil {
t.Fatal("nil command should not have root flags")
}
if ResolveFields(nil) != "" || ResolveJQ(nil) != "" {
t.Fatal("nil command filters should be empty")
}
root := &cobra.Command{Use: "root"}
child := &cobra.Command{Use: "child"}
root.AddCommand(child)
if ResolveFields(child) != "" || ResolveJQ(child) != "" {
t.Fatal("missing global filters should be empty")
}
root.PersistentFlags().String("fields", "", "global fields")
root.PersistentFlags().String("jq", "", "global jq")
if err := root.PersistentFlags().Set("fields", "id,name"); err != nil {
t.Fatal(err)
}
if err := root.PersistentFlags().Set("jq", ".id"); err != nil {
t.Fatal(err)
}
if got := ResolveFields(child); got != "id,name" {
t.Fatalf("ResolveFields = %q", got)
}
if got := ResolveJQ(child); got != ".id" {
t.Fatalf("ResolveJQ = %q", got)
}
wrong := &cobra.Command{Use: "wrong"}
wrong.PersistentFlags().Int("fields", 1, "global fields")
wrong.PersistentFlags().Int("jq", 1, "global jq")
_ = wrong.PersistentFlags().Set("fields", "2")
_ = wrong.PersistentFlags().Set("jq", "2")
if ResolveFields(wrong) != "" || ResolveJQ(wrong) != "" {
t.Fatal("wrongly typed filters should be ignored")
}
}
func TestCrossPlatformCoverageOutputWriterFailures(t *testing.T) {
filtered := filterSlice([]any{"scalar", map[string]any{"keep": 1}}, map[string]bool{"keep": true})
if len(filtered) != 2 || filtered[0] != "scalar" {
t.Fatalf("filterSlice = %#v", filtered)
}
payload := map[string]any{
"items": []any{
map[string]any{"a": "one", "b": strings.Repeat("x", 80)},
map[string]any{"a": "two", "b": "three"},
},
"total": 2,
}
for nth := 1; nth <= 80; nth++ {
_ = writeTableish(&failNthWriter{nth: nth}, payload)
_ = writeTable(&failNthWriter{nth: nth}, []string{"a", "b"}, [][]string{{"1", "2"}, {"3", "4"}})
_ = writeKeyValues(&failNthWriter{nth: nth}, map[string]any{strings.Repeat("k", 30): "v", "z": 2})
}
if err := writeTable(io.Discard, nil, nil); err != nil {
t.Fatal(err)
}
if err := writeTable(io.Discard, []string{"a"}, [][]string{{"1", "ignored"}}); err != nil {
t.Fatal(err)
}
for _, call := range []func(io.Writer) error{
func(w io.Writer) error { return WriteJSON(w, map[string]any{"x": 1}) },
func(w io.Writer) error { return writeRaw(w, "text") },
func(w io.Writer) error { return writeRaw(w, map[string]any{"x": 1}) },
func(w io.Writer) error { return writeTableish(w, payload) },
func(w io.Writer) error { return ApplyJQ(w, []any{1, 2}, ".[]") },
} {
for nth := 1; nth <= 8; nth++ {
_ = call(&failNthWriter{nth: nth})
}
}
if err := ApplyJQ(io.Discard, 1, ".foo"); err == nil {
t.Fatal("jq evaluation error expected")
}
if err := ApplyJQ(io.Discard, nil, "["); err == nil {
t.Fatal("jq parse error expected")
}
}
func TestCrossPlatformCoverageCSVFailureAndShapeEdges(t *testing.T) {
origWrite := writeCSVRecord
t.Cleanup(func() { writeCSVRecord = origWrite })
for failAt := 1; failAt <= 3; failAt++ {
calls := 0
writeCSVRecord = func(cw *csv.Writer, row []string) error {
calls++
if calls == failAt {
return errOutputInjected
}
return origWrite(cw, row)
}
_ = writeTableCSV(csv.NewWriter(io.Discard), []string{"h"}, [][]string{{"a"}, {"b"}})
calls = 0
_ = writeKeyValueCSV(csv.NewWriter(io.Discard), map[string]any{"a": 1, "b": 2})
calls = 0
_ = writeCSV(io.Discard, "scalar")
}
writeCSVRecord = origWrite
_ = writeCSV(&failNthWriter{nth: 1}, nil)
if err := writeCSV(&failNthWriter{nth: 1}, "scalar"); err == nil {
t.Fatal("scalar CSV flush failure expected")
}
if err := writeTableCSV(csv.NewWriter(&failNthWriter{nth: 1}), []string{"h"}, [][]string{{"v"}}); err == nil {
t.Fatal("table CSV flush failure expected")
}
if err := writeKeyValueCSV(csv.NewWriter(&failNthWriter{nth: 1}), map[string]any{"a": 1}); err == nil {
t.Fatal("key/value CSV flush failure expected")
}
headers, rows := broadcastMeta([]string{"id"}, [][]string{{"1"}}, nil)
if len(headers) != 1 || len(rows) != 1 {
t.Fatal("empty metadata changed rows")
}
headers, rows = broadcastMeta([]string{"id"}, [][]string{{"1"}}, map[string]any{"id": "collision"})
if len(headers) != 1 || len(rows) != 1 {
t.Fatal("colliding metadata should be ignored")
}
_, rows = broadcastMeta([]string{"id"}, nil, map[string]any{"total": 0})
if len(rows) != 1 {
t.Fatal("empty rows should retain metadata")
}
if err := writeCSV(io.Discard, map[string]any{"data": map[string]any{"x": 1}}); err != nil {
t.Fatal(err)
}
if err := writeTableCSV(csv.NewWriter(io.Discard), []string{"a", "b"}, [][]string{{"one"}}); err != nil {
t.Fatal(err)
}
}
func TestCrossPlatformCoverageNDJSONFailureEdges(t *testing.T) {
origEncode := encodeNDJSON
t.Cleanup(func() { encodeNDJSON = origEncode })
encodeNDJSON = func(*json.Encoder, any) error { return errOutputInjected }
for _, payload := range []any{
[]any{1},
map[string]any{"items": []any{map[string]any{"x": 1}}},
map[string]any{"x": 1},
"scalar",
} {
if err := writeNDJSON(io.Discard, payload); err == nil {
t.Fatalf("encode failure expected for %T", payload)
}
}
encodeNDJSON = origEncode
if err := writeNDJSON(&failNthWriter{nth: 1}, map[string]any{"x": 1}); err == nil {
t.Fatal("flush failure expected")
}
}
func TestCrossPlatformCoveragePrettyAndFormattingEdges(t *testing.T) {
forceNoColor(t)
for _, payload := range []map[string]any{
{"kind": "schema", "degraded": true, "reason": "offline", "hint": "retry"},
{"kind": "schema", "degraded": true, "reason": "offline"},
{"kind": "schema", "other": true},
{"kind": "schema", "products": []any{"invalid", map[string]any{
"id": "p", "name": "same", "description": "same", "tools": []any{
"invalid",
map[string]any{"name": "same", "cli_name": "same"},
map[string]any{"name": "rpc", "cli_name": "cli"},
map[string]any{"name": "3"}, map[string]any{"name": "4"}, map[string]any{"name": "5"},
map[string]any{"name": "6"}, map[string]any{"name": "7"}, map[string]any{"name": "8"},
}},
}},
} {
if err := writePretty(io.Discard, payload); err != nil {
t.Fatal(err)
}
}
toolPayload := map[string]any{
"kind": "schema",
"product": "invalid",
"tool": map[string]any{
"name": "rpc",
"cli_name": "rpc",
"title": "rpc",
"description": "rpc",
"parameters": map[string]any{
"any": "invalid",
"array": map[string]any{"type": "array", "items": map[string]any{}},
"enum": map[string]any{"enum": []any{"a", 2}, "description": "line1\nline2"},
"required": map[string]any{"type": "string"},
},
"required": []any{"required", 3},
"flag_overlay": map[string]any{
"any": "invalid",
"enum": map[string]any{
"alias": "enum-alias",
"transform": "map",
"transform_args": map[string]any{"z": 2},
"env_default": "ENUM_ENV",
"default": "a",
},
},
"output_schema": map[string]any{"type": "object"},
},
}
if err := writePretty(io.Discard, toolPayload); err != nil {
t.Fatal(err)
}
for _, value := range []any{nil, "text", map[string]any{"x": 1}} {
_ = formatValue(value)
}
for _, tc := range []struct {
s string
w int
}{{"abc", 0}, {"abc", 1}, {"abcdef", 3}, {"你好世界", 4}} {
_ = truncate(tc.s, tc.w)
}
for _, prop := range []map[string]any{
{"type": "array", "items": map[string]any{"type": "string"}},
{"type": "array"},
{},
{"type": "number"},
} {
_ = describeType(prop)
}
if got, err := normalizePayload("text"); err != nil || got != "text" {
t.Fatalf("normalize string = %v, %v", got, err)
}
if got, err := normalizePayload(nil); err != nil || got != nil {
t.Fatalf("normalize nil = %v, %v", got, err)
}
}
func TestCrossPlatformCoverageExtractRowsNestedMetadataCollision(t *testing.T) {
payload := map[string]any{
"result": map[string]any{
"items": []any{map[string]any{"id": 1}},
"same": "inner",
"inner": true,
},
"same": "outer",
"outer": true,
}
_, _, meta, ok := extractRowsFromMap(payload)
if !ok || meta["same"] != "outer" || meta["inner"] != true || meta["outer"] != true {
t.Fatalf("unexpected metadata: %#v", meta)
}
if _, _, _, ok := extractRowsFromMap(map[string]any{"x": 1}); ok {
t.Fatal("non-list map should not extract rows")
}
var buf bytes.Buffer
if err := Write(&buf, FormatPretty, map[string]any{"x": 1}); err != nil {
t.Fatal(err)
}
}