Files
dingtalk-workspace-cli/internal/app/schema_reviewed_input_contract_test.go

128 lines
5.5 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package app
import (
"encoding/json"
"reflect"
"testing"
)
func TestSchemaReviewedInputMetadataUsesExecutableShapes(t *testing.T) {
canonicals := []string{
"aitable.field_update",
"attendance.adjustment_search",
"attendance.approve_list",
"attendance.group_search",
"attendance.overtime_search",
"attendance.selfsetting_get",
"attendance.vacation_update_type",
"chat.list_owned_or_admin_groups",
"sheet.batch_update",
"sheet.chart_create",
"sheet.chart_update",
"sheet.create_pivot_table",
"sheet.update_pivot_table",
"sheet.range_batch_clear",
"todo.add_todo_reminder",
"todo.get_user_todos_in_current_org",
}
payload := schemaContractPayloadForBoundCanonicals(t, NewRootCommand(), canonicals...)
optional := false
parameterCases := []struct {
canonical string
flag string
format string
enum []string
noEnum bool
example string
required *bool
requiredWhen string
jsonKind byte
}{
{canonical: "attendance.approve_list", flag: "start", format: "date"},
{canonical: "attendance.approve_list", flag: "end", format: "date"},
{canonical: "attendance.approve_list", flag: "types", noEnum: true, example: "overtime,leave"},
{canonical: "attendance.adjustment_search", flag: "page", required: &optional},
{canonical: "attendance.adjustment_search", flag: "limit", required: &optional},
{canonical: "attendance.group_search", flag: "page", required: &optional},
{canonical: "attendance.group_search", flag: "limit", required: &optional},
{canonical: "attendance.overtime_search", flag: "page", required: &optional},
{canonical: "attendance.overtime_search", flag: "limit", required: &optional},
{canonical: "attendance.selfsetting_get", flag: "setting-scene", enum: []string{
"checkRemind", "fastCheck", "checkResultNotify", "lackRemind",
"personalAttendStatNotify", "bossAttendStatNotify",
}},
{canonical: "chat.list_owned_or_admin_groups", flag: "role", enum: []string{"OWNER", "ADMIN"}, required: &optional},
{canonical: "chat.list_owned_or_admin_groups", flag: "limit", required: &optional},
{canonical: "sheet.batch_update", flag: "operations", format: "json", jsonKind: '['},
{canonical: "sheet.chart_create", flag: "properties", format: "json", jsonKind: '{'},
{canonical: "sheet.chart_update", flag: "properties", format: "json", jsonKind: '{'},
{canonical: "sheet.create_pivot_table", flag: "properties", format: "json", jsonKind: '{'},
{canonical: "sheet.update_pivot_table", flag: "properties", format: "json", jsonKind: '{'},
{canonical: "sheet.range_batch_clear", flag: "ranges", format: "json", jsonKind: '['},
{canonical: "todo.add_todo_reminder", flag: "base-time", enum: []string{"dueTime", "customTime"}},
{canonical: "todo.add_todo_reminder", flag: "due-date-offset", example: "-30", requiredWhen: "base-time is dueTime"},
{canonical: "todo.add_todo_reminder", flag: "reminder-time-stamp", example: "2026-03-10T18:00:00+08:00", requiredWhen: "base-time is customTime"},
{canonical: "todo.get_user_todos_in_current_org", flag: "role-types", noEnum: true, example: "creator,executor"},
}
for _, test := range parameterCases {
t.Run(test.canonical+"/"+test.flag, func(t *testing.T) {
tool := payload.Tools[test.canonical]
parameter := schemaContractMap(tool["parameters"])[test.flag]
if test.format != "" && parameter["format"] != test.format {
t.Fatalf("format = %#v, want %q", parameter["format"], test.format)
}
if test.enum != nil {
if got := schemaContractStringSlice(parameter["enum"]); !reflect.DeepEqual(got, test.enum) {
t.Fatalf("enum = %#v, want %#v", got, test.enum)
}
}
if test.noEnum {
if got := schemaContractStringSlice(parameter["enum"]); len(got) != 0 {
t.Fatalf("enum = %#v, want no scalar enum for a CSV parameter", got)
}
}
if test.example != "" && parameter["example"] != test.example {
t.Fatalf("example = %#v, want %q", parameter["example"], test.example)
}
if test.required != nil && parameter["required"] != *test.required {
t.Fatalf("required = %#v, want %v", parameter["required"], *test.required)
}
if test.requiredWhen != "" && parameter["required_when"] != test.requiredWhen {
t.Fatalf("required_when = %#v, want %q", parameter["required_when"], test.requiredWhen)
}
if test.jsonKind != 0 {
example, ok := parameter["example"].(string)
if !ok || example == "" {
t.Fatalf("example = %#v, want non-empty JSON", parameter["example"])
}
var decoded any
if err := json.Unmarshal([]byte(example), &decoded); err != nil {
t.Fatalf("example is invalid JSON: %v", err)
}
if example[0] != test.jsonKind {
t.Fatalf("example = %s, want JSON kind %q", example, test.jsonKind)
}
switch value := decoded.(type) {
case []any:
if len(value) == 0 {
t.Fatal("example must not be an empty array")
}
case map[string]any:
if len(value) == 0 {
t.Fatal("example must not be an empty object")
}
}
}
})
}
wantUpdateFields := []string{"name", "unit", "paid", "per-hours", "when-can-leave", "visibility-rules"}
assertSchemaContractConstraintGroup(t, payload.Tools["attendance.vacation_update_type"], "require_one_of", wantUpdateFields)
assertSchemaContractConstraintGroup(t, payload.Tools["aitable.field_update"], "require_one_of", []string{"name", "config", "ai-config"})
}