311 lines
11 KiB
Go
311 lines
11 KiB
Go
package helpers
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func TestWhiteboardInjectedEncodingFailures(t *testing.T) {
|
|
previousMarshal := whiteboardJSONMarshal
|
|
whiteboardJSONMarshal = func(any) ([]byte, error) { return nil, errors.New("marshal") }
|
|
if got := buildWhiteboardCardJSONML("b", "w"); got != "" {
|
|
t.Fatalf("got %q", got)
|
|
}
|
|
whiteboardJSONMarshal = previousMarshal
|
|
|
|
previousPrepare := prepareWhiteboardCard
|
|
prepareWhiteboardCard = func(*cobra.Command, string) (string, error) { return "", errors.New("prepare") }
|
|
t.Cleanup(func() { prepareWhiteboardCard = previousPrepare })
|
|
caller := &whiteboardTestCaller{}
|
|
installWhiteboardTestCaller(t, caller)
|
|
cmd := newDocWhiteboardCommand()
|
|
cmd.SetArgs([]string{"insert", "--node", "n", "--yes"})
|
|
if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "模板未通过") {
|
|
t.Fatalf("err=%v", err)
|
|
}
|
|
|
|
previousCompact := compactWhiteboardJSON
|
|
compactWhiteboardJSON = func(*bytes.Buffer, []byte) error { return errors.New("compact") }
|
|
t.Cleanup(func() { compactWhiteboardJSON = previousCompact })
|
|
if _, _, err := validateWhiteboardNodes(json.RawMessage(`[{"id":"n"}]`)); err == nil {
|
|
t.Fatal("expected compact error")
|
|
}
|
|
}
|
|
|
|
func TestDocWhiteboardInsertDryRun(t *testing.T) {
|
|
caller := &whiteboardTestCaller{dry: true}
|
|
installWhiteboardTestCaller(t, caller)
|
|
cmd := newDocWhiteboardCommand()
|
|
cmd.SetArgs([]string{"insert", "--node", "n"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func writeWhiteboardFixture(t *testing.T, content string) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "whiteboard.json")
|
|
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func TestLoadWhiteboardUpdateFileRejectsInvalidInputs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
}{
|
|
{name: "invalid json", content: `{`},
|
|
{name: "trailing value", content: `{}` + ` {}`},
|
|
{name: "missing source", content: `{}`},
|
|
{name: "schema version", content: `{"source":{"schemaVersion":"2.0","catalogVersion":"dml-v1","nodes":[]}}`},
|
|
{name: "catalog version", content: `{"source":{"schemaVersion":"1.0","catalogVersion":"v2","nodes":[]}}`},
|
|
{name: "nodes missing", content: `{"source":{"schemaVersion":"1.0","catalogVersion":"dml-v1"}}`},
|
|
{name: "nodes malformed", content: `{"source":{"schemaVersion":"1.0","catalogVersion":"dml-v1","nodes":[}}`},
|
|
{name: "node primitive", content: `{"source":{"schemaVersion":"1.0","catalogVersion":"dml-v1","nodes":[1]}}`},
|
|
{name: "append empty", content: `{"source":{"schemaVersion":"1.0","catalogVersion":"dml-v1","nodes":[]}}`},
|
|
{name: "unknown field", content: `{"unknown":true}`},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if _, _, err := loadWhiteboardUpdateFile(writeWhiteboardFixture(t, test.content)); err == nil {
|
|
t.Fatal("expected validation error")
|
|
}
|
|
})
|
|
}
|
|
|
|
if _, _, err := loadWhiteboardUpdateFile(filepath.Join(t.TempDir(), "missing.json")); err == nil {
|
|
t.Fatal("expected missing-file error")
|
|
}
|
|
if _, _, err := loadWhiteboardUpdateFile(t.TempDir()); err == nil {
|
|
t.Fatal("expected directory read error")
|
|
}
|
|
if _, _, err := validateWhiteboardNodes(json.RawMessage(`[`)); err == nil {
|
|
t.Fatal("expected malformed nodes array error")
|
|
}
|
|
input, nodes, err := loadWhiteboardUpdateFile(writeWhiteboardFixture(t,
|
|
`{"overwrite":true,"source":{"schemaVersion":"1.0","catalogVersion":"dml-v1","nodes":[]}}`))
|
|
if err != nil || !input.Overwrite || nodes != "[]" {
|
|
t.Fatalf("input=%#v nodes=%q err=%v", input, nodes, err)
|
|
}
|
|
}
|
|
|
|
func TestWhiteboardOutputFiltersAndToolResponseErrors(t *testing.T) {
|
|
for _, name := range []string{"jq", "fields"} {
|
|
t.Run(name, func(t *testing.T) {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().String(name, "", "")
|
|
if err := cmd.Flags().Set(name, ".result"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := rejectWhiteboardOutputFilters(cmd); err == nil {
|
|
t.Fatal("expected rejected output filter")
|
|
}
|
|
})
|
|
}
|
|
|
|
responses := []string{
|
|
`{`,
|
|
`{} {}`,
|
|
`null`,
|
|
`{"resultJson":"{"}`,
|
|
`{"resultJson":"{} {}"}`,
|
|
}
|
|
for _, response := range responses {
|
|
caller := &whiteboardTestCaller{format: "json", response: func(whiteboardTestCall, int) string { return response }}
|
|
installWhiteboardTestCaller(t, caller)
|
|
if err := callWhiteboardTool(&cobra.Command{}, whiteboardQueryTool, nil); err == nil {
|
|
t.Fatalf("response %q should fail", response)
|
|
}
|
|
}
|
|
|
|
caller := &whiteboardTestCaller{dry: true}
|
|
installWhiteboardTestCaller(t, caller)
|
|
if err := callWhiteboardTool(&cobra.Command{}, whiteboardQueryTool, map[string]any{"partId": "p"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
caller = &whiteboardTestCaller{response: func(whiteboardTestCall, int) string { return "" }}
|
|
installWhiteboardTestCaller(t, caller)
|
|
if err := callWhiteboardTool(&cobra.Command{}, whiteboardQueryTool, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestWhiteboardDocumentQueryValidation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
response string
|
|
attrs bool
|
|
}{
|
|
{name: "invalid response", response: `{`},
|
|
{name: "missing block", response: `{"blocks":[]}`},
|
|
{name: "non object block", response: `{"blocks":[1]}`},
|
|
{name: "invalid jsonml", response: `{"blocks":[{"blockId":"b","jsonml":"{"}]}`},
|
|
{name: "missing attrs", response: `{"blocks":[{"blockId":"b","jsonml":"[]"}]}`, attrs: true},
|
|
{name: "attrs not object", response: `{"blocks":[{"blockId":"b","jsonml":"[\"card\",1]"}]}`, attrs: true},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
caller := &whiteboardTestCaller{response: func(whiteboardTestCall, int) string { return test.response }}
|
|
installWhiteboardTestCaller(t, caller)
|
|
var err error
|
|
if test.attrs {
|
|
_, err = queryWhiteboardCardAttrs(context.Background(), "n", "b")
|
|
} else {
|
|
_, err = queryWhiteboardCardNode(context.Background(), "n", "b")
|
|
}
|
|
if err == nil {
|
|
t.Fatal("expected query validation error")
|
|
}
|
|
})
|
|
}
|
|
caller := &whiteboardTestCaller{err: func(whiteboardTestCall, int) error { return errors.New("boom") }}
|
|
installWhiteboardTestCaller(t, caller)
|
|
if _, err := queryWhiteboardCardNode(context.Background(), "n", "b"); err == nil {
|
|
t.Fatal("expected caller error")
|
|
}
|
|
}
|
|
|
|
func TestWhiteboardCommandValidationBranches(t *testing.T) {
|
|
caller := &whiteboardTestCaller{format: "json"}
|
|
installWhiteboardTestCaller(t, caller)
|
|
for _, args := range [][]string{
|
|
{"query", "--node", "n"},
|
|
{"query", "--node", "n", "--part-id", "p", "--jq", "."},
|
|
{"update", "--node", "n", "--part-id", "p"},
|
|
{"update", "--node", "n", "--part-id", "p", "--fields", "result"},
|
|
} {
|
|
cmd := newWhiteboardCommand()
|
|
cmd.PersistentFlags().String("jq", "", "")
|
|
cmd.PersistentFlags().String("fields", "", "")
|
|
cmd.SetArgs(args)
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatalf("args %v should fail", args)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWhiteboardUpdateOverwriteAndSourceErrors(t *testing.T) {
|
|
caller := &whiteboardTestCaller{format: "json"}
|
|
installWhiteboardTestCaller(t, caller)
|
|
cmd := newWhiteboardCommand()
|
|
cmd.SetArgs([]string{"update", "--node", "n", "--part-id", "p", "--source", writeWhiteboardFixture(t,
|
|
`{"overwrite":true,"source":{"schemaVersion":"1.0","catalogVersion":"dml-v1","nodes":[]}}`), "--yes"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if caller.calls[0].args["mode"] != "overwrite" {
|
|
t.Fatalf("args=%#v", caller.calls[0].args)
|
|
}
|
|
|
|
cmd = newWhiteboardCommand()
|
|
cmd.SetArgs([]string{"update", "--node", "n", "--part-id", "p", "--source", filepath.Join(t.TempDir(), "missing")})
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatal("expected source error")
|
|
}
|
|
}
|
|
|
|
func TestDocMediaUploadValidationAndSuccess(t *testing.T) {
|
|
caller := &whiteboardTestCaller{response: func(whiteboardTestCall, int) string {
|
|
return `{"uploadUrl":"https://upload.example.test/token","resourceId":"r","resourceUrl":"https://resource.example.test/icon"}`
|
|
}}
|
|
installWhiteboardTestCaller(t, caller)
|
|
previousPut := httpPutFile
|
|
httpPutFile = func(context.Context, string, map[string]string, string, int64) error { return nil }
|
|
t.Cleanup(func() { httpPutFile = previousPut })
|
|
file := writeWhiteboardFixture(t, "svg")
|
|
cmd := newDocCommand()
|
|
cmd.SetArgs([]string{"media", "upload", "--node", "n", "--file", file, "--name", "icon", "--mime-type", "image/custom", "--yes"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if caller.calls[0].args["fileName"] != "icon.json" || caller.calls[0].args["mimeType"] != "image/custom" {
|
|
t.Fatalf("args=%#v", caller.calls[0].args)
|
|
}
|
|
|
|
for _, args := range [][]string{
|
|
{"media", "upload", "--node", "n"},
|
|
{"media", "upload", "--node", "n", "--file", filepath.Join(t.TempDir(), "missing")},
|
|
{"media", "upload", "--node", "n", "--file", t.TempDir()},
|
|
} {
|
|
cmd = newDocCommand()
|
|
cmd.SetArgs(args)
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatalf("args %v should fail", args)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDocMediaUploadRemainingBranches(t *testing.T) {
|
|
file := writeWhiteboardFixture(t, "svg")
|
|
|
|
caller := &whiteboardTestCaller{dry: true}
|
|
installWhiteboardTestCaller(t, caller)
|
|
cmd := newDocCommand()
|
|
cmd.SetArgs([]string{"media", "upload", "--node", "n", "--file", file})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
caller *whiteboardTestCaller
|
|
response string
|
|
}{
|
|
{name: "caller error", caller: &whiteboardTestCaller{err: func(whiteboardTestCall, int) error { return errors.New("call") }}},
|
|
{name: "missing resource url", caller: &whiteboardTestCaller{response: func(whiteboardTestCall, int) string {
|
|
return `{"uploadUrl":"https://upload.example.test/token","resourceId":"r"}`
|
|
}}},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
installWhiteboardTestCaller(t, test.caller)
|
|
cmd := newDocCommand()
|
|
cmd.SetArgs([]string{"media", "upload", "--node", "n", "--file", file, "--yes"})
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatal("expected upload error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDocWhiteboardInsertCallerError(t *testing.T) {
|
|
caller := &whiteboardTestCaller{err: func(call whiteboardTestCall, index int) error {
|
|
if index == 0 {
|
|
return errors.New("insert")
|
|
}
|
|
return nil
|
|
}}
|
|
installWhiteboardTestCaller(t, caller)
|
|
cmd := newDocWhiteboardCommand()
|
|
cmd.SetArgs([]string{"insert", "--node", "n", "--yes"})
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatal("expected insert error")
|
|
}
|
|
}
|
|
|
|
func TestExtractWhiteboardIDAndJSONEOF(t *testing.T) {
|
|
if got := extractWhiteboardID(nil); got != "" {
|
|
t.Fatalf("got %q", got)
|
|
}
|
|
if got := extractWhiteboardID(map[string]any{"metadata": map[string]any{"id": 1}}); got != "" {
|
|
t.Fatalf("got %q", got)
|
|
}
|
|
decoder := json.NewDecoder(strings.NewReader(`{} trailing`))
|
|
var value any
|
|
if err := decoder.Decode(&value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ensureWhiteboardJSONEOF(decoder); err == nil {
|
|
t.Fatal("expected trailing token error")
|
|
}
|
|
}
|