@aj-archipelago/cortex 1.3.62 → 1.3.63
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.
- package/.github/workflows/cortex-file-handler-test.yml +61 -0
- package/README.md +31 -7
- package/config/default.example.json +15 -0
- package/config.js +133 -12
- package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
- package/helper-apps/cortex-autogen2/Dockerfile +31 -0
- package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
- package/helper-apps/cortex-autogen2/README.md +183 -0
- package/helper-apps/cortex-autogen2/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/agents.py +131 -0
- package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
- package/helper-apps/cortex-autogen2/function_app.py +55 -0
- package/helper-apps/cortex-autogen2/host.json +15 -0
- package/helper-apps/cortex-autogen2/main.py +126 -0
- package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
- package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
- package/helper-apps/cortex-autogen2/requirements.txt +20 -0
- package/helper-apps/cortex-autogen2/send_task.py +105 -0
- package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
- package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
- package/helper-apps/cortex-autogen2/task_processor.py +488 -0
- package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
- package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
- package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
- package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
- package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
- package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
- package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
- package/helper-apps/cortex-azure-cleaner/README.md +36 -0
- package/helper-apps/cortex-file-converter/README.md +93 -0
- package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
- package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
- package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
- package/helper-apps/cortex-file-converter/requirements.txt +1 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
- package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
- package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
- package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
- package/helper-apps/cortex-file-handler/package.json +1 -1
- package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
- package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
- package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
- package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
- package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
- package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
- package/helper-apps/cortex-file-handler/src/constants.js +3 -0
- package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
- package/helper-apps/cortex-file-handler/src/index.js +116 -9
- package/helper-apps/cortex-file-handler/src/redis.js +61 -1
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
- package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
- package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
- package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
- package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
- package/helper-apps/cortex-file-handler/src/start.js +27 -17
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
- package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
- package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
- package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
- package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
- package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
- package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
- package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
- package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
- package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
- package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
- package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
- package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
- package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
- package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
- package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
- package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
- package/lib/cortexResponse.js +153 -0
- package/lib/entityConstants.js +21 -3
- package/lib/logger.js +21 -4
- package/lib/pathwayTools.js +28 -9
- package/lib/util.js +49 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +1 -0
- package/pathways/bing_afagent.js +54 -1
- package/pathways/call_tools.js +2 -3
- package/pathways/chat_jarvis.js +1 -1
- package/pathways/google_cse.js +27 -0
- package/pathways/grok_live_search.js +18 -0
- package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
- package/pathways/system/entity/memory/sys_memory_required.js +1 -0
- package/pathways/system/entity/memory/sys_search_memory.js +1 -0
- package/pathways/system/entity/sys_entity_agent.js +56 -4
- package/pathways/system/entity/sys_generator_quick.js +1 -0
- package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
- package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
- package/pathways/system/entity/tools/sys_tool_image.js +1 -1
- package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
- package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
- package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
- package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
- package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
- package/pathways/vision.js +1 -1
- package/server/graphql.js +1 -1
- package/server/modelExecutor.js +8 -0
- package/server/pathwayResolver.js +166 -16
- package/server/pathwayResponseParser.js +16 -8
- package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
- package/server/plugins/claude3VertexPlugin.js +193 -45
- package/server/plugins/gemini15ChatPlugin.js +21 -0
- package/server/plugins/gemini15VisionPlugin.js +360 -0
- package/server/plugins/googleCsePlugin.js +94 -0
- package/server/plugins/grokVisionPlugin.js +365 -0
- package/server/plugins/modelPlugin.js +3 -1
- package/server/plugins/openAiChatPlugin.js +106 -13
- package/server/plugins/openAiVisionPlugin.js +42 -30
- package/server/resolver.js +28 -4
- package/server/rest.js +270 -53
- package/server/typeDef.js +1 -0
- package/tests/{mocks.js → helpers/mocks.js} +5 -2
- package/tests/{server.js → helpers/server.js} +2 -2
- package/tests/helpers/sseAssert.js +23 -0
- package/tests/helpers/sseClient.js +73 -0
- package/tests/helpers/subscriptionAssert.js +11 -0
- package/tests/helpers/subscriptions.js +113 -0
- package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
- package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
- package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
- package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
- package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
- package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
- package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
- package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
- package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
- package/tests/integration/graphql/features/grok/grok.test.js +688 -0
- package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
- package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
- package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
- package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
- package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
- package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
- package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
- package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
- package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
- package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
- package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
- package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
- package/tests/{config.test.js → unit/core/config.test.js} +3 -3
- package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
- package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
- package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
- package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
- package/tests/unit/core/mergeResolver.test.js +952 -0
- package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
- package/tests/unit/core/pathwayResolver.test.js +187 -0
- package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
- package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
- package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
- package/tests/{util.test.js → unit/core/util.test.js} +1 -1
- package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
- package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
- package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
- package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
- package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
- package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
- package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
- package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
- package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
- package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
- package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
- package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
- package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
- package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
- package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
- package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
- package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
- package/pathways/system/workspaces/run_gpt4.js +0 -20
- package/pathways/system/workspaces/run_gpt4_32.js +0 -20
- package/tests/agentic.test.js +0 -256
- package/tests/pathwayResolver.test.js +0 -78
- package/tests/subscription.test.js +0 -387
- /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
- /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import FormData from 'form-data';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import XLSX from 'xlsx';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import {
|
|
11
|
+
startTestServer,
|
|
12
|
+
stopTestServer,
|
|
13
|
+
cleanupHashAndFile
|
|
14
|
+
} from './testUtils.helper.js';
|
|
15
|
+
import { port } from '../src/start.js';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
const baseUrl = `http://localhost:${port}/api/CortexFileHandler`;
|
|
20
|
+
|
|
21
|
+
// Helper function to extract file extension from URL
|
|
22
|
+
function getFileExtensionFromUrl(url) {
|
|
23
|
+
try {
|
|
24
|
+
const urlObj = new URL(url);
|
|
25
|
+
const pathname = urlObj.pathname;
|
|
26
|
+
const lastDotIndex = pathname.lastIndexOf('.');
|
|
27
|
+
if (lastDotIndex !== -1 && lastDotIndex < pathname.length - 1) {
|
|
28
|
+
return pathname.substring(lastDotIndex);
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.log("Error parsing extension from URL:", error);
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Helper function to upload files
|
|
37
|
+
async function uploadFile(filePath, requestId = null, hash = null) {
|
|
38
|
+
const form = new FormData();
|
|
39
|
+
form.append('file', fs.createReadStream(filePath));
|
|
40
|
+
|
|
41
|
+
if (requestId) {
|
|
42
|
+
form.append('requestId', requestId);
|
|
43
|
+
}
|
|
44
|
+
if (hash) {
|
|
45
|
+
form.append('hash', hash);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const response = await axios.post(baseUrl, form, {
|
|
49
|
+
headers: form.getHeaders(),
|
|
50
|
+
validateStatus: (status) => true,
|
|
51
|
+
timeout: 60000,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Test setup
|
|
58
|
+
test.before(async (t) => {
|
|
59
|
+
await startTestServer();
|
|
60
|
+
// Create a test directory for temporary files
|
|
61
|
+
t.context.testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-shortlived-conversion-'));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test.after(async (t) => {
|
|
65
|
+
// Clean up test directory
|
|
66
|
+
if (t.context.testDir && fs.existsSync(t.context.testDir)) {
|
|
67
|
+
fs.rmSync(t.context.testDir, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
await stopTestServer();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Test that short-lived URLs are generated from converted files, not original files
|
|
74
|
+
*/
|
|
75
|
+
test.serial(
|
|
76
|
+
"checkHash should generate shortLivedUrl from converted file URL for Excel files",
|
|
77
|
+
async (t) => {
|
|
78
|
+
// Create a minimal XLSX workbook in-memory
|
|
79
|
+
const workbook = XLSX.utils.book_new();
|
|
80
|
+
const worksheet = XLSX.utils.aoa_to_sheet([
|
|
81
|
+
["Product", "Price", "Quantity"],
|
|
82
|
+
["Apple", 1.50, 100],
|
|
83
|
+
["Banana", 0.75, 200],
|
|
84
|
+
["Cherry", 2.00, 50],
|
|
85
|
+
]);
|
|
86
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, "Products");
|
|
87
|
+
|
|
88
|
+
// Write it to a temp file
|
|
89
|
+
const filePath = path.join(t.context.testDir, `${uuidv4()}.xlsx`);
|
|
90
|
+
XLSX.writeFile(workbook, filePath);
|
|
91
|
+
|
|
92
|
+
const hash = `test-shortlived-conversion-${uuidv4()}`;
|
|
93
|
+
let uploadedUrl;
|
|
94
|
+
let convertedUrl;
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// 1. Upload the XLSX file with hash (conversion should run automatically)
|
|
98
|
+
const uploadResponse = await uploadFile(filePath, null, hash);
|
|
99
|
+
t.is(uploadResponse.status, 200, "Upload should succeed");
|
|
100
|
+
t.truthy(uploadResponse.data.converted, "Upload response must contain converted info");
|
|
101
|
+
t.truthy(uploadResponse.data.converted.url, "Converted URL should be present");
|
|
102
|
+
|
|
103
|
+
uploadedUrl = uploadResponse.data.url;
|
|
104
|
+
convertedUrl = uploadResponse.data.converted.url;
|
|
105
|
+
|
|
106
|
+
// Verify the original file is .xlsx and converted file is .csv
|
|
107
|
+
t.is(getFileExtensionFromUrl(uploadedUrl), '.xlsx', "Original URL should point to .xlsx file");
|
|
108
|
+
t.is(getFileExtensionFromUrl(convertedUrl), '.csv', "Converted URL should point to .csv file");
|
|
109
|
+
|
|
110
|
+
// 2. Give Redis a moment to persist
|
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
112
|
+
|
|
113
|
+
// 3. Check hash to get short-lived URL
|
|
114
|
+
const checkResponse = await axios.get(baseUrl, {
|
|
115
|
+
params: { hash, checkHash: true },
|
|
116
|
+
validateStatus: (status) => true,
|
|
117
|
+
timeout: 30000,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
t.is(checkResponse.status, 200, "checkHash should succeed");
|
|
121
|
+
t.truthy(checkResponse.data.shortLivedUrl, "Response should include shortLivedUrl");
|
|
122
|
+
t.truthy(checkResponse.data.converted, "Response should include converted info");
|
|
123
|
+
|
|
124
|
+
// 4. CRITICAL TEST: Verify short-lived URL is based on converted file, not original
|
|
125
|
+
const shortLivedUrlBase = checkResponse.data.shortLivedUrl.split('?')[0]; // Remove query params
|
|
126
|
+
const convertedUrlBase = checkResponse.data.converted.url.split('?')[0]; // Remove query params
|
|
127
|
+
const originalUrlBase = checkResponse.data.url.split('?')[0]; // Remove query params
|
|
128
|
+
|
|
129
|
+
// The short-lived URL should be based on the converted file URL
|
|
130
|
+
t.is(
|
|
131
|
+
shortLivedUrlBase,
|
|
132
|
+
convertedUrlBase,
|
|
133
|
+
"Short-lived URL should be based on converted file URL (.csv), not original file URL (.xlsx)"
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Double-check: short-lived URL should NOT be based on original file
|
|
137
|
+
t.not(
|
|
138
|
+
shortLivedUrlBase,
|
|
139
|
+
originalUrlBase,
|
|
140
|
+
"Short-lived URL should NOT be based on original file URL (.xlsx)"
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Verify the short-lived URL points to a CSV file, not Excel
|
|
144
|
+
t.true(
|
|
145
|
+
checkResponse.data.shortLivedUrl.includes('.csv'),
|
|
146
|
+
"Short-lived URL should point to .csv file (converted), not .xlsx file (original)"
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
t.false(
|
|
150
|
+
checkResponse.data.shortLivedUrl.includes('.xlsx'),
|
|
151
|
+
"Short-lived URL should NOT contain .xlsx extension"
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
} finally {
|
|
155
|
+
// Clean up
|
|
156
|
+
fs.unlinkSync(filePath);
|
|
157
|
+
await cleanupHashAndFile(hash, uploadedUrl, baseUrl);
|
|
158
|
+
if (convertedUrl) {
|
|
159
|
+
await cleanupHashAndFile(null, convertedUrl, baseUrl);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Test that short-lived URLs fallback to original files when no conversion exists
|
|
167
|
+
*/
|
|
168
|
+
test.serial(
|
|
169
|
+
"checkHash should generate shortLivedUrl from original file URL when no conversion exists",
|
|
170
|
+
async (t) => {
|
|
171
|
+
// Create a simple text file (no conversion needed)
|
|
172
|
+
const testContent = "This is a simple text file that doesn't need conversion.";
|
|
173
|
+
const filePath = path.join(t.context.testDir, `${uuidv4()}.txt`);
|
|
174
|
+
fs.writeFileSync(filePath, testContent);
|
|
175
|
+
|
|
176
|
+
const hash = `test-shortlived-original-${uuidv4()}`;
|
|
177
|
+
let uploadedUrl;
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// 1. Upload the text file with hash (no conversion should occur)
|
|
181
|
+
const uploadResponse = await uploadFile(filePath, null, hash);
|
|
182
|
+
t.is(uploadResponse.status, 200, "Upload should succeed");
|
|
183
|
+
t.falsy(uploadResponse.data.converted, "Upload response should NOT contain converted info for .txt files");
|
|
184
|
+
|
|
185
|
+
uploadedUrl = uploadResponse.data.url;
|
|
186
|
+
t.is(getFileExtensionFromUrl(uploadedUrl), '.txt', "Original URL should point to .txt file");
|
|
187
|
+
|
|
188
|
+
// 2. Give Redis a moment to persist
|
|
189
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
190
|
+
|
|
191
|
+
// 3. Check hash to get short-lived URL
|
|
192
|
+
const checkResponse = await axios.get(baseUrl, {
|
|
193
|
+
params: { hash, checkHash: true },
|
|
194
|
+
validateStatus: (status) => true,
|
|
195
|
+
timeout: 30000,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
t.is(checkResponse.status, 200, "checkHash should succeed");
|
|
199
|
+
t.truthy(checkResponse.data.shortLivedUrl, "Response should include shortLivedUrl");
|
|
200
|
+
t.falsy(checkResponse.data.converted, "Response should NOT include converted info for .txt files");
|
|
201
|
+
|
|
202
|
+
// 4. CRITICAL TEST: Verify short-lived URL is based on original file when no conversion exists
|
|
203
|
+
const shortLivedUrlBase = checkResponse.data.shortLivedUrl.split('?')[0]; // Remove query params
|
|
204
|
+
const originalUrlBase = checkResponse.data.url.split('?')[0]; // Remove query params
|
|
205
|
+
|
|
206
|
+
// The short-lived URL should be based on the original file URL
|
|
207
|
+
t.is(
|
|
208
|
+
shortLivedUrlBase,
|
|
209
|
+
originalUrlBase,
|
|
210
|
+
"Short-lived URL should be based on original file URL when no conversion exists"
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Verify the short-lived URL points to the text file
|
|
214
|
+
t.true(
|
|
215
|
+
checkResponse.data.shortLivedUrl.includes('.txt'),
|
|
216
|
+
"Short-lived URL should point to .txt file (original)"
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
} finally {
|
|
220
|
+
// Clean up
|
|
221
|
+
fs.unlinkSync(filePath);
|
|
222
|
+
await cleanupHashAndFile(hash, uploadedUrl, baseUrl);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
);
|
|
@@ -15,6 +15,8 @@ import { port, publicFolder, ipAddress } from "../src/start.js";
|
|
|
15
15
|
import {
|
|
16
16
|
cleanupHashAndFile,
|
|
17
17
|
getFolderNameFromUrl,
|
|
18
|
+
startTestServer,
|
|
19
|
+
stopTestServer,
|
|
18
20
|
} from "./testUtils.helper.js";
|
|
19
21
|
|
|
20
22
|
// Add these helper functions at the top after imports
|
|
@@ -103,18 +105,12 @@ async function uploadFile(file, requestId, hash = null) {
|
|
|
103
105
|
|
|
104
106
|
// Ensure server is ready before tests
|
|
105
107
|
test.before(async (t) => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
await startTestServer();
|
|
109
|
+
});
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} catch (error) {
|
|
113
|
-
// 404 is fine, it means server is running but directory is empty
|
|
114
|
-
if (error.response?.status !== 404) {
|
|
115
|
-
throw new Error("Server not ready");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
111
|
+
// Clean up server after tests
|
|
112
|
+
test.after.always(async () => {
|
|
113
|
+
await stopTestServer();
|
|
118
114
|
});
|
|
119
115
|
|
|
120
116
|
// Configuration Tests
|
|
@@ -365,7 +361,7 @@ test.serial("should validate requestId for delete operation", async (t) => {
|
|
|
365
361
|
t.is(response.status, 400, "Should return 400 for missing requestId");
|
|
366
362
|
t.is(
|
|
367
363
|
response.data,
|
|
368
|
-
"Please pass a requestId on the query string",
|
|
364
|
+
"Please pass either a requestId or hash on the query string",
|
|
369
365
|
"Should return proper error message",
|
|
370
366
|
);
|
|
371
367
|
});
|
|
@@ -103,3 +103,83 @@ test("should return null for invalid gcs credentials", (t) => {
|
|
|
103
103
|
// Cleanup
|
|
104
104
|
delete process.env.GCP_SERVICE_ACCOUNT_KEY;
|
|
105
105
|
});
|
|
106
|
+
|
|
107
|
+
// Container-specific tests
|
|
108
|
+
test("should get azure provider with default container when no container specified", async (t) => {
|
|
109
|
+
if (!process.env.AZURE_STORAGE_CONNECTION_STRING) {
|
|
110
|
+
t.pass("Skipping test - Azure not configured");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const factory = new StorageFactory();
|
|
115
|
+
const provider = await factory.getAzureProvider();
|
|
116
|
+
t.truthy(provider);
|
|
117
|
+
t.truthy(provider.containerName);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("should get azure provider with specific container name", async (t) => {
|
|
121
|
+
if (!process.env.AZURE_STORAGE_CONNECTION_STRING) {
|
|
122
|
+
t.pass("Skipping test - Azure not configured");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const factory = new StorageFactory();
|
|
127
|
+
|
|
128
|
+
// Mock the blobHandler constants for this test
|
|
129
|
+
const mockConstants = {
|
|
130
|
+
AZURE_STORAGE_CONTAINER_NAMES: ["container1", "container2", "container3"],
|
|
131
|
+
DEFAULT_AZURE_STORAGE_CONTAINER_NAME: "container1",
|
|
132
|
+
isValidContainerName: (name) => ["container1", "container2", "container3"].includes(name)
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Test with valid container name
|
|
136
|
+
const provider = await factory.getAzureProvider("container2");
|
|
137
|
+
t.truthy(provider);
|
|
138
|
+
t.is(provider.containerName, "container2");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("should throw error for invalid container name", async (t) => {
|
|
142
|
+
if (!process.env.AZURE_STORAGE_CONNECTION_STRING) {
|
|
143
|
+
t.pass("Skipping test - Azure not configured");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const factory = new StorageFactory();
|
|
148
|
+
|
|
149
|
+
// Test with invalid container name
|
|
150
|
+
await t.throwsAsync(
|
|
151
|
+
() => factory.getAzureProvider("invalid-container"),
|
|
152
|
+
{ message: /Invalid container name/ }
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("should cache providers by container name", async (t) => {
|
|
157
|
+
if (!process.env.AZURE_STORAGE_CONNECTION_STRING) {
|
|
158
|
+
t.pass("Skipping test - Azure not configured");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const factory = new StorageFactory();
|
|
163
|
+
|
|
164
|
+
// Mock valid container names for testing
|
|
165
|
+
const originalEnv = process.env.AZURE_STORAGE_CONTAINER_NAME;
|
|
166
|
+
process.env.AZURE_STORAGE_CONTAINER_NAME = "test1,test2,test3";
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const provider1 = await factory.getAzureProvider("test1");
|
|
170
|
+
const provider2 = await factory.getAzureProvider("test1");
|
|
171
|
+
const provider3 = await factory.getAzureProvider("test2");
|
|
172
|
+
|
|
173
|
+
// Same container should return same instance
|
|
174
|
+
t.is(provider1, provider2);
|
|
175
|
+
// Different container should return different instance
|
|
176
|
+
t.not(provider1, provider3);
|
|
177
|
+
} finally {
|
|
178
|
+
// Restore original env
|
|
179
|
+
if (originalEnv) {
|
|
180
|
+
process.env.AZURE_STORAGE_CONTAINER_NAME = originalEnv;
|
|
181
|
+
} else {
|
|
182
|
+
delete process.env.AZURE_STORAGE_CONTAINER_NAME;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|