fix: vault/workspace lifecycle — CreateVault creates workspace, SetCurrentVault loads workspace, ReloadPlugins keeps workspace capability, recursive tree rendering
This commit is contained in:
parent
5fa2c0ddf9
commit
67345a194a
Binary file not shown.
|
|
@ -192,6 +192,14 @@ func main() {
|
|||
fmt.Printf(" ✅ registered vault capability\n")
|
||||
}
|
||||
|
||||
// Register workspace capability (core service — always present when vault is open)
|
||||
if err := reg.Register("verstak-desktop", []string{"verstak/core/workspace/v1"}); err != nil {
|
||||
fmt.Printf(" ❌ register workspace capability: %v\n", err)
|
||||
allGood = false
|
||||
} else {
|
||||
fmt.Printf(" ✅ registered workspace capability\n")
|
||||
}
|
||||
|
||||
// Register plugin capabilities
|
||||
for _, p := range m.Provides {
|
||||
if err := reg.Register(m.ID, []string{p}); err != nil {
|
||||
|
|
@ -257,10 +265,10 @@ func main() {
|
|||
fmt.Printf("\n[capability count]\n")
|
||||
totalCaps := len(reg.List())
|
||||
fmt.Printf(" total capabilities: %d\n", totalCaps)
|
||||
if totalCaps >= 8 {
|
||||
fmt.Printf(" ✅ total capabilities >= 8 (%d)\n", totalCaps)
|
||||
if totalCaps >= 9 {
|
||||
fmt.Printf(" ✅ total capabilities >= 9 (%d)\n", totalCaps)
|
||||
} else {
|
||||
fmt.Printf(" ❌ total capabilities < 8 (got %d, expected >= 8)\n", totalCaps)
|
||||
fmt.Printf(" ❌ total capabilities < 9 (got %d, expected >= 9)\n", totalCaps)
|
||||
allGood = false
|
||||
}
|
||||
|
||||
|
|
@ -649,6 +657,50 @@ func runWorkspaceTest(root string) {
|
|||
fmt.Printf(" ✅ workspace.json exists on disk\n")
|
||||
fmt.Printf(" content:\n%s\n", string(wsData))
|
||||
|
||||
// ── Test 4-level deep tree ──
|
||||
fmt.Printf("\n[4-level deep tree]\n")
|
||||
// Create: root → folder1 → folder2 → case (4 levels)
|
||||
folder1, err := ws.CreateNode(rootID, workspace.TypeFolder, "Level 1 Folder")
|
||||
if err != nil {
|
||||
fmt.Printf(" ❌ create folder1: %v\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
fmt.Printf(" ✅ created: %s\n", folder1.Title)
|
||||
|
||||
folder2, err := ws.CreateNode(folder1.ID, workspace.TypeFolder, "Level 2 Folder")
|
||||
if err != nil {
|
||||
fmt.Printf(" ❌ create folder2: %v\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
fmt.Printf(" ✅ created: %s\n", folder2.Title)
|
||||
|
||||
deepCase, err := ws.CreateNode(folder2.ID, workspace.TypeCase, "Deep Case")
|
||||
if err != nil {
|
||||
fmt.Printf(" ❌ create deep case: %v\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
fmt.Printf(" ✅ created: %s (depth 4)\n", deepCase.Title)
|
||||
|
||||
tree = ws.GetTree()
|
||||
if len(tree.Nodes) != 7 { // root + case + folder + nested + folder1 + folder2 + deepCase
|
||||
fmt.Printf(" ❌ expected 7 nodes, got %d\n", len(tree.Nodes))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
fmt.Printf(" ✅ tree has 7 nodes (4 levels deep)\n")
|
||||
|
||||
// Verify deep case parent chain
|
||||
deepNode, _ := ws.GetNode(deepCase.ID)
|
||||
if deepNode.ParentID != folder2.ID {
|
||||
fmt.Printf(" ❌ deep case parent mismatch\n")
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
fmt.Printf(" ✅ deep case parent chain correct\n")
|
||||
|
||||
fmt.Printf("\n=== summary ===\n")
|
||||
fmt.Printf("✅ workspace test passed\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -439,6 +439,7 @@ Workspace — центральная модель Верстака вокруг
|
|||
|
||||
### Lifecycle Events
|
||||
|
||||
**Planned (not yet implemented in runtime):**
|
||||
- `workspace.node.created`
|
||||
- `workspace.node.renamed`
|
||||
- `workspace.node.moved`
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
import { onMount } from 'svelte';
|
||||
import * as App from '../../../wailsjs/go/api/App';
|
||||
|
||||
let nodes = [];
|
||||
let currentNodeId = '';
|
||||
export let nodes = [];
|
||||
export let currentNodeId = '';
|
||||
export let expandedNodes = {};
|
||||
export let depth = 0;
|
||||
|
||||
let loading = true;
|
||||
let error = '';
|
||||
let expandedNodes = {};
|
||||
let localError = '';
|
||||
let showCreate = false;
|
||||
let newNodeTitle = '';
|
||||
let newNodeParentId = '';
|
||||
|
|
@ -14,12 +16,14 @@
|
|||
let creating = false;
|
||||
|
||||
onMount(async () => {
|
||||
await loadTree();
|
||||
if (depth === 0) {
|
||||
await loadTree();
|
||||
}
|
||||
});
|
||||
|
||||
async function loadTree() {
|
||||
loading = true;
|
||||
error = '';
|
||||
localError = '';
|
||||
try {
|
||||
const result = await App.GetWorkspaceTree();
|
||||
if (result.status === 'not initialized') {
|
||||
|
|
@ -32,7 +36,7 @@
|
|||
if (root) expandedNodes[root.id] = true;
|
||||
}
|
||||
} catch (e) {
|
||||
error = String(e);
|
||||
localError = String(e);
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
|
@ -63,7 +67,7 @@
|
|||
|
||||
async function selectNode(id) {
|
||||
const err = await App.SetCurrentWorkspaceNode(id);
|
||||
if (err) { error = err; return; }
|
||||
if (err) { localError = err; return; }
|
||||
currentNodeId = id;
|
||||
}
|
||||
|
||||
|
|
@ -78,12 +82,12 @@
|
|||
if (!newNodeTitle.trim()) return;
|
||||
creating = true;
|
||||
const res = await App.CreateWorkspaceNode(newNodeParentId, newNodeType, newNodeTitle.trim());
|
||||
if (res.error) { error = res.error; creating = false; return; }
|
||||
if (res.error) { localError = res.error; creating = false; return; }
|
||||
showCreate = false;
|
||||
creating = false;
|
||||
await loadTree();
|
||||
expandedNodes[newNodeParentId] = true;
|
||||
expandedNodes = expandedNodes;
|
||||
await loadTree();
|
||||
}
|
||||
|
||||
function cancelCreate() {
|
||||
|
|
@ -92,67 +96,62 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="wt">
|
||||
<div class="wt-header">
|
||||
<span class="wt-title">Workspace</span>
|
||||
<button class="wt-btn" on:click={() => openCreate('', 'space')} type="button">+</button>
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="wt-loading">Loading...</div>
|
||||
{:else if error}
|
||||
<div class="wt-error">{error}</div>
|
||||
{:else}
|
||||
{#each roots() as node (node.id)}
|
||||
<div class="wt-node">
|
||||
<div class="wt-row" class:selected={node.id === currentNodeId}>
|
||||
{#if hasKids(node.id)}
|
||||
<button class="wt-expand" on:click={() => toggle(node.id)} type="button">{expandedNodes[node.id] ? '\u25BE' : '\u25B8'}</button>
|
||||
{:else}
|
||||
<span class="wt-expand-spacer"></span>
|
||||
{/if}
|
||||
<span class="wt-icon">{icon(node.type)}</span>
|
||||
<button class="wt-label" on:click={() => selectNode(node.id)} type="button">{node.title}</button>
|
||||
<button class="wt-btn wt-btn-small" on:click={() => openCreate(node.id, 'case')} type="button">+</button>
|
||||
</div>
|
||||
{#if expandedNodes[node.id]}
|
||||
{#each childrenOf(node.id) as child (child.id)}
|
||||
<div class="wt-node wt-child">
|
||||
<div class="wt-row" class:selected={child.id === currentNodeId}>
|
||||
{#if hasKids(child.id)}
|
||||
<button class="wt-expand" on:click={() => toggle(child.id)} type="button">{expandedNodes[child.id] ? '\u25BE' : '\u25B8'}</button>
|
||||
{:else}
|
||||
<span class="wt-expand-spacer"></span>
|
||||
{/if}
|
||||
<span class="wt-icon">{icon(child.type)}</span>
|
||||
<button class="wt-label" on:click={() => selectNode(child.id)} type="button">{child.title}</button>
|
||||
<button class="wt-btn wt-btn-small" on:click={() => openCreate(child.id, 'folder')} type="button">+</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if showCreate}
|
||||
<div class="wt-create">
|
||||
<div class="wt-create-header">
|
||||
<span>New {newNodeType}</span>
|
||||
<button class="wt-btn" on:click={cancelCreate} type="button">{'\u2715'}</button>
|
||||
</div>
|
||||
<input type="text" bind:value={newNodeTitle} placeholder="Name..." disabled={creating} />
|
||||
<div class="wt-create-actions">
|
||||
<button class="wt-btn-primary" on:click={doCreate} type="button" disabled={creating || !newNodeTitle.trim()}>{creating ? '...' : 'Create'}</button>
|
||||
<button class="wt-btn" on:click={cancelCreate} type="button" disabled={creating}>Cancel</button>
|
||||
</div>
|
||||
{#if depth === 0}
|
||||
<div class="wt">
|
||||
<div class="wt-header">
|
||||
<span class="wt-title">Workspace</span>
|
||||
<button class="wt-btn" on:click={() => openCreate('', 'space')} title="New Space" type="button">+</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="wt-loading">Loading...</div>
|
||||
{:else if localError}
|
||||
<div class="wt-error">{localError}</div>
|
||||
{:else}
|
||||
{#each roots() as node (node.id)}
|
||||
<svelte:self {node} {nodes} {currentNodeId} {expandedNodes} depth={1} {icon} {toggle} {hasKids} {selectNode} {openCreate} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if showCreate}
|
||||
<div class="wt-create">
|
||||
<div class="wt-create-header">
|
||||
<span>New {newNodeType}</span>
|
||||
<button class="wt-btn" on:click={cancelCreate} type="button">\u2715</button>
|
||||
</div>
|
||||
<input type="text" bind:value={newNodeTitle} placeholder="Name..." disabled={creating} />
|
||||
<div class="wt-create-actions">
|
||||
<button class="wt-btn-primary" on:click={doCreate} type="button" disabled={creating || !newNodeTitle.trim()}>{creating ? '...' : 'Create'}</button>
|
||||
<button class="wt-btn" on:click={cancelCreate} type="button" disabled={creating}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="wt-node" class:selected={node.id === currentNodeId} class:archived={node.status === 'archived'} class:sleeping={node.status === 'sleeping'}>
|
||||
<div class="wt-row" style="padding-left: {depth * 1.0 + 0.4}rem;">
|
||||
{#if hasKids(node.id)}
|
||||
<button class="wt-expand" on:click={() => toggle(node.id)} type="button">{expandedNodes[node.id] ? '\u25BE' : '\u25B8'}</button>
|
||||
{:else}
|
||||
<span class="wt-expand-spacer"></span>
|
||||
{/if}
|
||||
<span class="wt-icon">{icon(node.type)}</span>
|
||||
<button class="wt-label" on:click={() => selectNode(node.id)} type="button">{node.title}</button>
|
||||
{#if node.type !== 'case'}
|
||||
<button class="wt-btn wt-btn-small" on:click={() => openCreate(node.id, 'case')} title="Add child" type="button">+</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if expandedNodes[node.id]}
|
||||
{#each childrenOf(node.id) as child (child.id)}
|
||||
<svelte:self node={child} {nodes} {currentNodeId} {expandedNodes} depth={depth + 1} {icon} {toggle} {hasKids} {selectNode} {openCreate} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.wt { display: flex; flex-direction: column; flex: 1; overflow: hidden; position: relative; }
|
||||
.wt-header { display: flex; align-items: center; justify-content: space-between; padding: 0.4rem 0.6rem; border-bottom: 1px solid #0f3460; }
|
||||
.wt-header { display: flex; align-items: center; justify-content: space-between; padding: 0.4rem 0.6rem; border-bottom: 1px solid #0f3460; flex-shrink: 0; }
|
||||
.wt-title { color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
|
||||
.wt-btn { background: none; border: none; color: #666; cursor: pointer; font-size: 0.85rem; padding: 0.1rem 0.3rem; border-radius: 3px; }
|
||||
.wt-btn:hover { color: #4ecca3; background: rgba(78,204,163,0.1); }
|
||||
|
|
@ -161,16 +160,17 @@
|
|||
.wt-loading, .wt-error { padding: 0.5rem; font-size: 0.75rem; color: #666; }
|
||||
.wt-error { color: #e94560; }
|
||||
.wt-node { }
|
||||
.wt-row { display: flex; align-items: center; gap: 0.2rem; padding: 0.2rem 0.4rem; }
|
||||
.wt-row { display: flex; align-items: center; gap: 0.2rem; padding: 0.15rem 0; }
|
||||
.wt-row:hover { background: rgba(15,52,96,0.4); }
|
||||
.wt-row.selected { background: rgba(78,204,163,0.1); }
|
||||
.wt-expand { width: 1rem; height: 1rem; display: flex; align-items: center; justify-content: center; font-size: 0.65rem; color: #666; background: none; border: none; cursor: pointer; padding: 0; }
|
||||
.wt-expand { width: 1rem; height: 1rem; display: flex; align-items: center; justify-content: center; font-size: 0.65rem; color: #666; background: none; border: none; cursor: pointer; padding: 0; flex-shrink: 0; }
|
||||
.wt-expand:hover { color: #e0e0f0; }
|
||||
.wt-expand-spacer { width: 1rem; flex-shrink: 0; }
|
||||
.wt-icon { font-size: 0.8rem; flex-shrink: 0; }
|
||||
.wt-label { flex: 1; background: none; border: none; color: #e0e0f0; font-size: 0.78rem; text-align: left; cursor: pointer; padding: 0.1rem 0.2rem; border-radius: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.wt-label:hover { color: #4ecca3; }
|
||||
.wt-child .wt-row { padding-left: 1.2rem; }
|
||||
.wt-node.archived .wt-label { text-decoration: line-through; opacity: 0.5; }
|
||||
.wt-node.sleeping .wt-label { opacity: 0.6; }
|
||||
.wt-create { position: absolute; bottom: 0; left: 0; right: 0; background: #16213e; border-top: 1px solid #0f3460; padding: 0.6rem; z-index: 10; }
|
||||
.wt-create-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; }
|
||||
.wt-create input { width: 100%; background: #0f3460; border: 1px solid #1a3a5c; color: #e0e0f0; padding: 0.35rem 0.5rem; border-radius: 4px; font-size: 0.8rem; margin-bottom: 0.4rem; box-sizing: border-box; }
|
||||
|
|
|
|||
|
|
@ -159,6 +159,13 @@ func (a *App) ReloadPlugins() (int, string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Re-register workspace capability if workspace is initialized
|
||||
if a.workspace != nil && a.workspace.IsInitialized() {
|
||||
if err := a.capRegistry.Register("verstak-desktop", []string{"verstak/core/workspace/v1"}); err != nil {
|
||||
log.Printf("[api] ReloadPlugins: failed to re-register workspace capability: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
plugins, errs := plugin.DiscoverPlugins(discoveryDirs)
|
||||
|
||||
// Plugin lifecycle: register capabilities + contributions
|
||||
|
|
@ -407,6 +414,7 @@ func (a *App) UpdateAppSettings(patch map[string]interface{}) string {
|
|||
}
|
||||
|
||||
// SetCurrentVault sets the current vault path in app settings and re-opens the vault.
|
||||
// Loads workspace and registers vault + workspace capabilities.
|
||||
func (a *App) SetCurrentVault(path string) string {
|
||||
if a.appSettings == nil {
|
||||
return "app settings not initialized"
|
||||
|
|
@ -418,8 +426,9 @@ func (a *App) SetCurrentVault(path string) string {
|
|||
if err := a.vault.OpenVault(path); err != nil {
|
||||
return fmt.Sprintf("failed to open vault: %v", err)
|
||||
}
|
||||
// Save to app settings
|
||||
if err := a.appSettings.SetCurrentVault(path); err != nil {
|
||||
// Save the actual vault path (normalized by OpenVault, includes VerstakVault/)
|
||||
vaultPath := a.vault.GetVaultPath()
|
||||
if err := a.appSettings.SetCurrentVault(vaultPath); err != nil {
|
||||
return fmt.Sprintf("failed to save app settings: %v", err)
|
||||
}
|
||||
// Load plugin state for the vault
|
||||
|
|
@ -428,10 +437,24 @@ func (a *App) SetCurrentVault(path string) string {
|
|||
log.Printf("[api] SetCurrentVault: warning loading plugin state: %v", err)
|
||||
}
|
||||
}
|
||||
// Load workspace for the vault
|
||||
if a.workspace != nil {
|
||||
// Replace workspace manager with one pointing to the new vault
|
||||
a.workspace = workspace.NewManager(vaultPath)
|
||||
if err := a.workspace.Load(); err != nil {
|
||||
log.Printf("[api] SetCurrentVault: warning loading workspace: %v", err)
|
||||
}
|
||||
}
|
||||
// Register vault capability
|
||||
if err := a.capRegistry.Register("verstak-desktop", []string{"verstak/core/vault/v1"}); err != nil {
|
||||
log.Printf("[api] SetCurrentVault: failed to register vault capability: %v", err)
|
||||
}
|
||||
// Register workspace capability
|
||||
if a.workspace != nil && a.workspace.IsInitialized() {
|
||||
if err := a.capRegistry.Register("verstak-desktop", []string{"verstak/core/workspace/v1"}); err != nil {
|
||||
log.Printf("[api] SetCurrentVault: failed to register workspace capability: %v", err)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
|
||||
"github.com/verstak/verstak-desktop/internal/core/events"
|
||||
"github.com/verstak/verstak-desktop/internal/core/workspace"
|
||||
)
|
||||
|
||||
// VaultStatus represents the current state of a vault.
|
||||
|
|
@ -114,6 +115,12 @@ func (v *Vault) CreateVault(path string) error {
|
|||
return fmt.Errorf("failed to write vault.json: %w", err)
|
||||
}
|
||||
|
||||
// Create workspace.json with root node
|
||||
wsMgr := workspace.NewManager(vaultDir)
|
||||
if err := wsMgr.Load(); err != nil {
|
||||
return fmt.Errorf("failed to create workspace: %w", err)
|
||||
}
|
||||
|
||||
v.mu.Lock()
|
||||
v.status = StatusOpen
|
||||
v.path = vaultDir
|
||||
|
|
|
|||
|
|
@ -247,3 +247,113 @@ func TestVaultEvents_Published(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateVault_CreatesWorkspace(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
vaultPath := filepath.Join(dir, "testvault")
|
||||
|
||||
bus := events.NewBus()
|
||||
v := NewVault(bus)
|
||||
|
||||
if err := v.CreateVault(vaultPath); err != nil {
|
||||
t.Fatalf("CreateVault: %v", err)
|
||||
}
|
||||
|
||||
wsPath := filepath.Join(v.GetVaultPath(), ".verstak", "workspace.json")
|
||||
data, err := os.ReadFile(wsPath)
|
||||
if err != nil {
|
||||
t.Fatalf("workspace.json not found: %v", err)
|
||||
}
|
||||
|
||||
var ws struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
Nodes []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status string `json:"status"`
|
||||
ParentID string `json:"parentId"`
|
||||
} `json:"nodes"`
|
||||
CurrentNodeID string `json:"currentNodeId"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &ws); err != nil {
|
||||
t.Fatalf("failed to parse workspace.json: %v", err)
|
||||
}
|
||||
|
||||
if ws.SchemaVersion != 1 {
|
||||
t.Errorf("schemaVersion: got %d, want 1", ws.SchemaVersion)
|
||||
}
|
||||
if len(ws.Nodes) != 1 {
|
||||
t.Fatalf("expected 1 root node, got %d", len(ws.Nodes))
|
||||
}
|
||||
if ws.Nodes[0].Type != "space" {
|
||||
t.Errorf("root type: got %q, want %q", ws.Nodes[0].Type, "space")
|
||||
}
|
||||
if ws.Nodes[0].Title != "My Workspace" {
|
||||
t.Errorf("root title: got %q, want %q", ws.Nodes[0].Title, "My Workspace")
|
||||
}
|
||||
if ws.Nodes[0].Status != "active" {
|
||||
t.Errorf("root status: got %q, want %q", ws.Nodes[0].Status, "active")
|
||||
}
|
||||
if ws.CurrentNodeID != ws.Nodes[0].ID {
|
||||
t.Errorf("currentNodeId should be root node id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenVault_WorkspaceLoads(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
vaultPath := filepath.Join(dir, "testvault")
|
||||
|
||||
bus := events.NewBus()
|
||||
v := NewVault(bus)
|
||||
|
||||
if err := v.CreateVault(vaultPath); err != nil {
|
||||
t.Fatalf("CreateVault: %v", err)
|
||||
}
|
||||
|
||||
v.CloseVault()
|
||||
|
||||
if err := v.OpenVault(vaultPath); err != nil {
|
||||
t.Fatalf("OpenVault: %v", err)
|
||||
}
|
||||
|
||||
wsPath := filepath.Join(v.GetVaultPath(), ".verstak", "workspace.json")
|
||||
data, err := os.ReadFile(wsPath)
|
||||
if err != nil {
|
||||
t.Fatalf("workspace.json not found after reopen: %v", err)
|
||||
}
|
||||
|
||||
var ws struct {
|
||||
Nodes []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
} `json:"nodes"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &ws); err != nil {
|
||||
t.Fatalf("failed to parse workspace.json: %v", err)
|
||||
}
|
||||
if len(ws.Nodes) != 1 {
|
||||
t.Fatalf("expected 1 node after reopen, got %d", len(ws.Nodes))
|
||||
}
|
||||
if ws.Nodes[0].Type != "space" {
|
||||
t.Errorf("root type after reopen: got %q, want %q", ws.Nodes[0].Type, "space")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateVault_VaultPathNormalized(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
vaultPath := filepath.Join(dir, "testvault")
|
||||
|
||||
bus := events.NewBus()
|
||||
v := NewVault(bus)
|
||||
|
||||
if err := v.CreateVault(vaultPath); err != nil {
|
||||
t.Fatalf("CreateVault: %v", err)
|
||||
}
|
||||
|
||||
expectedPath := filepath.Join(vaultPath, "VerstakVault")
|
||||
if v.GetVaultPath() != expectedPath {
|
||||
t.Errorf("GetVaultPath: got %q, want %q", v.GetVaultPath(), expectedPath)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue