Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test issues #52

Merged
merged 9 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
"source.organizeImports": "explicit",
"source.fixAll": "explicit"
},
"editor.rulers": [
80
],
"editor.rulers": [80],
"editor.quickSuggestions": {
"comments": "on",
"strings": "on",
"other": "on"
},
"editor.suggestOnTriggerCharacters": true
}
"editor.suggestOnTriggerCharacters": true,
"[shellscript]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
}
}
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ services:
target: test
environment:
TEST_FAAS_STARTUP_DEPLOY: ${TEST_FAAS_STARTUP_DEPLOY:-false}
TEST_FAAS_DEPENDENCY_DEPLOY: ${TEST_FAAS_DEPENDENCY_DEPLOY:-true}
network_mode: host
depends_on:
- faas
Expand Down
75 changes: 64 additions & 11 deletions src/utils/install.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,81 @@
import { promises as fs } from 'fs';
import path from 'path';
import { Resource } from '../app';
import { exec } from './exec';

const createInstallDependenciesScript = (
runner: string,
path: string
): string => {
type Runner = 'python' | 'nodejs' | 'ruby' | 'csharp';

const targetFiles: Record<Runner, string> = {
nodejs: 'package.json',
python: 'requirements.txt',
ruby: 'Gemfile',
csharp: 'project.json'
};

const isRunner = (runner: string): runner is Runner => {
return ['nodejs', 'python', 'ruby', 'csharp'].includes(runner);
};

const findDependencyFile = async (
dir: string,
runner: Runner
): Promise<string | null> => {
const files = await fs.readdir(dir);

for (const file of files) {
const fullPath = path.join(dir, file);
const stat = await fs.stat(fullPath);

if (stat.isDirectory()) {
const result = await findDependencyFile(fullPath, runner);
if (result) return result;
} else if (file === targetFiles[runner]) {
return dir;
}
}

return null;
};

const createInstallDependenciesScript = async (
runner: Runner,
basePath: string
): Promise<string> => {
const dependencyFilePath = await findDependencyFile(basePath, runner);

if (!dependencyFilePath) {
throw new Error(`No ${runner} dependencies file found`);
}

const installDependenciesScript: Record<string, string> = {
python: `cd ${path} && metacall pip3 install -r requirements.txt`,
nodejs: `cd ${path} && metacall npm i`,
ruby: `cd ${path} && metacall bundle install`,
csharp: `cd ${path} && metacall dotnet restore && metacall dotnet release`
python: `cd ${dependencyFilePath} && metacall pip3 install -r requirements.txt`,
nodejs: `cd ${dependencyFilePath} && metacall npm i`,
ruby: `cd ${dependencyFilePath} && metacall bundle install`,
csharp: `cd ${dependencyFilePath} && metacall dotnet restore && metacall dotnet release`
};
return installDependenciesScript[runner];
};

// Todo: Async Error Handling
export const installDependencies = async (
resource: Resource
): Promise<void> => {
if (!resource.runners) return;

for (const runner of resource.runners) {
if (runner == undefined) continue;
else {
await exec(createInstallDependenciesScript(runner, resource.path));
if (runner && isRunner(runner)) {
try {
const script = await createInstallDependenciesScript(
runner,
resource.path
);
await exec(script);
} catch (err) {
console.error(
`Failed to install dependencies for runner ${runner}:`,
err
);
}
}
}
};
19 changes: 19 additions & 0 deletions test/data/python-dependency-app/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import requests

# Fetch random joke function
def fetchJoke():
try:
response = requests.get('https://official-joke-api.appspot.com/random_joke')
response.raise_for_status() # Raise an error for bad status codes
joke = response.json()
return {
'setup': joke['setup'],
'punchline': joke['punchline']
}
except requests.RequestException as e:
return {'message': 'Error fetching joke', 'error': str(e)}

# Example usage
if __name__ == "__main__":
joke = fetchJoke()
print(joke)
5 changes: 5 additions & 0 deletions test/data/python-dependency-app/metacall.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"language_id": "py",
"path": ".",
"scripts": ["handler.py"]
}
1 change: 1 addition & 0 deletions test/data/python-dependency-app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requests
115 changes: 80 additions & 35 deletions test/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,20 @@

set -exuo pipefail

# Maximum number of retries
MAX_RETRIES=5
RETRY_COUNT=0

# FaaS base URL
BASE_URL="http://localhost:9000"

# #function to check readiness
function check_readiness() {
local status_code
status_code=$(curl -s -o /dev/null -w "%{http_code}" $BASE_URL/readiness)
echo "$status_code"
}

# Get the prefix of a deployment
function getPrefix() {
prefix=$(metacall-deploy --dev --inspect Raw | jq -r ".[] | select(.suffix == \"$1\") | .prefix")
Expand All @@ -38,56 +49,90 @@ function deploy() {
}

# Wait for the FaaS to be ready
while [[ ! $(curl -s -o /dev/null -w "%{http_code}" $BASE_URL/readiness) = "200" ]]; do
while [[ $(check_readiness) != "200" ]]; do
if [[ $RETRY_COUNT -ge $MAX_RETRIES ]]; then
echo "Readiness check failed after $MAX_RETRIES retries."
exit 1
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
sleep 1
done

echo "FaaS ready, starting tests."

# Test deploy (Python) without dependencies
app="python-base-app"
pushd data/$app
# Function to run tests
function run_tests() {
local app=$1
local test_func=$2

pushd data/$app
deploy
prefix=$(getPrefix $app)
url=$BASE_URL/$prefix/$app/v1/call
[[ $(curl -s $url/number) = 100 ]] || exit 1
[[ $(curl -s $url/text) = '"asd"' ]] || exit 1
popd

# Test inspect
echo "Testing inspect functionality."
$test_func $url
popd

# Inspect the deployed projects
inspect_response=$(curl -s $BASE_URL/api/inspect)
# Test inspect
echo "Testing inspect functionality."

# Verify inspection
if [[ $inspect_response != *"$prefix"* ]]; then
echo "Inspection test failed."
exit 1
fi
# Inspect the deployed projects
inspect_response=$(curl -s $BASE_URL/api/inspect)

# Verify packages are included in the response
if [[ $inspect_response != *"packages"* ]]; then
echo "packages not found in inspection response."
exit 1
fi
# Verify inspection
if [[ $inspect_response != *"$prefix"* ]]; then
echo "Inspection test failed."
exit 1
fi

echo "Inspection test passed."
# Verify packages are included in the response
if [[ $inspect_response != *"packages"* ]]; then
echo "packages not found in inspection response."
exit 1
fi

# Test delete only if we are not testing startup deployments
if [[ "${TEST_FAAS_STARTUP_DEPLOY}" == "true" ]]; then
echo "Testing delete functionality."
echo "Inspection test passed."

# Test delete only if we are not testing startup deployments
if [[ "${TEST_FAAS_STARTUP_DEPLOY}" == "true" ]]; then
echo "Testing delete functionality."

# Delete the deployed project
curl -X POST -H "Content-Type: application/json" -d '{"suffix":"'"$app"'","prefix":"'"$prefix"'","version":"v1"}' $BASE_URL/api/deploy/delete

# Verify deletion
if [[ "$app" == "python-dependency-app" ]]; then
if [[ $(curl -s -o /dev/null -w "%{http_code}" $BASE_URL/$prefix/$app/v1/call/fetchJoke) != "404" ]]; then
echo "Deletion test failed."
exit 1
fi
else
if [[ $(curl -s -o /dev/null -w "%{http_code}" $BASE_URL/$prefix/$app/v1/call/number) != "404" ]]; then
echo "Deletion test failed."
exit 1
fi
fi

echo "Deletion test passed."
fi
}

# Delete the deployed project
curl -X POST -H "Content-Type: application/json" -d '{"suffix":"python-base-app","prefix":"'"$prefix"'","version":"v1"}' $BASE_URL/api/deploy/delete
# Test function for python-base-app
function test_python_base_app() {
local url=$1
[[ $(curl -s $url/number) = 100 ]] || exit 1
[[ $(curl -s $url/text) = '"asd"' ]] || exit 1
}

# Verify deletion
if [[ $(curl -s -o /dev/null -w "%{http_code}" $BASE_URL/$prefix/$app/v1/call/number) != "404" ]]; then
echo "Deletion test failed."
exit 1
fi
# Test function for python-dependency-app
function test_python_dependency_app() {
local url=$1
[[ $(curl -s $url/fetchJoke) == *"setup"* && $(curl -s $url/fetchJoke) == *"punchline"* ]] || exit 1
}

echo "Deletion test passed."
# Run tests without dependencies
run_tests "python-base-app" test_python_base_app
if [[ "${TEST_FAAS_DEPENDENCY_DEPLOY}" == "true" ]]; then
run_tests "python-dependency-app" test_python_dependency_app
fi

echo "Integration tests passed without errors."
echo "Integration tests passed without errors."
Loading