@pioneer-platform/pioneer-cache 1.0.0 โ†’ 1.0.2

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.
@@ -0,0 +1,265 @@
1
+ /*
2
+ CRITICAL: Redis Persistence Diagnostic Test
3
+
4
+ This test validates that Redis writes are ACTUALLY persisting to the Redis server.
5
+ It will catch the bug where writes appear successful but data isn't stored.
6
+ */
7
+
8
+ import { describe, it, expect, beforeAll, afterAll } from 'bun:test';
9
+
10
+ // @ts-ignore
11
+ const TAG = ' | REDIS-PERSISTENCE-TEST | ';
12
+
13
+ describe('Redis Persistence Validation', () => {
14
+ let redis: any;
15
+ let redisModule: any;
16
+
17
+ beforeAll(async () => {
18
+ console.log(TAG, '๐Ÿ” Starting Redis persistence diagnostic...');
19
+
20
+ // Force load REAL Redis, not mock
21
+ delete require.cache[require.resolve('@pioneer-platform/default-redis')];
22
+ const originalEnv = process.env.NODE_ENV;
23
+ delete process.env.NODE_ENV; // Ensure we don't use mock
24
+
25
+ redisModule = require('@pioneer-platform/default-redis');
26
+ redis = redisModule.redis;
27
+
28
+ console.log(TAG, 'โœ… Redis module loaded');
29
+ console.log(TAG, ` NODE_ENV: ${process.env.NODE_ENV || '(not set)'}`);
30
+ console.log(TAG, ` Redis constructor: ${redis.constructor.name}`);
31
+ console.log(TAG, ` Is mock?: ${redis.constructor.name === 'RedisMock' ? 'โš ๏ธ YES - THIS IS THE BUG!' : 'โœ… NO'}`);
32
+
33
+ // Test basic connection
34
+ const ping = await redis.ping();
35
+ console.log(TAG, ` Ping: ${ping}`);
36
+
37
+ // Verify connection details
38
+ const options = redis.options;
39
+ console.log(TAG, ` Host: ${options.host}:${options.port}`);
40
+ });
41
+
42
+ afterAll(async () => {
43
+ // Clean up test keys
44
+ const testKeys = await redis.keys('test:*');
45
+ if (testKeys.length > 0) {
46
+ await redis.del(...testKeys);
47
+ }
48
+
49
+ // Don't disconnect - other tests might be using it
50
+ console.log(TAG, '๐Ÿงน Cleaned up test keys');
51
+ });
52
+
53
+ it('should verify Redis is real not mock', async () => {
54
+ expect(redis.constructor.name).not.toBe('RedisMock');
55
+ expect(redis.constructor.name).toBe('Redis');
56
+ });
57
+
58
+ it('should write and immediately read back the same value', async () => {
59
+ const key = 'test:simple:write';
60
+ const value = JSON.stringify({ test: 'data', timestamp: Date.now() });
61
+
62
+ // Write
63
+ await redis.set(key, value);
64
+ console.log(TAG, `โœ… Wrote key: ${key}`);
65
+
66
+ // Read immediately
67
+ const result = await redis.get(key);
68
+ console.log(TAG, `โœ… Read back: ${result ? 'SUCCESS' : 'โŒ FAILED - NULL'}`);
69
+
70
+ expect(result).toBe(value);
71
+ });
72
+
73
+ it('should persist data with TTL', async () => {
74
+ const key = 'test:ttl:write';
75
+ const value = JSON.stringify({ test: 'ttl-data', timestamp: Date.now() });
76
+ const ttlSeconds = 60;
77
+
78
+ // Write with TTL
79
+ await redis.set(key, value, 'EX', ttlSeconds);
80
+ console.log(TAG, `โœ… Wrote key with ${ttlSeconds}s TTL: ${key}`);
81
+
82
+ // Verify it exists
83
+ const result = await redis.get(key);
84
+ expect(result).toBe(value);
85
+
86
+ // Check TTL is set
87
+ const ttl = await redis.ttl(key);
88
+ console.log(TAG, ` TTL remaining: ${ttl}s`);
89
+ expect(ttl).toBeGreaterThan(0);
90
+ expect(ttl).toBeLessThanOrEqual(ttlSeconds);
91
+ });
92
+
93
+ it('should detect if writes disappear after small delay', async () => {
94
+ const key = 'test:delayed:read';
95
+ const value = JSON.stringify({ test: 'delayed-data', timestamp: Date.now() });
96
+
97
+ // Write
98
+ await redis.set(key, value);
99
+ console.log(TAG, `โœ… Wrote key: ${key}`);
100
+
101
+ // Wait 100ms
102
+ await new Promise(resolve => setTimeout(resolve, 100));
103
+
104
+ // Read after delay
105
+ const result = await redis.get(key);
106
+ console.log(TAG, ` Read after 100ms: ${result ? 'SUCCESS' : 'โŒ FAILED - DATA DISAPPEARED!'}`);
107
+
108
+ expect(result).toBe(value);
109
+ });
110
+
111
+ it('should verify balance_v2 cache format works', async () => {
112
+ const key = 'balance_v2:eip155:1:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';
113
+ const cacheValue = {
114
+ value: {
115
+ caip: 'eip155:1/slip44:60',
116
+ pubkey: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
117
+ balance: '123456789'
118
+ },
119
+ timestamp: Date.now(),
120
+ source: 'test',
121
+ lastUpdated: new Date().toISOString()
122
+ };
123
+
124
+ const ttlSeconds = 300;
125
+
126
+ // Write balance cache
127
+ await redis.set(key, JSON.stringify(cacheValue), 'EX', ttlSeconds);
128
+ console.log(TAG, `โœ… Wrote balance cache: ${key}`);
129
+
130
+ // Read back
131
+ const result = await redis.get(key);
132
+ expect(result).not.toBeNull();
133
+
134
+ const parsed = JSON.parse(result);
135
+ expect(parsed.value.balance).toBe('123456789');
136
+
137
+ // Check TTL
138
+ const ttl = await redis.ttl(key);
139
+ console.log(TAG, ` TTL: ${ttl}s`);
140
+ expect(ttl).toBeGreaterThan(0);
141
+ });
142
+
143
+ it('should detect keys using SCAN command', async () => {
144
+ const keyPrefix = 'test:scan:';
145
+ const numKeys = 5;
146
+
147
+ // Write multiple keys
148
+ for (let i = 0; i < numKeys; i++) {
149
+ await redis.set(`${keyPrefix}${i}`, `value${i}`);
150
+ }
151
+
152
+ // Use SCAN to find them
153
+ let cursor = '0';
154
+ const foundKeys: string[] = [];
155
+
156
+ do {
157
+ const result = await redis.scan(cursor, 'MATCH', `${keyPrefix}*`, 'COUNT', 10);
158
+ cursor = result[0];
159
+ foundKeys.push(...result[1]);
160
+ } while (cursor !== '0');
161
+
162
+ console.log(TAG, ` SCAN found ${foundKeys.length} keys with pattern ${keyPrefix}*`);
163
+ expect(foundKeys.length).toBeGreaterThanOrEqual(numKeys);
164
+ });
165
+
166
+ it('should verify connection string is correct', async () => {
167
+ const options = redis.options;
168
+
169
+ console.log(TAG, '๐Ÿ“‹ Redis Connection Details:');
170
+ console.log(TAG, ` Host: ${options.host}`);
171
+ console.log(TAG, ` Port: ${options.port}`);
172
+ console.log(TAG, ` DB: ${options.db || 0}`);
173
+ console.log(TAG, ` Family: ${options.family || 4}`);
174
+
175
+ expect(options.host).toBe('127.0.0.1');
176
+ expect(options.port).toBe(6379);
177
+ });
178
+
179
+ it('should test actual BalanceCache class persistence', async () => {
180
+ // Import after Redis is loaded
181
+ const { BalanceCache } = require('../src/stores/balance-cache');
182
+ const balanceModule = require('@pioneer-platform/pioneer-balance');
183
+
184
+ const cache = new BalanceCache(redis, balanceModule, {
185
+ logCacheHits: true,
186
+ logCacheMisses: true,
187
+ enableQueue: false // Disable queue for this test
188
+ });
189
+
190
+ const testKey = 'balance_v2:eip155:1:0xTESTADDRESS';
191
+ const testData = {
192
+ caip: 'eip155:1/slip44:60',
193
+ pubkey: '0xTESTADDRESS',
194
+ balance: '999999999'
195
+ };
196
+
197
+ // Write via BalanceCache
198
+ console.log(TAG, ' Writing via BalanceCache.updateCache()...');
199
+ await cache.updateCache(testKey, testData);
200
+
201
+ // Verify it actually wrote
202
+ const result = await redis.get(testKey);
203
+ console.log(TAG, ` Direct Redis read: ${result ? 'SUCCESS' : 'โŒ FAILED'}`);
204
+
205
+ expect(result).not.toBeNull();
206
+
207
+ if (result) {
208
+ const parsed = JSON.parse(result);
209
+ expect(parsed.value.balance).toBe('999999999');
210
+
211
+ // Check TTL is set
212
+ const ttl = await redis.ttl(testKey);
213
+ console.log(TAG, ` TTL: ${ttl}s (expected: 300s)`);
214
+ expect(ttl).toBeGreaterThan(0);
215
+ }
216
+
217
+ // Clean up
218
+ await redis.del(testKey);
219
+ });
220
+
221
+ it('should run comprehensive persistence check', async () => {
222
+ console.log(TAG, '\n๐Ÿ”ฌ COMPREHENSIVE PERSISTENCE CHECK:');
223
+
224
+ const testKey = 'test:comprehensive';
225
+ const testValue = 'persistence-check-' + Date.now();
226
+
227
+ // 1. Write
228
+ console.log(TAG, ' Step 1: Writing value...');
229
+ await redis.set(testKey, testValue);
230
+
231
+ // 2. Confirm with GET
232
+ console.log(TAG, ' Step 2: Reading back immediately...');
233
+ const read1 = await redis.get(testKey);
234
+ console.log(TAG, ` Read: ${read1 === testValue ? 'โœ… MATCH' : 'โŒ MISMATCH'}`);
235
+ expect(read1).toBe(testValue);
236
+
237
+ // 3. Check EXISTS
238
+ console.log(TAG, ' Step 3: Checking EXISTS...');
239
+ const exists = await redis.exists(testKey);
240
+ console.log(TAG, ` EXISTS: ${exists === 1 ? 'โœ… TRUE' : 'โŒ FALSE'}`);
241
+ expect(exists).toBe(1);
242
+
243
+ // 4. Check with KEYS pattern
244
+ console.log(TAG, ' Step 4: Searching with KEYS...');
245
+ const keys = await redis.keys('test:comprehensive');
246
+ console.log(TAG, ` KEYS found: ${keys.length === 1 ? 'โœ… 1' : 'โŒ ' + keys.length}`);
247
+ expect(keys.length).toBe(1);
248
+
249
+ // 5. Wait and re-read
250
+ console.log(TAG, ' Step 5: Waiting 200ms and re-reading...');
251
+ await new Promise(resolve => setTimeout(resolve, 200));
252
+ const read2 = await redis.get(testKey);
253
+ console.log(TAG, ` Read: ${read2 === testValue ? 'โœ… STILL THERE' : 'โŒ DISAPPEARED!'}`);
254
+ expect(read2).toBe(testValue);
255
+
256
+ // 6. DEL and verify
257
+ console.log(TAG, ' Step 6: Deleting and verifying...');
258
+ await redis.del(testKey);
259
+ const read3 = await redis.get(testKey);
260
+ console.log(TAG, ` After DEL: ${read3 === null ? 'โœ… DELETED' : 'โŒ STILL EXISTS!'}`);
261
+ expect(read3).toBeNull();
262
+
263
+ console.log(TAG, 'โœ… All persistence checks PASSED!\n');
264
+ });
265
+ });