package paneldecl import ( "git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk" ) // 本文件是字段层与表格元素层(列 / 行内动作)的拒绝用例。 // rejectCasesFieldCore 是字段的必填、形状与选项来源。 func rejectCasesFieldCore() []rejectCase { return []rejectCase{ { name: "字段 key 缺失", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[0].Key = "" }) }, want: ErrMissingField, wantPath: "adminPanels[0].sections[0].fields[0].key", }, { name: "字段 key 形状非法(中划线)", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[0].Key = "api-key" }) }, want: ErrInvalidFieldKey, wantPath: "adminPanels[0].sections[0].fields[0].key", }, { name: "字段类型不在闭集", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[0].Type = "chart" }) }, want: ErrUnknownFieldType, wantPath: "adminPanels[0].sections[0].fields[0].type", }, { name: "字段 key 在区块内重复", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[1].Key = "provider" }) }, want: ErrDuplicateID, wantPath: "adminPanels[0].sections[0].fields[1].key", }, { name: "optionsFrom 塞了任意 URL", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[0].OptionsFrom = "https://api.example.com/list" }) }, want: ErrInvalidOptionsFrom, wantPath: "adminPanels[0].sections[0].fields[0].optionsFrom", }, { name: "optionsFrom 配在 text 字段上", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[2].OptionsFrom = "plugin:providers" }) }, want: ErrInvalidOptionsFrom, wantPath: "adminPanels[0].sections[0].fields[2].optionsFrom", }, { name: "字段文案含控制字符", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[2].Label = "接口\n地址" }) }, want: ErrInvalidText, wantPath: "adminPanels[0].sections[0].fields[2].label", }, } } // rejectCasesFieldBoundsAndDefaults 是 secret 默认值、数值边界与默认值类型。 func rejectCasesFieldBoundsAndDefaults() []rejectCase { return []rejectCase{ { name: "secret 字段声明默认值", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[1].Default = "sk-leaked" }) }, want: ErrSecretDefault, wantPath: "adminPanels[0].sections[0].fields[1].default", }, { name: "min/max 配在 text 字段上", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[2].Min = floatPtr(1) }) }, want: ErrInvalidBounds, wantPath: "adminPanels[0].sections[0].fields[2].min", }, { name: "min 大于 max", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { f := &p.Sections[0].Fields[2] f.Type = sdk.FieldNumber f.Min = floatPtr(10) f.Max = floatPtr(1) }) }, want: ErrInvalidBounds, wantPath: "adminPanels[0].sections[0].fields[2].min", }, { name: "默认值与字段类型不匹配(下拉字段给了数字)", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Fields[0].Default = 42 }) }, want: ErrInvalidDefault, wantPath: "adminPanels[0].sections[0].fields[0].default", }, } } // rejectCasesTableItems 是列与行内动作的拒绝用例。 func rejectCasesTableItems() []rejectCase { return []rejectCase{ { name: "列 key 缺失", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].Columns[0].Key = "" }) }, want: ErrMissingField, wantPath: "adminPanels[0].sections[1].columns[0].key", }, { name: "列 key 重复", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].Columns[1].Key = "provider" }) }, want: ErrDuplicateID, wantPath: "adminPanels[0].sections[1].columns[1].key", }, { name: "列 label 含控制字符", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].Columns[0].Label = "状\t态" }) }, want: ErrInvalidText, wantPath: "adminPanels[0].sections[1].columns[0].label", }, { name: "行内动作 id 含大写", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].RowActions[0].ID = "Test" }) }, want: ErrInvalidRowActionID, wantPath: "adminPanels[0].sections[1].rowActions[0].id", }, { name: "行内动作缺 button 文案", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].RowActions[0].Label = "" }) }, want: ErrMissingField, wantPath: "adminPanels[0].sections[1].rowActions[0].label", }, { name: "行内动作塞了任意 URL", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].RowActions[0].Action = "https://evil.example.com/x" }) }, want: ErrInvalidAction, wantPath: "adminPanels[0].sections[1].rowActions[0].action", }, { name: "行内动作 id 重复", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].RowActions[1].ID = "test" }) }, want: ErrDuplicateID, wantPath: "adminPanels[0].sections[1].rowActions[1].id", }, { name: "submit 有动作键但没有动作名", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[0].Submit = &sdk.Submit{} }) }, want: ErrMissingField, wantPath: "adminPanels[0].sections[0].submit.action", }, { name: "source 动作名含冒号(URL 形态)", panels: func() []sdk.Panel { return panelsOf(samplePanel(), func(p *sdk.Panel) { p.Sections[1].Source = &sdk.Source{Action: "http://api.example.com/list"} }) }, want: ErrInvalidAction, wantPath: "adminPanels[0].sections[1].source.action", }, } }