verstak-desktop/internal/core/workbench/routing_test.go

450 lines
13 KiB
Go

package workbench
import (
"testing"
"github.com/verstak/verstak-desktop/internal/core/contribution"
"github.com/verstak/verstak-desktop/internal/core/plugin"
)
func provider(pluginID, id string, priority int, component string, supports ...plugin.OpenProviderSupport) contribution.ContributionOpenProvider {
return contribution.ContributionOpenProvider{
PluginID: pluginID,
Item: plugin.ContributionOpenProvider{
ID: id,
Title: id,
Priority: priority,
Component: component,
Supports: supports,
},
}
}
func TestSelectProviderUsesNotesMarkdownPreference(t *testing.T) {
r := NewRouter(Preferences{
DefaultNotesMarkdownEditorProvider: "community.notes-editor",
})
providers := []contribution.ContributionOpenProvider{
provider("official.editor", "official.markdown", 100, "OfficialMarkdown", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md", ".markdown"},
Contexts: []string{"generic-markdown", "notes-markdown"},
}),
provider("community.editor", "community.notes-editor", 10, "CommunityNotes", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{"notes-markdown"},
}),
}
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Clients/Acme/Notes/Overview.md",
Extension: ".md",
Mode: "edit",
Context: OpenResourceContext{
SourceView: "notes",
IsInsideNotesFolder: true,
NotesMode: true,
},
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "community.notes-editor" {
t.Fatalf("provider = %q, want community.notes-editor", selected.Item.ID)
}
}
func TestSelectProviderFallsBackByPriorityThenID(t *testing.T) {
r := NewRouter(Preferences{
DefaultMarkdownEditorProvider: "disabled.or.missing",
})
providers := []contribution.ContributionOpenProvider{
provider("b.plugin", "b.provider", 100, "B", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{"generic-markdown"},
}),
provider("a.plugin", "a.provider", 100, "A", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{"generic-markdown"},
}),
provider("high.plugin", "high.provider", 200, "High", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".txt"},
}),
}
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/readme.md",
Extension: ".md",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "a.provider" {
t.Fatalf("provider = %q, want deterministic tie winner a.provider", selected.Item.ID)
}
}
func TestSelectProviderTieBreaksByPluginIDThenProviderID(t *testing.T) {
r := NewRouter(Preferences{})
providers := []contribution.ContributionOpenProvider{
provider("b.plugin", "a.provider", 100, "B", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{ContextGenericMarkdown},
}),
provider("a.plugin", "z.provider", 100, "A", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{ContextGenericMarkdown},
}),
}
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/readme.md",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.PluginID != "a.plugin" || selected.Item.ID != "z.provider" {
t.Fatalf("provider = %+v, want a.plugin/z.provider", selected)
}
}
func TestSelectProviderMatchesGenericTextContext(t *testing.T) {
r := NewRouter(Preferences{})
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/readme.txt",
Mode: "view",
}, []contribution.ContributionOpenProvider{
provider("text.plugin", "text.provider", 10, "Text", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".txt"},
Contexts: []string{ContextGenericText},
}),
})
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "text.provider" {
t.Fatalf("provider = %q, want text.provider", selected.Item.ID)
}
}
func TestGenericMarkdownDoesNotSelectNotesOnlyProvider(t *testing.T) {
r := NewRouter(Preferences{})
_, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/readme.md",
Mode: "view",
}, []contribution.ContributionOpenProvider{
provider("notes.plugin", "notes.provider", 10, "Notes", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{ContextNotesMarkdown},
}),
})
if err == nil {
t.Fatal("expected no provider for generic markdown with notes-only provider")
}
}
func TestOpenResourceStoresSelectedProviderAndRequest(t *testing.T) {
r := NewRouter(Preferences{})
result, err := r.OpenResource(OpenResourceRequest{
Kind: "vault-file",
Path: "Notes/Overview.md",
Extension: ".md",
Mode: "edit",
Context: OpenResourceContext{
IsInsideNotesFolder: true,
NotesMode: true,
},
}, []contribution.ContributionOpenProvider{
provider("official.editor", "official.markdown", 100, "MarkdownEditor", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{"notes-markdown"},
}),
})
if err != nil {
t.Fatalf("OpenResource: %v", err)
}
if result.Status != "opened" || result.ProviderID != "official.markdown" || result.ProviderComponent != "MarkdownEditor" {
t.Fatalf("result = %+v", result)
}
opened := r.OpenedResources()
if len(opened) != 1 || opened[0].Request.Path != "Notes/Overview.md" {
t.Fatalf("opened = %+v", opened)
}
}
func TestOpenResourceReturnsNoProviderFallback(t *testing.T) {
r := NewRouter(Preferences{})
result, err := r.OpenResource(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/unknown.bin",
}, nil)
if err != nil {
t.Fatalf("OpenResource: %v", err)
}
if result.Status != "no-provider" || result.Request.Path != "Docs/unknown.bin" || result.Message == "" {
t.Fatalf("result = %+v", result)
}
if len(r.OpenedResources()) != 0 {
t.Fatalf("no-provider result should not store opened resource: %+v", r.OpenedResources())
}
}
func TestSelectProviderUsesTextPreference(t *testing.T) {
r := NewRouter(Preferences{
DefaultTextEditorProvider: "custom.text-editor",
})
providers := []contribution.ContributionOpenProvider{
provider("official.editor", "builtin.text", 100, "BuiltinText", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".txt"},
Contexts: []string{ContextGenericText},
}),
provider("custom.editor", "custom.text-editor", 10, "CustomText", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".txt"},
Contexts: []string{ContextGenericText},
}),
}
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/todo.txt",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "custom.text-editor" {
t.Fatalf("provider = %q, want custom.text-editor", selected.Item.ID)
}
}
func TestSelectProviderUsesMarkdownPreference(t *testing.T) {
r := NewRouter(Preferences{
DefaultMarkdownEditorProvider: "community.markdown-editor",
})
providers := []contribution.ContributionOpenProvider{
provider("official.editor", "builtin.markdown", 100, "BuiltinMarkdown", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{ContextGenericMarkdown},
}),
provider("community.editor", "community.markdown-editor", 10, "CommunityMarkdown", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{ContextGenericMarkdown},
}),
}
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/readme.md",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "community.markdown-editor" {
t.Fatalf("provider = %q, want community.markdown-editor", selected.Item.ID)
}
}
func TestSelectProviderMatchesMime(t *testing.T) {
r := NewRouter(Preferences{})
providers := []contribution.ContributionOpenProvider{
provider("image.plugin", "image.viewer", 10, "ImageViewer", plugin.OpenProviderSupport{
Kind: "vault-file",
Mime: []string{"image/png", "image/jpeg"},
}),
}
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Photos/screenshot.png",
Extension: ".png",
Mime: "image/png",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "image.viewer" {
t.Fatalf("provider = %q, want image.viewer", selected.Item.ID)
}
}
func TestSelectProviderExtensionCaseInsensitive(t *testing.T) {
r := NewRouter(Preferences{})
providers := []contribution.ContributionOpenProvider{
provider("editor.plugin", "md.editor", 10, "MDEditor", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
}),
}
tests := []struct {
name string
ext string
path string
}{
{"uppercase .MD", ".MD", "Docs/README.MD"},
{"mixed case .Md", ".Md", "Docs/Notes.Md"},
{"lowercase .md", ".md", "Docs/readme.md"},
{"markdown extension uppercase", ".MD", "Docs/guide.MD"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: tt.path,
Extension: tt.ext,
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "md.editor" {
t.Fatalf("provider = %q, want md.editor for ext %s", selected.Item.ID, tt.ext)
}
})
}
}
func TestSelectProviderMultipleSupportsEntries(t *testing.T) {
r := NewRouter(Preferences{})
providers := []contribution.ContributionOpenProvider{
provider("editor.plugin", "multi.editor", 10, "MultiEditor", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
Contexts: []string{ContextGenericMarkdown},
}, plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".txt"},
Contexts: []string{ContextGenericText},
}),
}
t.Run("matches markdown entry", func(t *testing.T) {
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/readme.md",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "multi.editor" {
t.Fatalf("provider = %q, want multi.editor", selected.Item.ID)
}
})
t.Run("matches text entry", func(t *testing.T) {
selected, err := r.SelectProvider(OpenResourceRequest{
Kind: "vault-file",
Path: "Docs/todo.txt",
Mode: "view",
}, providers)
if err != nil {
t.Fatalf("SelectProvider: %v", err)
}
if selected.Item.ID != "multi.editor" {
t.Fatalf("provider = %q, want multi.editor", selected.Item.ID)
}
})
}
func TestSelectProviderKindMismatch(t *testing.T) {
r := NewRouter(Preferences{})
_, err := r.SelectProvider(OpenResourceRequest{
Kind: "http-url",
Path: "https://example.com/file.md",
Mode: "view",
}, []contribution.ContributionOpenProvider{
provider("editor.plugin", "vault.editor", 10, "VaultEditor", plugin.OpenProviderSupport{
Kind: "vault-file",
Extensions: []string{".md"},
}),
})
if err == nil {
t.Fatal("expected no provider for http-url kind with vault-file provider")
}
}
func TestDetermineContextName(t *testing.T) {
tests := []struct {
name string
request OpenResourceRequest
want string
}{
{
name: "text",
request: OpenResourceRequest{Kind: "vault-file", Path: "Docs/readme.txt"},
want: ContextGenericText,
},
{
name: "markdown",
request: OpenResourceRequest{Kind: "vault-file", Path: "Docs/readme.md"},
want: ContextGenericMarkdown,
},
{
name: "notes markdown with explicit context",
request: OpenResourceRequest{
Kind: "vault-file",
Path: "Notes/Overview.md",
Context: OpenResourceContext{
IsInsideNotesFolder: true,
NotesMode: true,
},
},
want: ContextNotesMarkdown,
},
{
name: "notes markdown auto-detected from path",
request: OpenResourceRequest{
Kind: "vault-file",
Path: "Workspace/Notes/MyNote.md",
},
want: ContextNotesMarkdown,
},
{
name: "notes overview auto-detected",
request: OpenResourceRequest{
Kind: "vault-file",
Path: "Notes/Overview.md",
},
want: ContextNotesMarkdown,
},
{
name: "non-notes markdown stays generic",
request: OpenResourceRequest{
Kind: "vault-file",
Path: "Workspace/Files/readme.md",
},
want: ContextGenericMarkdown,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := DetermineContextName(tt.request); got != tt.want {
t.Fatalf("DetermineContextName = %q, want %q", got, tt.want)
}
})
}
}