mirror of
https://github.com/boolean-maybe/tiki
synced 2026-04-21 13:37:20 +00:00
594 lines
16 KiB
Go
594 lines
16 KiB
Go
package plugin
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/boolean-maybe/tiki/ruki"
|
|
)
|
|
|
|
func TestParsePluginConfig_FullyInline(t *testing.T) {
|
|
schema := testSchema()
|
|
|
|
cfg := pluginFileConfig{
|
|
Name: "Inline Test",
|
|
Foreground: "#ffffff",
|
|
Background: "#000000",
|
|
Key: "I",
|
|
Lanes: []PluginLaneConfig{
|
|
{Name: "Todo", Filter: `select where status = "ready"`},
|
|
},
|
|
View: "expanded",
|
|
Default: true,
|
|
}
|
|
|
|
def, err := parsePluginConfig(cfg, "test", schema)
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
tp, ok := def.(*TikiPlugin)
|
|
if !ok {
|
|
t.Fatalf("Expected TikiPlugin, got %T", def)
|
|
}
|
|
|
|
if tp.Name != "Inline Test" {
|
|
t.Errorf("Expected name 'Inline Test', got '%s'", tp.Name)
|
|
}
|
|
|
|
if tp.Rune != 'I' {
|
|
t.Errorf("Expected rune 'I', got '%c'", tp.Rune)
|
|
}
|
|
|
|
if tp.ViewMode != "expanded" {
|
|
t.Errorf("Expected view mode 'expanded', got '%s'", tp.ViewMode)
|
|
}
|
|
|
|
if len(tp.Lanes) != 1 || tp.Lanes[0].Filter == nil {
|
|
t.Fatal("Expected lane filter to be parsed")
|
|
}
|
|
|
|
if !tp.Lanes[0].Filter.IsSelect() {
|
|
t.Error("Expected lane filter to be a SELECT statement")
|
|
}
|
|
|
|
if !tp.IsDefault() {
|
|
t.Error("Expected IsDefault() to return true")
|
|
}
|
|
}
|
|
|
|
func TestParsePluginConfig_Minimal(t *testing.T) {
|
|
cfg := pluginFileConfig{
|
|
Name: "Minimal",
|
|
Lanes: []PluginLaneConfig{
|
|
{Name: "Bugs", Filter: `select where type = "bug"`},
|
|
},
|
|
}
|
|
|
|
def, err := parsePluginConfig(cfg, "test", testSchema())
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
tp, ok := def.(*TikiPlugin)
|
|
if !ok {
|
|
t.Fatalf("Expected TikiPlugin, got %T", def)
|
|
}
|
|
|
|
if tp.Name != "Minimal" {
|
|
t.Errorf("Expected name 'Minimal', got '%s'", tp.Name)
|
|
}
|
|
|
|
if len(tp.Lanes) != 1 || tp.Lanes[0].Filter == nil {
|
|
t.Error("Expected lane filter to be parsed")
|
|
}
|
|
}
|
|
|
|
func TestParsePluginConfig_NoName(t *testing.T) {
|
|
cfg := pluginFileConfig{
|
|
Lanes: []PluginLaneConfig{
|
|
{Name: "Todo", Filter: `select where status = "ready"`},
|
|
},
|
|
}
|
|
|
|
_, err := parsePluginConfig(cfg, "test", testSchema())
|
|
if err == nil {
|
|
t.Fatal("Expected error for plugin without name")
|
|
}
|
|
}
|
|
|
|
func TestPluginTypeExplicit(t *testing.T) {
|
|
// inline plugin with type doki
|
|
cfg := pluginFileConfig{
|
|
Name: "Type Doki Test",
|
|
Type: "doki",
|
|
Fetcher: "internal",
|
|
Text: "some text",
|
|
}
|
|
|
|
def, err := parsePluginConfig(cfg, "test", testSchema())
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if def.GetType() != "doki" {
|
|
t.Errorf("Expected type 'doki', got '%s'", def.GetType())
|
|
}
|
|
|
|
if _, ok := def.(*DokiPlugin); !ok {
|
|
t.Errorf("Expected DokiPlugin type assertion to succeed")
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_WorkflowFile(t *testing.T) {
|
|
// create a temp directory with a workflow.yaml
|
|
tmpDir := t.TempDir()
|
|
workflowContent := `views:
|
|
- name: TestBoard
|
|
default: true
|
|
key: "F5"
|
|
lanes:
|
|
- name: Ready
|
|
filter: select where status = "ready"
|
|
- name: TestDocs
|
|
type: doki
|
|
fetcher: internal
|
|
text: "hello"
|
|
key: "D"
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("Failed to write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(errs) != 0 {
|
|
t.Fatalf("Expected no errors, got: %v", errs)
|
|
}
|
|
if len(plugins) != 2 {
|
|
t.Fatalf("Expected 2 plugins, got %d", len(plugins))
|
|
}
|
|
|
|
if plugins[0].GetName() != "TestBoard" {
|
|
t.Errorf("Expected first plugin 'TestBoard', got '%s'", plugins[0].GetName())
|
|
}
|
|
if plugins[1].GetName() != "TestDocs" {
|
|
t.Errorf("Expected second plugin 'TestDocs', got '%s'", plugins[1].GetName())
|
|
}
|
|
|
|
// verify config indices
|
|
if plugins[0].GetConfigIndex() != 0 {
|
|
t.Errorf("Expected config index 0, got %d", plugins[0].GetConfigIndex())
|
|
}
|
|
if plugins[1].GetConfigIndex() != 1 {
|
|
t.Errorf("Expected config index 1, got %d", plugins[1].GetConfigIndex())
|
|
}
|
|
|
|
// verify default flag
|
|
if !plugins[0].IsDefault() {
|
|
t.Error("Expected TestBoard to be default")
|
|
}
|
|
if plugins[1].IsDefault() {
|
|
t.Error("Expected TestDocs to not be default")
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_NoFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
plugins, _, errs := loadPluginsFromFile(filepath.Join(tmpDir, "workflow.yaml"), testSchema())
|
|
if plugins != nil {
|
|
t.Errorf("Expected nil plugins when no workflow.yaml, got %d", len(plugins))
|
|
}
|
|
if len(errs) != 1 {
|
|
t.Errorf("Expected 1 error for missing file, got %d", len(errs))
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_InvalidPlugin(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
workflowContent := `views:
|
|
- name: Valid
|
|
key: "V"
|
|
lanes:
|
|
- name: Todo
|
|
filter: select where status = "ready"
|
|
- name: Invalid
|
|
type: unknown
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("Failed to write workflow.yaml: %v", err)
|
|
}
|
|
|
|
// should load valid plugin and skip invalid one
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("Expected 1 valid plugin (invalid skipped), got %d", len(plugins))
|
|
}
|
|
if len(errs) != 1 {
|
|
t.Fatalf("Expected 1 error for invalid plugin, got %d: %v", len(errs), errs)
|
|
}
|
|
|
|
if plugins[0].GetName() != "Valid" {
|
|
t.Errorf("Expected plugin 'Valid', got '%s'", plugins[0].GetName())
|
|
}
|
|
}
|
|
|
|
func TestDefaultPlugin_ExplicitDefault(t *testing.T) {
|
|
plugins := []Plugin{
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "First"}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Second", Default: true}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Third"}},
|
|
}
|
|
got := DefaultPlugin(plugins)
|
|
if got.GetName() != "Second" {
|
|
t.Errorf("Expected 'Second' (marked default), got %q", got.GetName())
|
|
}
|
|
}
|
|
|
|
func TestDefaultPlugin_NoDefault(t *testing.T) {
|
|
plugins := []Plugin{
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Alpha"}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Beta"}},
|
|
}
|
|
got := DefaultPlugin(plugins)
|
|
if got.GetName() != "Alpha" {
|
|
t.Errorf("Expected first plugin 'Alpha' as fallback, got %q", got.GetName())
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_LegacyConversion(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
// workflow with legacy filter expressions that need conversion
|
|
workflowContent := `views:
|
|
- name: Board
|
|
key: "F5"
|
|
sort: Priority
|
|
lanes:
|
|
- name: Ready
|
|
filter: status = 'ready'
|
|
action: status = 'inProgress'
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(errs) != 0 {
|
|
t.Fatalf("expected no errors, got: %v", errs)
|
|
}
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("expected 1 plugin, got %d", len(plugins))
|
|
}
|
|
|
|
tp, ok := plugins[0].(*TikiPlugin)
|
|
if !ok {
|
|
t.Fatalf("expected TikiPlugin, got %T", plugins[0])
|
|
}
|
|
|
|
// filter should have been converted and parsed (with order by from sort)
|
|
if tp.Lanes[0].Filter == nil {
|
|
t.Fatal("expected filter to be parsed after legacy conversion")
|
|
}
|
|
if !tp.Lanes[0].Filter.IsSelect() {
|
|
t.Error("expected SELECT filter after conversion")
|
|
}
|
|
if tp.Lanes[0].Action == nil {
|
|
t.Fatal("expected action to be parsed after legacy conversion")
|
|
}
|
|
if !tp.Lanes[0].Action.IsUpdate() {
|
|
t.Error("expected UPDATE action after conversion")
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_UnnamedPlugin(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
workflowContent := `views:
|
|
- name: Valid
|
|
key: "V"
|
|
lanes:
|
|
- name: Todo
|
|
filter: select where status = "ready"
|
|
- lanes:
|
|
- name: Bad
|
|
filter: select where status = "done"
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
// unnamed plugin should be skipped, valid one should load
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("expected 1 valid plugin, got %d", len(plugins))
|
|
}
|
|
if len(errs) != 1 {
|
|
t.Fatalf("expected 1 error for unnamed plugin, got %d: %v", len(errs), errs)
|
|
}
|
|
if plugins[0].GetName() != "Valid" {
|
|
t.Errorf("expected plugin 'Valid', got %q", plugins[0].GetName())
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_InvalidYAML(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte("invalid: yaml: content: ["), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if plugins != nil {
|
|
t.Error("expected nil plugins for invalid YAML")
|
|
}
|
|
if len(errs) != 1 {
|
|
t.Fatalf("expected 1 error for invalid YAML, got %d: %v", len(errs), errs)
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_EmptyViews(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
workflowContent := `views: []
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(plugins) != 0 {
|
|
t.Errorf("expected 0 plugins for empty views, got %d", len(plugins))
|
|
}
|
|
if len(errs) != 0 {
|
|
t.Errorf("expected 0 errors for empty views, got %d", len(errs))
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_DokiConfigIndex(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
workflowContent := `views:
|
|
- name: Board
|
|
key: "B"
|
|
lanes:
|
|
- name: Todo
|
|
filter: select where status = "ready"
|
|
- name: Docs
|
|
key: "D"
|
|
type: doki
|
|
fetcher: internal
|
|
text: "hello"
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, _, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(errs) != 0 {
|
|
t.Fatalf("expected no errors, got: %v", errs)
|
|
}
|
|
if len(plugins) != 2 {
|
|
t.Fatalf("expected 2 plugins, got %d", len(plugins))
|
|
}
|
|
|
|
// verify DokiPlugin has correct ConfigIndex
|
|
dp, ok := plugins[1].(*DokiPlugin)
|
|
if !ok {
|
|
t.Fatalf("expected DokiPlugin, got %T", plugins[1])
|
|
}
|
|
if dp.ConfigIndex != 1 {
|
|
t.Errorf("expected DokiPlugin ConfigIndex 1, got %d", dp.ConfigIndex)
|
|
}
|
|
}
|
|
|
|
func TestMergePluginLists(t *testing.T) {
|
|
base := []Plugin{
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Board", FilePath: "base.yaml"}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Bugs", FilePath: "base.yaml"}},
|
|
}
|
|
overrides := []Plugin{
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "Board", FilePath: "override.yaml"}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "NewView", FilePath: "override.yaml"}},
|
|
}
|
|
|
|
result := mergePluginLists(base, overrides)
|
|
|
|
// Bugs (non-overridden) + Board (merged) + NewView (new)
|
|
if len(result) != 3 {
|
|
t.Fatalf("expected 3 plugins, got %d", len(result))
|
|
}
|
|
|
|
names := make([]string, len(result))
|
|
for i, p := range result {
|
|
names[i] = p.GetName()
|
|
}
|
|
|
|
// Bugs should come first (non-overridden base), then Board (merged), then NewView (new)
|
|
if names[0] != "Bugs" {
|
|
t.Errorf("expected first plugin 'Bugs', got %q", names[0])
|
|
}
|
|
if names[1] != "Board" {
|
|
t.Errorf("expected second plugin 'Board', got %q", names[1])
|
|
}
|
|
if names[2] != "NewView" {
|
|
t.Errorf("expected third plugin 'NewView', got %q", names[2])
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_GlobalActions(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
workflowContent := `views:
|
|
actions:
|
|
- key: "a"
|
|
label: "Assign to me"
|
|
action: update where id = id() set assignee=user()
|
|
plugins:
|
|
- name: Board
|
|
key: "B"
|
|
lanes:
|
|
- name: Todo
|
|
filter: select where status = "ready"
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, globalActions, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(errs) != 0 {
|
|
t.Fatalf("expected no errors, got: %v", errs)
|
|
}
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("expected 1 plugin, got %d", len(plugins))
|
|
}
|
|
if len(globalActions) != 1 {
|
|
t.Fatalf("expected 1 global action, got %d", len(globalActions))
|
|
}
|
|
if globalActions[0].Rune != 'a' {
|
|
t.Errorf("expected rune 'a', got %q", globalActions[0].Rune)
|
|
}
|
|
if globalActions[0].Label != "Assign to me" {
|
|
t.Errorf("expected label 'Assign to me', got %q", globalActions[0].Label)
|
|
}
|
|
}
|
|
|
|
func TestLoadPluginsFromFile_LegacyFormatWithGlobalActions(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
// old list format — should still load plugins (global actions not possible in old format)
|
|
workflowContent := `views:
|
|
- name: Board
|
|
key: "B"
|
|
lanes:
|
|
- name: Todo
|
|
filter: select where status = "ready"
|
|
`
|
|
workflowPath := filepath.Join(tmpDir, "workflow.yaml")
|
|
if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
|
|
t.Fatalf("write workflow.yaml: %v", err)
|
|
}
|
|
|
|
plugins, globalActions, errs := loadPluginsFromFile(workflowPath, testSchema())
|
|
if len(errs) != 0 {
|
|
t.Fatalf("expected no errors, got: %v", errs)
|
|
}
|
|
if len(plugins) != 1 {
|
|
t.Fatalf("expected 1 plugin, got %d", len(plugins))
|
|
}
|
|
if len(globalActions) != 0 {
|
|
t.Errorf("expected 0 global actions from legacy format, got %d", len(globalActions))
|
|
}
|
|
}
|
|
|
|
func TestMergeGlobalActions(t *testing.T) {
|
|
stmt := mustParseAction(t, `update where id = id() set status="ready"`)
|
|
|
|
base := []PluginAction{
|
|
{Rune: 'a', Label: "Assign", Action: stmt},
|
|
{Rune: 'b', Label: "Board", Action: stmt},
|
|
}
|
|
overrides := []PluginAction{
|
|
{Rune: 'b', Label: "Board Override", Action: stmt},
|
|
{Rune: 'c', Label: "Create", Action: stmt},
|
|
}
|
|
|
|
result := mergeGlobalActions(base, overrides)
|
|
if len(result) != 3 {
|
|
t.Fatalf("expected 3 actions, got %d", len(result))
|
|
}
|
|
// 'a' unchanged, 'b' overridden, 'c' appended
|
|
if result[0].Label != "Assign" {
|
|
t.Errorf("expected 'Assign', got %q", result[0].Label)
|
|
}
|
|
if result[1].Label != "Board Override" {
|
|
t.Errorf("expected 'Board Override', got %q", result[1].Label)
|
|
}
|
|
if result[2].Label != "Create" {
|
|
t.Errorf("expected 'Create', got %q", result[2].Label)
|
|
}
|
|
}
|
|
|
|
func TestMergeGlobalActions_EmptyOverrides(t *testing.T) {
|
|
stmt := mustParseAction(t, `update where id = id() set status="ready"`)
|
|
base := []PluginAction{{Rune: 'a', Label: "Assign", Action: stmt}}
|
|
result := mergeGlobalActions(base, nil)
|
|
if len(result) != 1 {
|
|
t.Fatalf("expected 1 action, got %d", len(result))
|
|
}
|
|
}
|
|
|
|
func TestMergeGlobalActionsIntoPlugins(t *testing.T) {
|
|
stmt := mustParseAction(t, `update where id = id() set status="ready"`)
|
|
|
|
plugins := []Plugin{
|
|
&TikiPlugin{
|
|
BasePlugin: BasePlugin{Name: "Board"},
|
|
Actions: []PluginAction{{Rune: 'b', Label: "Board action", Action: stmt}},
|
|
},
|
|
&TikiPlugin{
|
|
BasePlugin: BasePlugin{Name: "Backlog"},
|
|
Actions: nil,
|
|
},
|
|
&DokiPlugin{
|
|
BasePlugin: BasePlugin{Name: "Help"},
|
|
},
|
|
}
|
|
|
|
globals := []PluginAction{
|
|
{Rune: 'a', Label: "Assign", Action: stmt},
|
|
{Rune: 'b', Label: "Global board", Action: stmt}, // conflicts with Board's 'b'
|
|
}
|
|
|
|
mergeGlobalActionsIntoPlugins(plugins, globals)
|
|
|
|
// Board: should have 'b' (local) + 'a' (global) — 'b' global skipped
|
|
board, ok := plugins[0].(*TikiPlugin)
|
|
if !ok {
|
|
t.Fatalf("Board: expected *TikiPlugin, got %T", plugins[0])
|
|
}
|
|
if len(board.Actions) != 2 {
|
|
t.Fatalf("Board: expected 2 actions, got %d", len(board.Actions))
|
|
}
|
|
if board.Actions[0].Label != "Board action" {
|
|
t.Errorf("Board: first action should be local 'Board action', got %q", board.Actions[0].Label)
|
|
}
|
|
if board.Actions[1].Label != "Assign" {
|
|
t.Errorf("Board: second action should be global 'Assign', got %q", board.Actions[1].Label)
|
|
}
|
|
|
|
// Backlog: should have both globals ('a' and 'b')
|
|
backlog, ok := plugins[1].(*TikiPlugin)
|
|
if !ok {
|
|
t.Fatalf("Backlog: expected *TikiPlugin, got %T", plugins[1])
|
|
}
|
|
if len(backlog.Actions) != 2 {
|
|
t.Fatalf("Backlog: expected 2 actions, got %d", len(backlog.Actions))
|
|
}
|
|
|
|
// Help (DokiPlugin): should have no actions (skipped)
|
|
// DokiPlugin has no Actions field — nothing to check
|
|
}
|
|
|
|
func mustParseAction(t *testing.T, input string) *ruki.ValidatedStatement {
|
|
t.Helper()
|
|
parser := testParser()
|
|
stmt, err := parser.ParseAndValidateStatement(input, ruki.ExecutorRuntimePlugin)
|
|
if err != nil {
|
|
t.Fatalf("parse ruki statement %q: %v", input, err)
|
|
}
|
|
return stmt
|
|
}
|
|
|
|
func TestDefaultPlugin_MultipleDefaults(t *testing.T) {
|
|
plugins := []Plugin{
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "A"}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "B", Default: true}},
|
|
&TikiPlugin{BasePlugin: BasePlugin{Name: "C", Default: true}},
|
|
}
|
|
got := DefaultPlugin(plugins)
|
|
if got.GetName() != "B" {
|
|
t.Errorf("Expected first default 'B', got %q", got.GetName())
|
|
}
|
|
}
|