| 1 | package extension |
| 2 | |
| 3 | import ( |
| 4 | "testing" |
| 5 | |
| 6 | "reasonix/internal/provider" |
| 7 | ) |
| 8 | |
| 9 | func TestWithLiveContributionsPreservesCacheHash(t *testing.T) { |
| 10 | base := &RuntimeSnapshot{ |
| 11 | generation: 1, |
| 12 | systemPrompt: "sys", |
| 13 | toolSchemas: []provider.ToolSchema{{Name: "bash"}}, |
| 14 | cacheHash: "hash-stable", |
| 15 | systemHash: "sys-h", |
| 16 | toolsHash: "tools-h", |
| 17 | interceptorChain: map[InterceptorPoint][]Contribution{ |
| 18 | PointToolBefore: {{Kind: KindInterceptor, ID: string(PointToolBefore), Source: ContributionSource{Scope: ScopePlugin, PluginID: "old"}}}, |
| 19 | }, |
| 20 | catalog: NewCatalog(), |
| 21 | } |
| 22 | base.catalog.Add(Contribution{Kind: KindTool, ID: "bash", Source: ContributionSource{Scope: ScopeBuiltin}}) |
| 23 | base.catalog.freeze() |
| 24 | |
| 25 | live := []Contribution{ |
| 26 | {Kind: KindInterceptor, ID: string(PointToolBefore), Priority: 1, Source: ContributionSource{Scope: ScopePlugin, PluginID: "new"}, Payload: "x"}, |
| 27 | {Kind: KindProvider, ID: "p/m", Source: ContributionSource{Scope: ScopePlugin, PluginID: "new"}, Payload: provider.Descriptor{Ref: "p/m"}}, |
| 28 | } |
| 29 | next := base.WithLiveContributions(9, live) |
| 30 | if next.Generation() != 9 { |
| 31 | t.Fatalf("gen = %d", next.Generation()) |
| 32 | } |
| 33 | if next.CacheHash() != "hash-stable" { |
| 34 | t.Fatal("CacheHash must not churn on live contribution refresh") |
| 35 | } |
| 36 | if next.SystemPrompt() != "sys" { |
| 37 | t.Fatal("system prompt moved") |
| 38 | } |
| 39 | chain := next.InterceptorChain()[PointToolBefore] |
| 40 | if len(chain) != 1 || chain[0].Source.PluginID != "new" { |
| 41 | t.Fatalf("chain = %+v", chain) |
| 42 | } |
| 43 | // Empty live must clear plugin interceptors (plugin removed). |
| 44 | cleared := base.WithLiveContributions(10, nil) |
| 45 | if len(cleared.InterceptorChain()[PointToolBefore]) != 0 { |
| 46 | t.Fatal("empty live must drop plugin interceptors") |
| 47 | } |
| 48 | // Builtin tool must still be present. |
| 49 | found := false |
| 50 | for _, ct := range next.Catalog().ByKind(KindTool) { |
| 51 | if ct.ID == "bash" { |
| 52 | found = true |
| 53 | } |
| 54 | } |
| 55 | if !found { |
| 56 | t.Fatal("expected host tool retained") |
| 57 | } |
| 58 | } |
| 59 |