@onlineapps/conn-base-storage 1.0.6 → 1.0.8

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 (35) hide show
  1. package/README.md +5 -4
  2. package/package.json +2 -2
  3. package/coverage/base.css +0 -224
  4. package/coverage/block-navigation.js +0 -87
  5. package/coverage/clover.xml +0 -297
  6. package/coverage/coverage-final.json +0 -6
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +0 -131
  9. package/coverage/index.js.html +0 -1579
  10. package/coverage/internal-url-adapter.js.html +0 -604
  11. package/coverage/lcov-report/base.css +0 -224
  12. package/coverage/lcov-report/block-navigation.js +0 -87
  13. package/coverage/lcov-report/config.js.html +0 -244
  14. package/coverage/lcov-report/defaults.js.html +0 -214
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/index.html +0 -176
  17. package/coverage/lcov-report/index.js.html +0 -2608
  18. package/coverage/lcov-report/internal-url-adapter.js.html +0 -559
  19. package/coverage/lcov-report/prettify.css +0 -1
  20. package/coverage/lcov-report/prettify.js +0 -2
  21. package/coverage/lcov-report/sharedUrlAdapter.js.html +0 -856
  22. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  23. package/coverage/lcov-report/sorter.js +0 -210
  24. package/coverage/lcov.info +0 -13
  25. package/coverage/prettify.css +0 -1
  26. package/coverage/prettify.js +0 -2
  27. package/coverage/sort-arrow-sprite.png +0 -0
  28. package/coverage/sorter.js +0 -210
  29. package/tests/component/storage.component.test.js +0 -363
  30. package/tests/integration/setup.js +0 -3
  31. package/tests/integration/storage.integration.test.js +0 -224
  32. package/tests/unit/internal-url-adapter.test.js +0 -210
  33. package/tests/unit/legacy.storage.test.js.bak +0 -614
  34. package/tests/unit/storage.extended.unit.test.js +0 -444
  35. package/tests/unit/storage.unit.test.js +0 -382
@@ -1,363 +0,0 @@
1
- /**
2
- * Component Tests pro StorageConnector
3
- * Pre-integrační testy - testují spolupráci s ostatními connektory
4
- *
5
- * Testovací strategie:
6
- * - Pokud jsou dostupné ostatní connektory (logger, registry), použijí se
7
- * - Pokud nejsou dostupné, použijí se mocky
8
- * - MinIO je vždy mockované
9
- * - Testy MUSÍ VŽDY PROJÍT (100% pass rate)
10
- */
11
-
12
- const StorageConnector = require('../../src/index');
13
- const crypto = require('crypto');
14
-
15
- // Pokus o načtení dalších connectorů
16
- let LoggerConnector;
17
- let RegistryClient;
18
-
19
- try {
20
- LoggerConnector = require('@onlineapps/connector-logger');
21
- } catch (e) {
22
- // Logger není dostupný, použijeme mock
23
- LoggerConnector = class MockLogger {
24
- constructor() {
25
- this.info = jest.fn();
26
- this.error = jest.fn();
27
- this.warn = jest.fn();
28
- this.debug = jest.fn();
29
- }
30
- };
31
- }
32
-
33
- try {
34
- RegistryClient = require('@onlineapps/connector-registry-client');
35
- } catch (e) {
36
- // Registry není dostupný, použijeme mock
37
- RegistryClient = class MockRegistry {
38
- constructor() {
39
- this.register = jest.fn().mockResolvedValue(true);
40
- this.unregister = jest.fn().mockResolvedValue(true);
41
- this.getService = jest.fn().mockResolvedValue(null);
42
- }
43
- };
44
- }
45
-
46
- // Mock MinIO vždy
47
- jest.mock('minio', () => {
48
- return {
49
- Client: jest.fn().mockImplementation(() => ({
50
- bucketExists: jest.fn().mockResolvedValue(true),
51
- makeBucket: jest.fn().mockResolvedValue(),
52
- setBucketPolicy: jest.fn().mockResolvedValue(),
53
- putObject: jest.fn().mockResolvedValue(),
54
- getObject: jest.fn().mockImplementation(() => {
55
- // Default mock stream
56
- return {
57
- [Symbol.asyncIterator]: async function* () {
58
- yield Buffer.from('test content');
59
- }
60
- };
61
- }),
62
- statObject: jest.fn().mockResolvedValue({
63
- size: 100,
64
- metaData: {
65
- 'x-amz-meta-fingerprint': 'mockfingerprint',
66
- 'x-amz-meta-uploaded-at': new Date().toISOString()
67
- }
68
- }),
69
- removeObject: jest.fn().mockResolvedValue(),
70
- removeBucket: jest.fn().mockResolvedValue(),
71
- listObjectsV2: jest.fn().mockReturnValue({
72
- on: jest.fn((event, callback) => {
73
- if (event === 'data') {
74
- callback({ name: 'file1.txt', size: 100, lastModified: new Date() });
75
- } else if (event === 'end') {
76
- callback();
77
- }
78
- return { on: jest.fn() };
79
- })
80
- }),
81
- presignedGetObject: jest.fn().mockResolvedValue('http://mock-url'),
82
- presignedGetUrl: jest.fn().mockResolvedValue('http://mock-url'),
83
- useSSL: false,
84
- port: 9000,
85
- host: 'localhost'
86
- }))
87
- };
88
- });
89
-
90
- describe('StorageConnector Component Tests @component', () => {
91
- let storage;
92
- let logger;
93
- let registry;
94
-
95
- beforeEach(() => {
96
- jest.clearAllMocks();
97
-
98
- // Inicializace s loggerem
99
- logger = new LoggerConnector();
100
- registry = new RegistryClient();
101
-
102
- storage = new StorageConnector({
103
- logger: logger,
104
- registry: registry
105
- });
106
- });
107
-
108
- describe('Integration with Logger', () => {
109
- test('should log operations when logger is provided', async () => {
110
- await storage.initialize();
111
-
112
- // Zkontrolujeme, že logger byl použit
113
- if (logger.info && typeof logger.info === 'function') {
114
- // Logger je dostupný nebo mockovaný
115
- expect(storage.logger).toBeDefined();
116
- }
117
- });
118
-
119
- test('should work without logger', async () => {
120
- const storageNoLogger = new StorageConnector();
121
- await storageNoLogger.initialize();
122
-
123
- expect(storageNoLogger.initialized).toBe(true);
124
- });
125
- });
126
-
127
- describe('Integration with Registry', () => {
128
- test('should register service when registry is provided', async () => {
129
- if (registry.register && typeof registry.register === 'function') {
130
- await storage.initialize();
131
-
132
- // Storage by se měl zaregistrovat
133
- // Toto závisí na implementaci
134
- }
135
- });
136
-
137
- test('should work without registry', async () => {
138
- const storageNoRegistry = new StorageConnector();
139
- await storageNoRegistry.initialize();
140
-
141
- expect(storageNoRegistry.initialized).toBe(true);
142
- });
143
- });
144
-
145
- describe('Storage Operations with Components', () => {
146
- test('should upload and log operation', async () => {
147
- await storage.initialize();
148
-
149
- const result = await storage.uploadWithFingerprint(
150
- 'test-bucket',
151
- 'test content',
152
- 'test-prefix'
153
- );
154
-
155
- expect(result.fingerprint).toBeDefined();
156
- expect(result.bucket).toBe('test-bucket');
157
-
158
- // Logger je interní winston logger, ne náš mock
159
- // Zkontrolujeme jen že operace proběhla
160
- expect(result.existed).toBeDefined();
161
- });
162
-
163
- test('should download and verify with logging', async () => {
164
- await storage.initialize();
165
-
166
- const mockClient = storage.minioClient;
167
- const content = 'verified content';
168
- const fingerprint = storage.generateFingerprint(content);
169
-
170
- // Nastavit mock pro správný obsah
171
- mockClient.getObject.mockReturnValue({
172
- [Symbol.asyncIterator]: async function* () {
173
- yield Buffer.from(content);
174
- }
175
- });
176
-
177
- const result = await storage.downloadWithVerification(
178
- 'test-bucket',
179
- 'test-path',
180
- fingerprint
181
- );
182
-
183
- expect(result).toBe(content);
184
- });
185
-
186
- test('should handle errors and log them', async () => {
187
- const mockClient = storage.minioClient;
188
- mockClient.bucketExists.mockRejectedValue(new Error('Test error'));
189
-
190
- await expect(storage.initialize()).rejects.toThrow('Test error');
191
-
192
- // Logger je interní winston logger
193
- // Zkontrolujeme jen že error byl vyhozen
194
- expect(storage.initialized).toBe(false);
195
- });
196
- });
197
-
198
- describe('Cache Coordination', () => {
199
- test('should coordinate cache between multiple instances', () => {
200
- const storage1 = new StorageConnector({ cacheMaxSize: 5 });
201
- const storage2 = new StorageConnector({ cacheMaxSize: 5 });
202
-
203
- // Každá instance má svou vlastní cache
204
- storage1.addToCache('key1', 'value1');
205
- storage2.addToCache('key2', 'value2');
206
-
207
- expect(storage1.getFromCache('key1')).toBe('value1');
208
- expect(storage1.getFromCache('key2')).toBeUndefined();
209
-
210
- expect(storage2.getFromCache('key2')).toBe('value2');
211
- expect(storage2.getFromCache('key1')).toBeUndefined();
212
- });
213
- });
214
-
215
- describe('Fingerprint Validation Across Components', () => {
216
- test('should generate consistent fingerprints', () => {
217
- const storage1 = new StorageConnector();
218
- const storage2 = new StorageConnector();
219
-
220
- const content = 'shared content';
221
- const fp1 = storage1.generateFingerprint(content);
222
- const fp2 = storage2.generateFingerprint(content);
223
-
224
- expect(fp1).toBe(fp2);
225
- });
226
-
227
- test('should validate fingerprints from other sources', async () => {
228
- await storage.initialize();
229
-
230
- // Simulace fingerprintu z jiného zdroje
231
- const externalContent = 'external content';
232
- const externalFingerprint = crypto.createHash('sha256')
233
- .update(externalContent)
234
- .digest('hex');
235
-
236
- // Náš connector by měl generovat stejný fingerprint
237
- const ourFingerprint = storage.generateFingerprint(externalContent);
238
-
239
- expect(ourFingerprint).toBe(externalFingerprint);
240
- });
241
- });
242
-
243
- describe('Batch Operations', () => {
244
- test('should handle multiple uploads efficiently', async () => {
245
- await storage.initialize();
246
-
247
- const uploads = [];
248
- for (let i = 0; i < 5; i++) {
249
- uploads.push(
250
- storage.uploadWithFingerprint(
251
- 'test-bucket',
252
- `content-${i}`,
253
- 'batch'
254
- )
255
- );
256
- }
257
-
258
- const results = await Promise.all(uploads);
259
-
260
- expect(results).toHaveLength(5);
261
- results.forEach((result, index) => {
262
- expect(result.fingerprint).toBeDefined();
263
- expect(result.bucket).toBe('test-bucket');
264
- });
265
- });
266
-
267
- test('should handle concurrent downloads', async () => {
268
- await storage.initialize();
269
-
270
- const downloads = [];
271
- for (let i = 0; i < 3; i++) {
272
- downloads.push(
273
- storage.downloadWithVerification(
274
- 'test-bucket',
275
- `path-${i}`,
276
- null // Bez verifikace fingerprintu
277
- )
278
- );
279
- }
280
-
281
- const results = await Promise.all(downloads);
282
-
283
- expect(results).toHaveLength(3);
284
- results.forEach(result => {
285
- expect(result).toBeDefined();
286
- });
287
- });
288
- });
289
-
290
- describe('URL Generation with Components', () => {
291
- test('should generate consistent URLs across instances', () => {
292
- const storage1 = new StorageConnector({
293
- endPoint: 'minio.example.com',
294
- port: 443,
295
- useSSL: true
296
- });
297
-
298
- const storage2 = new StorageConnector({
299
- endPoint: 'minio.example.com',
300
- port: 443,
301
- useSSL: true
302
- });
303
-
304
- const url1 = storage1.getInternalUrl('bucket', 'path/file.txt');
305
- const url2 = storage2.getInternalUrl('bucket', 'path/file.txt');
306
-
307
- expect(url1).toBe(url2);
308
- expect(url1).toBe('internal://storage/bucket/path/file.txt');
309
- });
310
-
311
- test('should generate abstract internal URLs', () => {
312
- const configs = [
313
- { useSSL: false, port: 80 },
314
- { useSSL: true, port: 443 },
315
- { useSSL: false, port: 9000 },
316
- { useSSL: true, port: 9443 }
317
- ];
318
-
319
- configs.forEach(config => {
320
- const s = new StorageConnector({
321
- endPoint: 'localhost',
322
- ...config
323
- });
324
-
325
- const url = s.getInternalUrl('bucket', 'file.txt');
326
- // Vždy vrací stejnou abstraktní URL nezávisle na konfiguraci
327
- expect(url).toBe('internal://storage/bucket/file.txt');
328
- });
329
- });
330
- });
331
- });
332
-
333
- // Export pro použití v jiných testech
334
- module.exports = {
335
- MockStorageConnector: class MockStorageConnector {
336
- constructor() {
337
- this.initialized = false;
338
- this.cache = new Map();
339
- }
340
-
341
- async initialize() {
342
- this.initialized = true;
343
- return true;
344
- }
345
-
346
- generateFingerprint(content) {
347
- return 'mock-fingerprint-' + String(content).slice(0, 10);
348
- }
349
-
350
- async uploadWithFingerprint(bucket, content, prefix) {
351
- return {
352
- fingerprint: this.generateFingerprint(content),
353
- bucket,
354
- path: `${prefix}/mock-path`,
355
- existed: false
356
- };
357
- }
358
-
359
- async downloadWithVerification(bucket, path, fingerprint) {
360
- return 'mock-content';
361
- }
362
- }
363
- };
@@ -1,3 +0,0 @@
1
- module.exports = async () => {
2
- console.log('\n📦 Storage Connector Integration Tests\n');
3
- };
@@ -1,224 +0,0 @@
1
- /**
2
- * Integration Tests for StorageConnector
3
- * These tests require MinIO to be running
4
- */
5
-
6
- const StorageConnector = require('../../src/index');
7
- const crypto = require('crypto');
8
-
9
- // Helper to conditionally skip tests
10
- const testIf = (condition) => condition ? test : test.skip;
11
-
12
- // Check if MinIO is available
13
- const checkMinIOConnection = async () => {
14
- const net = require('net');
15
- return new Promise((resolve) => {
16
- const client = new net.Socket();
17
- const timeout = setTimeout(() => {
18
- client.destroy();
19
- resolve(false);
20
- }, 1000);
21
-
22
- client.connect(33025, 'localhost', () => {
23
- clearTimeout(timeout);
24
- client.destroy();
25
- resolve(true);
26
- });
27
-
28
- client.on('error', () => {
29
- clearTimeout(timeout);
30
- resolve(false);
31
- });
32
- });
33
- };
34
-
35
- describe('StorageConnector Integration Tests @integration', () => {
36
- let storage;
37
- let minioAvailable = false;
38
-
39
- beforeAll(async () => {
40
- minioAvailable = await checkMinIOConnection();
41
- if (!minioAvailable) {
42
- console.log('\n⚠️ MinIO is not available at localhost:33025');
43
- console.log(' Integration tests will be skipped.');
44
- console.log(' To run integration tests, start MinIO with:');
45
- console.log(' docker-compose up api_shared_storage\n');
46
- }
47
- });
48
-
49
- beforeEach(() => {
50
- if (minioAvailable) {
51
- storage = new StorageConnector({
52
- endPoint: 'localhost',
53
- port: 33025,
54
- useSSL: false,
55
- accessKey: 'minioadmin',
56
- secretKey: 'minioadmin',
57
- bucketName: 'test-connector-storage'
58
- });
59
- }
60
- });
61
-
62
- afterEach(async () => {
63
- if (minioAvailable && storage && storage.initialized) {
64
- // Clean up test objects but keep the bucket
65
- try {
66
- const objectsStream = storage.minioClient.listObjects(storage.config.bucketName, '', true);
67
- const objects = [];
68
- objectsStream.on('data', obj => objects.push(obj.name));
69
- await new Promise((resolve, reject) => {
70
- objectsStream.on('end', resolve);
71
- objectsStream.on('error', reject);
72
- });
73
-
74
- for (const objectName of objects) {
75
- await storage.minioClient.removeObject(storage.config.bucketName, objectName);
76
- }
77
- } catch (err) {
78
- // Ignore cleanup errors
79
- }
80
- }
81
- });
82
-
83
- describe('MinIO Operations', () => {
84
- testIf(minioAvailable)('should initialize and create bucket', async () => {
85
- await storage.initialize();
86
- expect(storage.initialized).toBe(true);
87
-
88
- const bucketExists = await storage.minioClient.bucketExists(storage.config.bucketName);
89
- expect(bucketExists).toBe(true);
90
- });
91
-
92
- testIf(minioAvailable)('should upload and download with fingerprint verification', async () => {
93
- await storage.initialize();
94
-
95
- const content = 'Test content for integration test';
96
- const objectName = 'test-file.txt';
97
-
98
- // Upload with fingerprint
99
- const uploadResult = await storage.uploadWithFingerprint(objectName, content);
100
- expect(uploadResult.fingerprint).toBeDefined();
101
- expect(uploadResult.objectName).toContain(uploadResult.fingerprint);
102
-
103
- // Download with verification
104
- const downloadResult = await storage.downloadWithVerification(uploadResult.objectName);
105
- expect(downloadResult.valid).toBe(true);
106
- expect(downloadResult.data.toString()).toBe(content);
107
- expect(downloadResult.fingerprint).toBe(uploadResult.fingerprint);
108
- });
109
-
110
- testIf(minioAvailable)('should detect fingerprint mismatch', async () => {
111
- await storage.initialize();
112
-
113
- const content = 'Original content';
114
- const objectName = 'test-file.txt';
115
-
116
- // Upload content
117
- const uploadResult = await storage.uploadWithFingerprint(objectName, content);
118
-
119
- // Manually upload different content with same name pattern
120
- const differentContent = 'Modified content';
121
- const wrongFingerprint = 'wrong123fingerprint456';
122
- const tamperedName = storage.getObjectNameWithFingerprint(objectName, wrongFingerprint);
123
-
124
- await storage.minioClient.putObject(
125
- storage.config.bucketName,
126
- tamperedName,
127
- differentContent
128
- );
129
-
130
- // Try to download and verify
131
- const downloadResult = await storage.downloadWithVerification(tamperedName);
132
- expect(downloadResult.valid).toBe(false);
133
- expect(downloadResult.error).toContain('mismatch');
134
- });
135
-
136
- testIf(minioAvailable)('should handle duplicate uploads (idempotency)', async () => {
137
- await storage.initialize();
138
-
139
- const content = 'Duplicate test content';
140
- const objectName = 'duplicate-file.txt';
141
-
142
- // Upload twice
143
- const result1 = await storage.uploadWithFingerprint(objectName, content);
144
- const result2 = await storage.uploadWithFingerprint(objectName, content);
145
-
146
- // Should have same fingerprint
147
- expect(result1.fingerprint).toBe(result2.fingerprint);
148
- expect(result1.objectName).toBe(result2.objectName);
149
- });
150
-
151
- testIf(minioAvailable)('should list objects with fingerprints', async () => {
152
- await storage.initialize();
153
-
154
- // Upload multiple files
155
- await storage.uploadWithFingerprint('file1.txt', 'Content 1');
156
- await storage.uploadWithFingerprint('file2.txt', 'Content 2');
157
- await storage.uploadWithFingerprint('file3.txt', 'Content 3');
158
-
159
- // List objects
160
- const objects = await storage.listObjectsWithFingerprints();
161
- expect(objects.length).toBeGreaterThanOrEqual(3);
162
-
163
- objects.forEach(obj => {
164
- expect(obj.name).toBeDefined();
165
- expect(obj.fingerprint).toBeDefined();
166
- expect(obj.size).toBeDefined();
167
- expect(obj.lastModified).toBeDefined();
168
- });
169
- });
170
-
171
- testIf(minioAvailable)('should generate presigned URL', async () => {
172
- await storage.initialize();
173
-
174
- const content = 'Content for presigned URL';
175
- const objectName = 'presigned-file.txt';
176
-
177
- // Upload file
178
- const uploadResult = await storage.uploadWithFingerprint(objectName, content);
179
-
180
- // Generate presigned URL
181
- const url = await storage.getPresignedUrl(uploadResult.objectName, 60);
182
- expect(url).toBeDefined();
183
- expect(url).toContain('http');
184
- expect(url).toContain(uploadResult.objectName);
185
- });
186
-
187
- testIf(minioAvailable)('should handle cache correctly', async () => {
188
- await storage.initialize();
189
-
190
- const content = 'Cached content';
191
- const objectName = 'cached-file.txt';
192
-
193
- // Upload
194
- const uploadResult = await storage.uploadWithFingerprint(objectName, content);
195
-
196
- // First download (not cached)
197
- const result1 = await storage.downloadWithVerification(uploadResult.objectName);
198
- expect(result1.fromCache).toBe(false);
199
-
200
- // Second download (should be cached)
201
- const result2 = await storage.downloadWithVerification(uploadResult.objectName);
202
- expect(result2.fromCache).toBe(true);
203
- expect(result2.data.toString()).toBe(content);
204
- });
205
- });
206
-
207
- describe('Error Handling', () => {
208
- testIf(minioAvailable)('should handle non-existent object', async () => {
209
- await storage.initialize();
210
-
211
- await expect(storage.downloadWithVerification('non-existent-file.txt'))
212
- .rejects.toThrow();
213
- });
214
-
215
- testIf(minioAvailable)('should handle invalid credentials', async () => {
216
- const invalidStorage = new StorageConnector({
217
- accessKey: 'invalid',
218
- secretKey: 'invalid'
219
- });
220
-
221
- await expect(invalidStorage.initialize()).rejects.toThrow();
222
- });
223
- });
224
- });