re-uploading work

This commit is contained in:
2026-02-04 17:46:30 -06:00
commit 3b14c65998
1388 changed files with 381262 additions and 0 deletions

View File

@@ -0,0 +1,574 @@
"""
T2.13: Node.js Action Execution
Tests that JavaScript actions execute with Node.js runtime, with support for
npm package dependencies and proper isolation.
Test validates:
- npm install runs for pack dependencies
- node_modules created in pack directory
- Action can require packages
- Dependencies isolated per pack
- Worker supports Node.js runtime type
"""
import time
import pytest
from helpers.client import AttuneClient
from helpers.fixtures import unique_ref
from helpers.polling import wait_for_execution_status
def test_nodejs_action_basic(client: AttuneClient, test_pack):
"""
Test basic Node.js action execution.
Flow:
1. Create Node.js action with simple script
2. Execute action
3. Verify execution succeeds
4. Verify Node.js runtime works
"""
print("\n" + "=" * 80)
print("TEST: Node.js Action Execution (T2.13)")
print("=" * 80)
pack_ref = test_pack["ref"]
# ========================================================================
# STEP 1: Create basic Node.js action
# ========================================================================
print("\n[STEP 1] Creating basic Node.js action...")
# Simple Node.js script
nodejs_script = """
const params = process.argv[2] ? JSON.parse(process.argv[2]) : {};
console.log('✓ Node.js action started');
console.log(` Node version: ${process.version}`);
console.log(` Platform: ${process.platform}`);
const result = {
success: true,
message: 'Hello from Node.js',
nodeVersion: process.version,
params: params
};
console.log('✓ Action completed successfully');
console.log(JSON.stringify(result));
process.exit(0);
"""
action = client.create_action(
pack_ref=pack_ref,
data={
"name": f"nodejs_basic_{unique_ref()}",
"description": "Basic Node.js action",
"runner_type": "nodejs",
"entry_point": "action.js",
"enabled": True,
"parameters": {
"message": {"type": "string", "required": False, "default": "Hello"}
},
},
)
action_ref = action["ref"]
print(f"✓ Created Node.js action: {action_ref}")
print(f" Runner: nodejs")
# ========================================================================
# STEP 2: Execute action
# ========================================================================
print("\n[STEP 2] Executing Node.js action...")
execution = client.create_execution(
action_ref=action_ref, parameters={"message": "Test message"}
)
execution_id = execution["id"]
print(f"✓ Execution created: ID={execution_id}")
# ========================================================================
# STEP 3: Wait for completion
# ========================================================================
print("\n[STEP 3] Waiting for execution to complete...")
result = wait_for_execution_status(
client=client,
execution_id=execution_id,
expected_status="succeeded",
timeout=30,
)
print(f"✓ Execution completed: status={result['status']}")
# ========================================================================
# STEP 4: Verify execution details
# ========================================================================
print("\n[STEP 4] Verifying execution details...")
execution_details = client.get_execution(execution_id)
assert execution_details["status"] == "succeeded", (
f"❌ Expected 'succeeded', got '{execution_details['status']}'"
)
print(" ✓ Execution succeeded")
stdout = execution_details.get("stdout", "")
if stdout:
if "Node.js action started" in stdout:
print(" ✓ Node.js runtime executed")
if "Node version:" in stdout:
print(" ✓ Node.js version detected")
if "Action completed successfully" in stdout:
print(" ✓ Action completed successfully")
else:
print(" No stdout available")
# ========================================================================
# FINAL SUMMARY
# ========================================================================
print("\n" + "=" * 80)
print("TEST SUMMARY: Node.js Action Execution")
print("=" * 80)
print(f"✓ Node.js action: {action_ref}")
print(f"✓ Execution: succeeded")
print(f"✓ Node.js runtime: working")
print("\n✅ TEST PASSED: Node.js execution works correctly!")
print("=" * 80 + "\n")
def test_nodejs_action_with_axios(client: AttuneClient, test_pack):
"""
Test Node.js action with npm package dependency (axios).
Flow:
1. Create package.json with axios dependency
2. Create action that requires axios
3. Worker installs npm dependencies
4. Execute action
5. Verify node_modules created
6. Verify action can require packages
"""
print("\n" + "=" * 80)
print("TEST: Node.js Action - With Axios Package")
print("=" * 80)
pack_ref = test_pack["ref"]
# ========================================================================
# STEP 1: Create Node.js action with axios
# ========================================================================
print("\n[STEP 1] Creating Node.js action with axios...")
# Action that uses axios
axios_script = """
const params = process.argv[2] ? JSON.parse(process.argv[2]) : {};
try {
const axios = require('axios');
console.log('✓ Successfully imported axios library');
console.log(` axios version: ${axios.VERSION || 'unknown'}`);
// Make HTTP request
axios.get('https://httpbin.org/get', { timeout: 5000 })
.then(response => {
console.log(`✓ HTTP request successful: status=${response.status}`);
const result = {
success: true,
library: 'axios',
statusCode: response.status
};
console.log(JSON.stringify(result));
process.exit(0);
})
.catch(error => {
console.error(`✗ HTTP request failed: ${error.message}`);
process.exit(1);
});
} catch (error) {
console.error(`✗ Failed to import axios: ${error.message}`);
console.error(' (Dependencies may not be installed yet)');
process.exit(1);
}
"""
action = client.create_action(
pack_ref=pack_ref,
data={
"name": f"nodejs_axios_{unique_ref()}",
"description": "Node.js action with axios dependency",
"runner_type": "nodejs",
"entry_point": "http_action.js",
"enabled": True,
"parameters": {},
"metadata": {"npm_dependencies": {"axios": "^1.6.0"}},
},
)
action_ref = action["ref"]
print(f"✓ Created Node.js action: {action_ref}")
print(f" Dependencies: axios ^1.6.0")
# ========================================================================
# STEP 2: Execute action
# ========================================================================
print("\n[STEP 2] Executing action...")
print(" Note: First execution may take longer (installing dependencies)")
execution = client.create_execution(action_ref=action_ref, parameters={})
execution_id = execution["id"]
print(f"✓ Execution created: ID={execution_id}")
# ========================================================================
# STEP 3: Wait for completion
# ========================================================================
print("\n[STEP 3] Waiting for execution to complete...")
# First execution may take longer due to npm install
result = wait_for_execution_status(
client=client,
execution_id=execution_id,
expected_status="succeeded",
timeout=60, # Longer timeout for npm install
)
print(f"✓ Execution completed: status={result['status']}")
# ========================================================================
# STEP 4: Verify execution details
# ========================================================================
print("\n[STEP 4] Verifying execution details...")
execution_details = client.get_execution(execution_id)
assert execution_details["status"] == "succeeded", (
f"❌ Expected 'succeeded', got '{execution_details['status']}'"
)
print(" ✓ Execution succeeded")
stdout = execution_details.get("stdout", "")
if stdout:
if "Successfully imported axios" in stdout:
print(" ✓ axios library imported successfully")
if "axios version:" in stdout:
print(" ✓ axios version detected")
if "HTTP request successful" in stdout:
print(" ✓ HTTP request executed successfully")
else:
print(" No stdout available")
# ========================================================================
# STEP 5: Execute again to test caching
# ========================================================================
print("\n[STEP 5] Executing again to test node_modules caching...")
execution2 = client.create_execution(action_ref=action_ref, parameters={})
execution2_id = execution2["id"]
print(f"✓ Second execution created: ID={execution2_id}")
start_time = time.time()
result2 = wait_for_execution_status(
client=client,
execution_id=execution2_id,
expected_status="succeeded",
timeout=30,
)
end_time = time.time()
second_exec_time = end_time - start_time
print(f"✓ Second execution completed: status={result2['status']}")
print(
f" Time: {second_exec_time:.1f}s (should be faster with cached node_modules)"
)
# ========================================================================
# STEP 6: Validate success criteria
# ========================================================================
print("\n[STEP 6] Validating success criteria...")
assert result["status"] == "succeeded", "❌ First execution should succeed"
assert result2["status"] == "succeeded", "❌ Second execution should succeed"
print(" ✓ Both executions succeeded")
if "Successfully imported axios" in stdout:
print(" ✓ Action imported npm package")
else:
print(" Import verification not available in output")
if second_exec_time < 10:
print(f" ✓ Second execution fast: {second_exec_time:.1f}s (cached)")
else:
print(f" Second execution time: {second_exec_time:.1f}s")
# ========================================================================
# FINAL SUMMARY
# ========================================================================
print("\n" + "=" * 80)
print("TEST SUMMARY: Node.js Action with Axios")
print("=" * 80)
print(f"✓ Action with npm dependencies: {action_ref}")
print(f"✓ Dependency: axios ^1.6.0")
print(f"✓ First execution: succeeded")
print(f"✓ Second execution: succeeded (cached)")
print(f"✓ Package import: successful")
print(f"✓ HTTP request: successful")
print("\n✅ TEST PASSED: Node.js with npm dependencies works!")
print("=" * 80 + "\n")
def test_nodejs_action_multiple_packages(client: AttuneClient, test_pack):
"""
Test Node.js action with multiple npm packages.
Flow:
1. Create action with multiple npm dependencies
2. Verify all packages can be required
3. Verify action uses multiple packages
"""
print("\n" + "=" * 80)
print("TEST: Node.js Action - Multiple Packages")
print("=" * 80)
pack_ref = test_pack["ref"]
# ========================================================================
# STEP 1: Create action with multiple dependencies
# ========================================================================
print("\n[STEP 1] Creating action with multiple npm packages...")
multi_pkg_script = """
const params = process.argv[2] ? JSON.parse(process.argv[2]) : {};
try {
const axios = require('axios');
const lodash = require('lodash');
console.log('✓ All packages imported successfully');
console.log(` - axios: available`);
console.log(` - lodash: ${lodash.VERSION}`);
// Use both packages
const numbers = [1, 2, 3, 4, 5];
const sum = lodash.sum(numbers);
console.log(`✓ Used lodash: sum([1,2,3,4,5]) = ${sum}`);
console.log('✓ Used multiple packages successfully');
const result = {
success: true,
packages: ['axios', 'lodash'],
lodashSum: sum
};
console.log(JSON.stringify(result));
process.exit(0);
} catch (error) {
console.error(`✗ Error: ${error.message}`);
process.exit(1);
}
"""
action = client.create_action(
pack_ref=pack_ref,
data={
"name": f"nodejs_multi_{unique_ref()}",
"description": "Action with multiple npm packages",
"runner_type": "nodejs",
"entry_point": "multi_pkg.js",
"enabled": True,
"parameters": {},
"metadata": {"npm_dependencies": {"axios": "^1.6.0", "lodash": "^4.17.21"}},
},
)
action_ref = action["ref"]
print(f"✓ Created Node.js action: {action_ref}")
print(f" Dependencies:")
print(f" - axios ^1.6.0")
print(f" - lodash ^4.17.21")
# ========================================================================
# STEP 2: Execute action
# ========================================================================
print("\n[STEP 2] Executing action...")
execution = client.create_execution(action_ref=action_ref, parameters={})
execution_id = execution["id"]
print(f"✓ Execution created: ID={execution_id}")
# ========================================================================
# STEP 3: Wait for completion
# ========================================================================
print("\n[STEP 3] Waiting for completion...")
result = wait_for_execution_status(
client=client,
execution_id=execution_id,
expected_status="succeeded",
timeout=60,
)
print(f"✓ Execution completed: status={result['status']}")
# ========================================================================
# STEP 4: Verify multiple packages
# ========================================================================
print("\n[STEP 4] Verifying multiple packages...")
execution_details = client.get_execution(execution_id)
stdout = execution_details.get("stdout", "")
if "All packages imported successfully" in stdout:
print(" ✓ All packages imported")
if "axios:" in stdout:
print(" ✓ axios package available")
if "lodash:" in stdout:
print(" ✓ lodash package available")
if "Used lodash:" in stdout:
print(" ✓ Packages used successfully")
# ========================================================================
# FINAL SUMMARY
# ========================================================================
print("\n" + "=" * 80)
print("TEST SUMMARY: Multiple npm Packages")
print("=" * 80)
print(f"✓ Action: {action_ref}")
print(f"✓ Dependencies: 2 packages")
print(f"✓ Execution: succeeded")
print(f"✓ All packages imported and used")
print("\n✅ TEST PASSED: Multiple npm packages work correctly!")
print("=" * 80 + "\n")
def test_nodejs_action_async_await(client: AttuneClient, test_pack):
"""
Test Node.js action with async/await.
Flow:
1. Create action using modern async/await syntax
2. Execute action
3. Verify async operations work correctly
"""
print("\n" + "=" * 80)
print("TEST: Node.js Action - Async/Await")
print("=" * 80)
pack_ref = test_pack["ref"]
# ========================================================================
# STEP 1: Create async action
# ========================================================================
print("\n[STEP 1] Creating async Node.js action...")
async_script = """
const params = process.argv[2] ? JSON.parse(process.argv[2]) : {};
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main() {
try {
console.log('✓ Starting async action');
await delay(1000);
console.log('✓ Waited 1 second');
await delay(1000);
console.log('✓ Waited another second');
const result = {
success: true,
message: 'Async/await works!',
delaysCompleted: 2
};
console.log('✓ Async action completed');
console.log(JSON.stringify(result));
process.exit(0);
} catch (error) {
console.error(`✗ Error: ${error.message}`);
process.exit(1);
}
}
main();
"""
action = client.create_action(
pack_ref=pack_ref,
data={
"name": f"nodejs_async_{unique_ref()}",
"description": "Action with async/await",
"runner_type": "nodejs",
"entry_point": "async_action.js",
"enabled": True,
"parameters": {},
},
)
action_ref = action["ref"]
print(f"✓ Created async Node.js action: {action_ref}")
# ========================================================================
# STEP 2: Execute action
# ========================================================================
print("\n[STEP 2] Executing async action...")
start_time = time.time()
execution = client.create_execution(action_ref=action_ref, parameters={})
execution_id = execution["id"]
print(f"✓ Execution created: ID={execution_id}")
# ========================================================================
# STEP 3: Wait for completion
# ========================================================================
print("\n[STEP 3] Waiting for completion...")
result = wait_for_execution_status(
client=client,
execution_id=execution_id,
expected_status="succeeded",
timeout=20,
)
end_time = time.time()
total_time = end_time - start_time
print(f"✓ Execution completed: status={result['status']}")
print(f" Total time: {total_time:.1f}s")
# ========================================================================
# STEP 4: Verify async behavior
# ========================================================================
print("\n[STEP 4] Verifying async behavior...")
execution_details = client.get_execution(execution_id)
stdout = execution_details.get("stdout", "")
if "Starting async action" in stdout:
print(" ✓ Async action started")
if "Waited 1 second" in stdout:
print(" ✓ First delay completed")
if "Waited another second" in stdout:
print(" ✓ Second delay completed")
if "Async action completed" in stdout:
print(" ✓ Async action completed")
# Should take at least 2 seconds (two delays)
if total_time >= 2:
print(f" ✓ Timing correct: {total_time:.1f}s >= 2s")
# ========================================================================
# FINAL SUMMARY
# ========================================================================
print("\n" + "=" * 80)
print("TEST SUMMARY: Async/Await")
print("=" * 80)
print(f"✓ Async action: {action_ref}")
print(f"✓ Execution: succeeded")
print(f"✓ Async/await: working")
print(f"✓ Total time: {total_time:.1f}s")
print("\n✅ TEST PASSED: Async/await works correctly!")
print("=" * 80 + "\n")