Skip to content

Commit 446ba69

Browse files
akoclaude
andcommitted
fix: resolveSnippetRef now checks session cache before querying backend (#509)
Snippets created earlier in the same script were not visible to pages created later in the same execution session: resolveSnippetRef always queried the backend, which doesn't see uncommitted documents. Added the same cache-first lookup that resolveMicroflow and resolvePageRef already use: check ctx.Cache.createdSnippets before falling through to the backend ListSnippets call. This makes forward snippet references within a single script work regardless of declaration order. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b385a57 commit 446ba69

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

mdl/executor/cmd_pages_builder_input.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ func (pb *pageBuilder) resolveSnippetRef(snippetRef string) (model.ID, error) {
8383
snippetName = snippetRef
8484
}
8585

86+
// First, check if the snippet was created during this session
87+
// (not yet visible via reader)
88+
if pb.execCache != nil && pb.execCache.createdSnippets != nil {
89+
if info, ok := pb.execCache.createdSnippets[snippetRef]; ok {
90+
return info.ID, nil
91+
}
92+
if moduleName != "" {
93+
if info, ok := pb.execCache.createdSnippets[moduleName+"."+snippetName]; ok {
94+
return info.ID, nil
95+
}
96+
}
97+
}
98+
8699
snippets, err := pb.backend.ListSnippets()
87100
if err != nil {
88101
return "", err
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/backend/mock"
9+
"github.com/mendixlabs/mxcli/model"
10+
"github.com/mendixlabs/mxcli/sdk/pages"
11+
)
12+
13+
// TestResolveSnippetRef_FromCache verifies that resolveSnippetRef finds snippets
14+
// that were created earlier in the same session (before the backend sees them).
15+
// Regression test for issue #509.
16+
func TestResolveSnippetRef_FromCache(t *testing.T) {
17+
mod := mkModule("MyModule")
18+
19+
mb := &mock.MockBackend{
20+
IsConnectedFunc: func() bool { return true },
21+
// Backend returns nothing — snippet only exists in session cache
22+
ListSnippetsFunc: func() ([]*pages.Snippet, error) { return nil, nil },
23+
}
24+
25+
snpID := model.ID("snp-session-1")
26+
cache := &executorCache{
27+
createdSnippets: map[string]*createdSnippetInfo{
28+
"MyModule.NavMenu": {
29+
ID: snpID,
30+
Name: "NavMenu",
31+
ModuleName: "MyModule",
32+
},
33+
},
34+
}
35+
36+
h := mkHierarchy(mod)
37+
withContainer(h, mod.ID, mod.ID)
38+
39+
pb := &pageBuilder{
40+
backend: mb,
41+
execCache: cache,
42+
}
43+
pb.execCache.hierarchy = h
44+
45+
// Should resolve from cache, not from backend
46+
id, err := pb.resolveSnippetRef("MyModule.NavMenu")
47+
if err != nil {
48+
t.Fatalf("resolveSnippetRef returned error: %v", err)
49+
}
50+
if id != snpID {
51+
t.Fatalf("expected ID %q, got %q", snpID, id)
52+
}
53+
}
54+
55+
// TestResolveSnippetRef_NotFoundInCache verifies that when a snippet is absent
56+
// from both cache and backend, a "not found" error is returned.
57+
func TestResolveSnippetRef_NotFoundInCache(t *testing.T) {
58+
mb := &mock.MockBackend{
59+
IsConnectedFunc: func() bool { return true },
60+
ListSnippetsFunc: func() ([]*pages.Snippet, error) { return nil, nil },
61+
ListModulesFunc: func() ([]*model.Module, error) { return nil, nil },
62+
}
63+
64+
cache := &executorCache{
65+
createdSnippets: map[string]*createdSnippetInfo{},
66+
}
67+
68+
mod := mkModule("MyModule")
69+
h := mkHierarchy(mod)
70+
withContainer(h, mod.ID, mod.ID)
71+
72+
pb := &pageBuilder{
73+
backend: mb,
74+
execCache: cache,
75+
}
76+
pb.execCache.hierarchy = h
77+
78+
_, err := pb.resolveSnippetRef("MyModule.Missing")
79+
if err == nil {
80+
t.Fatal("expected error for missing snippet, got nil")
81+
}
82+
}

0 commit comments

Comments
 (0)