@kya-os/mcp-i-core 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,177 +0,0 @@
1
- /**
2
- * Cache Invalidation: No Warming Test
3
- *
4
- * CRITICAL TEST: Proves that clearAndRefresh does NOT fetch immediately after clearing.
5
- *
6
- * Background:
7
- * - In mcp-i-cloudflare@1.6.4+, cache warming was added as an "optimization"
8
- * - The idea was to fetch fresh data immediately after clearing
9
- * - BUT this backfired when upstream APIs (AgentShield) have CDN caching
10
- * - The immediate fetch would get stale CDN data and store it in our cache
11
- * - This defeated the purpose of cache invalidation
12
- *
13
- * Fix:
14
- * - clearAndRefresh now just DELETES the cache entry
15
- * - It does NOT fetch or warm the cache
16
- * - The next actual tool call will fetch fresh data (with natural delay)
17
- *
18
- * This was the behavior in mcp-i-cloudflare@1.6.3 that worked perfectly.
19
- */
20
-
21
- import { describe, it, expect, vi, beforeEach } from "vitest";
22
- import { ToolProtectionService } from "../../services/tool-protection.service.js";
23
- import { InMemoryToolProtectionCache } from "../../cache/tool-protection-cache.js";
24
-
25
- describe("Cache Invalidation - No Warming", () => {
26
- let cache: InMemoryToolProtectionCache;
27
- let service: ToolProtectionService;
28
- let fetchSpy: ReturnType<typeof vi.fn>;
29
-
30
- beforeEach(() => {
31
- cache = new InMemoryToolProtectionCache();
32
- fetchSpy = vi.fn();
33
-
34
- service = new ToolProtectionService(
35
- {
36
- apiUrl: "https://test.agentshield.com",
37
- apiKey: "test-key",
38
- projectId: "test-project",
39
- cacheTtl: 300000,
40
- debug: true,
41
- },
42
- cache
43
- );
44
-
45
- // Spy on the internal fetch method
46
- (service as any).fetchFromApi = fetchSpy;
47
- });
48
-
49
- it("clearAndRefresh should DELETE cache without fetching", async () => {
50
- // Seed the cache with old data
51
- const oldConfig = {
52
- toolProtections: {
53
- greet: { requiresDelegation: false, requiredScopes: ["greet:execute"] },
54
- },
55
- };
56
- await cache.set("config:tool-protections:test-project", oldConfig, 300000);
57
-
58
- // Clear the cache
59
- const result = await service.clearAndRefresh("did:key:test-agent");
60
-
61
- // CRITICAL: fetchFromApi should NOT have been called
62
- expect(fetchSpy).not.toHaveBeenCalled();
63
-
64
- // Result should indicate cache was cleared, not refreshed
65
- expect(result.source).toBe("cleared");
66
- expect(result.cacheKey).toBe("config:tool-protections:test-project");
67
- expect(result.config.toolProtections).toEqual({});
68
- });
69
-
70
- it("cache should be empty after clearAndRefresh (no warming)", async () => {
71
- // Seed the cache
72
- const oldConfig = {
73
- toolProtections: {
74
- greet: { requiresDelegation: true, requiredScopes: ["greet:execute"] },
75
- },
76
- };
77
- await cache.set("config:tool-protections:test-project", oldConfig, 300000);
78
-
79
- // Verify it's cached
80
- const beforeClear = await cache.get("config:tool-protections:test-project");
81
- expect(beforeClear).not.toBeNull();
82
- expect(beforeClear?.toolProtections.greet.requiresDelegation).toBe(true);
83
-
84
- // Clear the cache
85
- await service.clearAndRefresh("did:key:test-agent");
86
-
87
- // Cache should be EMPTY (not warmed with new data)
88
- const afterClear = await cache.get("config:tool-protections:test-project");
89
- expect(afterClear).toBeNull();
90
- });
91
-
92
- it("next getToolProtectionConfig call should fetch fresh from API", async () => {
93
- // Setup: Mock API to return DIFFERENT data than what was cached
94
- const staleConfig = {
95
- toolProtections: {
96
- greet: { requiresDelegation: false, requiredScopes: [] },
97
- },
98
- };
99
-
100
- // Seed cache with stale data
101
- await cache.set(
102
- "config:tool-protections:test-project",
103
- staleConfig,
104
- 300000
105
- );
106
-
107
- // Mock API to return fresh data (requiresDelegation: TRUE)
108
- fetchSpy.mockResolvedValueOnce({
109
- data: {
110
- toolProtections: {
111
- greet: {
112
- requiresDelegation: true,
113
- requiredScopes: ["greet:execute"],
114
- },
115
- },
116
- },
117
- });
118
-
119
- // Clear cache (should NOT fetch)
120
- await service.clearAndRefresh("did:key:test-agent");
121
- expect(fetchSpy).not.toHaveBeenCalled();
122
-
123
- // Now get config - this SHOULD fetch from API
124
- const result = await service.getToolProtectionConfig("did:key:test-agent");
125
-
126
- // API should have been called now (cache was cleared, so it fetched)
127
- expect(fetchSpy).toHaveBeenCalledTimes(1);
128
-
129
- // Result should have fresh data from API (not stale cached data)
130
- expect(result.toolProtections.greet.requiresDelegation).toBe(true);
131
- });
132
-
133
- it("proves stale CDN data issue is avoided", async () => {
134
- /**
135
- * This test proves why NO warming is better than warming.
136
- *
137
- * Scenario:
138
- * 1. User toggles delegation ON in dashboard
139
- * 2. AgentShield DB updates, but CDN still has old data (5 min TTL)
140
- * 3. User triggers cache clear
141
- *
142
- * WITH warming (broken):
143
- * - clearAndRefresh immediately fetches from API
144
- * - API returns stale CDN data (requiresDelegation: false)
145
- * - We store stale data in our cache
146
- * - Tool call uses stale data = BUG
147
- *
148
- * WITHOUT warming (fixed):
149
- * - clearAndRefresh just deletes cache
150
- * - Next tool call fetches from API (some time later)
151
- * - Better chance CDN has refreshed
152
- * - Even if CDN still stale, user can retry after CDN TTL
153
- */
154
-
155
- // Simulate stale CDN response (old config before user toggled delegation)
156
- const staleCdnResponse = {
157
- data: {
158
- toolProtections: {
159
- greet: { requiresDelegation: false, requiredScopes: [] },
160
- },
161
- },
162
- };
163
-
164
- fetchSpy.mockResolvedValue(staleCdnResponse);
165
-
166
- // Clear cache
167
- await service.clearAndRefresh("did:key:test-agent");
168
-
169
- // CRITICAL: We did NOT store the stale CDN data
170
- // fetchSpy was NOT called during clearAndRefresh
171
- expect(fetchSpy).not.toHaveBeenCalled();
172
-
173
- // Cache is empty, not poisoned with stale data
174
- const cached = await cache.get("config:tool-protections:test-project");
175
- expect(cached).toBeNull();
176
- });
177
- });