@qwickapps/server 1.1.6 → 1.1.9

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.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/dist/core/control-panel.d.ts.map +1 -1
  3. package/dist/core/control-panel.js +5 -8
  4. package/dist/core/control-panel.js.map +1 -1
  5. package/dist/core/gateway.d.ts +5 -0
  6. package/dist/core/gateway.d.ts.map +1 -1
  7. package/dist/core/gateway.js +390 -28
  8. package/dist/core/gateway.js.map +1 -1
  9. package/dist/core/health-manager.d.ts.map +1 -1
  10. package/dist/core/health-manager.js +3 -9
  11. package/dist/core/health-manager.js.map +1 -1
  12. package/dist/core/logging.d.ts.map +1 -1
  13. package/dist/core/logging.js +2 -6
  14. package/dist/core/logging.js.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +7 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/plugins/cache-plugin.d.ts +219 -0
  20. package/dist/plugins/cache-plugin.d.ts.map +1 -0
  21. package/dist/plugins/cache-plugin.js +326 -0
  22. package/dist/plugins/cache-plugin.js.map +1 -0
  23. package/dist/plugins/cache-plugin.test.d.ts +8 -0
  24. package/dist/plugins/cache-plugin.test.d.ts.map +1 -0
  25. package/dist/plugins/cache-plugin.test.js +188 -0
  26. package/dist/plugins/cache-plugin.test.js.map +1 -0
  27. package/dist/plugins/config-plugin.js +1 -1
  28. package/dist/plugins/config-plugin.js.map +1 -1
  29. package/dist/plugins/diagnostics-plugin.js +1 -1
  30. package/dist/plugins/diagnostics-plugin.js.map +1 -1
  31. package/dist/plugins/health-plugin.js +1 -1
  32. package/dist/plugins/health-plugin.js.map +1 -1
  33. package/dist/plugins/index.d.ts +6 -0
  34. package/dist/plugins/index.d.ts.map +1 -1
  35. package/dist/plugins/index.js +4 -0
  36. package/dist/plugins/index.js.map +1 -1
  37. package/dist/plugins/logs-plugin.d.ts.map +1 -1
  38. package/dist/plugins/logs-plugin.js +1 -3
  39. package/dist/plugins/logs-plugin.js.map +1 -1
  40. package/dist/plugins/postgres-plugin.d.ts +155 -0
  41. package/dist/plugins/postgres-plugin.d.ts.map +1 -0
  42. package/dist/plugins/postgres-plugin.js +244 -0
  43. package/dist/plugins/postgres-plugin.js.map +1 -0
  44. package/dist/plugins/postgres-plugin.test.d.ts +8 -0
  45. package/dist/plugins/postgres-plugin.test.d.ts.map +1 -0
  46. package/dist/plugins/postgres-plugin.test.js +165 -0
  47. package/dist/plugins/postgres-plugin.test.js.map +1 -0
  48. package/dist-ui/assets/{index-Bk7ypbI4.js → index-CW1BviRn.js} +2 -2
  49. package/dist-ui/assets/{index-Bk7ypbI4.js.map → index-CW1BviRn.js.map} +1 -1
  50. package/dist-ui/index.html +1 -1
  51. package/package.json +13 -2
  52. package/src/core/control-panel.ts +5 -8
  53. package/src/core/gateway.ts +412 -30
  54. package/src/core/health-manager.ts +3 -9
  55. package/src/core/logging.ts +2 -6
  56. package/src/index.ts +22 -0
  57. package/src/plugins/cache-plugin.test.ts +241 -0
  58. package/src/plugins/cache-plugin.ts +503 -0
  59. package/src/plugins/config-plugin.ts +1 -1
  60. package/src/plugins/diagnostics-plugin.ts +1 -1
  61. package/src/plugins/health-plugin.ts +1 -1
  62. package/src/plugins/index.ts +10 -0
  63. package/src/plugins/logs-plugin.ts +1 -3
  64. package/src/plugins/postgres-plugin.test.ts +213 -0
  65. package/src/plugins/postgres-plugin.ts +345 -0
  66. package/ui/src/api/controlPanelApi.ts +1 -1
  67. package/ui/src/pages/LogsPage.tsx +6 -10
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Cache Plugin Tests
3
+ *
4
+ * Note: These tests use mocks since we don't want to require a real Redis instance.
5
+ * Integration tests should be run separately with a real Redis instance.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9
+
10
+ // Mock ioredis before importing the plugin
11
+ vi.mock('ioredis', () => {
12
+ const mockClient = {
13
+ get: vi.fn().mockResolvedValue(null),
14
+ setex: vi.fn().mockResolvedValue('OK'),
15
+ del: vi.fn().mockResolvedValue(1),
16
+ exists: vi.fn().mockResolvedValue(1),
17
+ expire: vi.fn().mockResolvedValue(1),
18
+ ttl: vi.fn().mockResolvedValue(3600),
19
+ incr: vi.fn().mockResolvedValue(1),
20
+ incrby: vi.fn().mockResolvedValue(5),
21
+ keys: vi.fn().mockResolvedValue([]),
22
+ info: vi.fn().mockResolvedValue('used_memory_human:1.5M\n'),
23
+ dbsize: vi.fn().mockResolvedValue(100),
24
+ ping: vi.fn().mockResolvedValue('PONG'),
25
+ quit: vi.fn().mockResolvedValue('OK'),
26
+ on: vi.fn(),
27
+ status: 'ready',
28
+ };
29
+
30
+ return {
31
+ default: vi.fn(() => mockClient),
32
+ };
33
+ });
34
+
35
+ import {
36
+ createCachePlugin,
37
+ getCache,
38
+ hasCache,
39
+ type CachePluginConfig,
40
+ } from './cache-plugin.js';
41
+
42
+ describe('Cache Plugin', () => {
43
+ const mockConfig: CachePluginConfig = {
44
+ url: 'redis://localhost:6379',
45
+ keyPrefix: 'test:',
46
+ defaultTtl: 3600,
47
+ healthCheck: false, // Disable for unit tests
48
+ };
49
+
50
+ const mockContext = {
51
+ config: { productName: 'Test', port: 3000 },
52
+ app: {} as any,
53
+ router: {} as any,
54
+ logger: {
55
+ debug: vi.fn(),
56
+ info: vi.fn(),
57
+ warn: vi.fn(),
58
+ error: vi.fn(),
59
+ },
60
+ registerHealthCheck: vi.fn(),
61
+ };
62
+
63
+ beforeEach(() => {
64
+ vi.clearAllMocks();
65
+ });
66
+
67
+ afterEach(async () => {
68
+ // Clean up any registered instances
69
+ if (hasCache('test')) {
70
+ const cache = getCache('test');
71
+ await cache.close();
72
+ }
73
+ });
74
+
75
+ describe('createCachePlugin', () => {
76
+ it('should create a plugin with correct name', () => {
77
+ const plugin = createCachePlugin(mockConfig, 'test');
78
+ expect(plugin.name).toBe('cache:test');
79
+ });
80
+
81
+ it('should use "default" as instance name when not specified', () => {
82
+ const plugin = createCachePlugin(mockConfig);
83
+ expect(plugin.name).toBe('cache:default');
84
+ });
85
+
86
+ it('should have low order number (initialize early)', () => {
87
+ const plugin = createCachePlugin(mockConfig);
88
+ expect(plugin.order).toBeLessThan(10);
89
+ });
90
+ });
91
+
92
+ describe('onInit', () => {
93
+ it('should register the cache instance', async () => {
94
+ const plugin = createCachePlugin(mockConfig, 'test');
95
+ await plugin.onInit?.(mockContext as any);
96
+
97
+ expect(hasCache('test')).toBe(true);
98
+ });
99
+
100
+ it('should log debug message on successful connection', async () => {
101
+ const plugin = createCachePlugin(mockConfig, 'test');
102
+ await plugin.onInit?.(mockContext as any);
103
+
104
+ expect(mockContext.logger.debug).toHaveBeenCalledWith(
105
+ expect.stringContaining('connected')
106
+ );
107
+ });
108
+
109
+ it('should register health check when enabled', async () => {
110
+ const configWithHealth = { ...mockConfig, healthCheck: true };
111
+ const plugin = createCachePlugin(configWithHealth, 'test');
112
+ await plugin.onInit?.(mockContext as any);
113
+
114
+ expect(mockContext.registerHealthCheck).toHaveBeenCalledWith(
115
+ expect.objectContaining({
116
+ name: 'redis',
117
+ type: 'custom',
118
+ })
119
+ );
120
+ });
121
+
122
+ it('should use custom health check name when provided', async () => {
123
+ const configWithCustomName = {
124
+ ...mockConfig,
125
+ healthCheck: true,
126
+ healthCheckName: 'custom-cache',
127
+ };
128
+ const plugin = createCachePlugin(configWithCustomName, 'test');
129
+ await plugin.onInit?.(mockContext as any);
130
+
131
+ expect(mockContext.registerHealthCheck).toHaveBeenCalledWith(
132
+ expect.objectContaining({
133
+ name: 'custom-cache',
134
+ })
135
+ );
136
+ });
137
+ });
138
+
139
+ describe('getCache', () => {
140
+ it('should return registered instance', async () => {
141
+ const plugin = createCachePlugin(mockConfig, 'test');
142
+ await plugin.onInit?.(mockContext as any);
143
+
144
+ const cache = getCache('test');
145
+ expect(cache).toBeDefined();
146
+ expect(cache.get).toBeDefined();
147
+ expect(cache.set).toBeDefined();
148
+ expect(cache.delete).toBeDefined();
149
+ });
150
+
151
+ it('should throw error for unregistered instance', () => {
152
+ expect(() => getCache('nonexistent')).toThrow(
153
+ 'Cache instance "nonexistent" not found'
154
+ );
155
+ });
156
+ });
157
+
158
+ describe('hasCache', () => {
159
+ it('should return false for unregistered instance', () => {
160
+ expect(hasCache('nonexistent')).toBe(false);
161
+ });
162
+
163
+ it('should return true for registered instance', async () => {
164
+ const plugin = createCachePlugin(mockConfig, 'test');
165
+ await plugin.onInit?.(mockContext as any);
166
+
167
+ expect(hasCache('test')).toBe(true);
168
+ });
169
+ });
170
+
171
+ describe('CacheInstance', () => {
172
+ it('should get value and parse JSON', async () => {
173
+ const plugin = createCachePlugin(mockConfig, 'test');
174
+ await plugin.onInit?.(mockContext as any);
175
+
176
+ const cache = getCache('test');
177
+ // Mock will return null by default
178
+ const result = await cache.get('key');
179
+ expect(result).toBeNull();
180
+ });
181
+
182
+ it('should set value with JSON stringification', async () => {
183
+ const plugin = createCachePlugin(mockConfig, 'test');
184
+ await plugin.onInit?.(mockContext as any);
185
+
186
+ const cache = getCache('test');
187
+ await cache.set('key', { foo: 'bar' }, 3600);
188
+ // Just verify it doesn't throw
189
+ });
190
+
191
+ it('should return cache stats', async () => {
192
+ const plugin = createCachePlugin(mockConfig, 'test');
193
+ await plugin.onInit?.(mockContext as any);
194
+
195
+ const cache = getCache('test');
196
+ const stats = await cache.getStats();
197
+ expect(stats).toHaveProperty('connected');
198
+ expect(stats).toHaveProperty('keyCount');
199
+ });
200
+
201
+ it('should check if key exists', async () => {
202
+ const plugin = createCachePlugin(mockConfig, 'test');
203
+ await plugin.onInit?.(mockContext as any);
204
+
205
+ const cache = getCache('test');
206
+ const exists = await cache.exists('key');
207
+ expect(typeof exists).toBe('boolean');
208
+ });
209
+
210
+ it('should get TTL for a key', async () => {
211
+ const plugin = createCachePlugin(mockConfig, 'test');
212
+ await plugin.onInit?.(mockContext as any);
213
+
214
+ const cache = getCache('test');
215
+ const ttl = await cache.ttl('key');
216
+ expect(typeof ttl).toBe('number');
217
+ });
218
+
219
+ it('should increment a value', async () => {
220
+ const plugin = createCachePlugin(mockConfig, 'test');
221
+ await plugin.onInit?.(mockContext as any);
222
+
223
+ const cache = getCache('test');
224
+ const value = await cache.incr('counter');
225
+ expect(typeof value).toBe('number');
226
+ });
227
+ });
228
+
229
+ describe('onShutdown', () => {
230
+ it('should close client and unregister instance', async () => {
231
+ const plugin = createCachePlugin(mockConfig, 'test');
232
+ await plugin.onInit?.(mockContext as any);
233
+
234
+ expect(hasCache('test')).toBe(true);
235
+
236
+ await plugin.onShutdown?.();
237
+
238
+ expect(hasCache('test')).toBe(false);
239
+ });
240
+ });
241
+ });