diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c997905..0345655 100755 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,59 +18,25 @@ jobs: fail-fast: false matrix: php: ['8.1', '8.2', '8.3'] - laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] - guzzle: ['6.*', '7.*'] + laravel: ['10.*', '11.*', '12.*'] dependency-version: [prefer-lowest, prefer-stable] include: - - laravel: 8.* - testbench: 6.* - - laravel: 9.* - testbench: 7.* - laravel: 10.* - testbench: 8.* + testbench: ^8.23 - laravel: 11.* - testbench: 9.* + testbench: ^9.5 - laravel: 12.* - testbench: 10.* + testbench: ^10 exclude: - # Laravel 8 exclusions - - laravel: 8.* - php: 8.1 - dependency-version: prefer-lowest - - laravel: 8.* - php: 8.2 - dependency-version: prefer-lowest - - laravel: 8.* - php: 8.3 - dependency-version: prefer-lowest - - # Laravel 9 exclusions - - laravel: 9.* - php: 8.2 - dependency-version: prefer-lowest - - laravel: 9.* - php: 8.3 - dependency-version: prefer-lowest - - # Laravel 11 exclusions + # Laravel 11 requires PHP 8.2+ - laravel: 11.* php: 8.1 - # Laravel 12 exclusions + # Laravel 12 requires PHP 8.2+ - laravel: 12.* php: 8.1 - # Guzzle exclusions - - laravel: 9.* - guzzle: 6.* - - laravel: 10.* - guzzle: 6.* - - laravel: 11.* - guzzle: 6.* - - laravel: 12.* - guzzle: 6.* - - name: P${{ matrix.php }} / L${{ matrix.laravel }} / G${{ matrix.guzzle }} / ${{ matrix.dependency-version }} + name: P${{ matrix.php }} / L${{ matrix.laravel }} / ${{ matrix.dependency-version }} steps: - name: Checkout code @@ -91,8 +57,7 @@ jobs: - name: Install dependencies run: | - composer self-update ${{ matrix.composer-version }} - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "guzzlehttp/guzzle:${{ matrix.guzzle }}" --no-interaction --no-update --dev + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update --dev composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: Execute tests diff --git a/.gitignore b/.gitignore index f190ee6..cf89ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.claude/ /vendor /storage/framework/testing .env diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7f3a532 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,91 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What is Sidecar? + +Sidecar is a Laravel package that lets you deploy and run AWS Lambda functions directly from your Laravel app. Write a function in Node, Python, Ruby, Java, or any Lambda-supported runtime, and call it from PHP as easily as `MyFunction::execute(['key' => 'value'])`. + +The package handles all the AWS complexity: packaging your code, uploading to S3, creating/updating Lambda functions, managing versions, and invoking them. + +## Testing + +```bash +# Run all tests +./vendor/bin/phpunit + +# Run a single test file +./vendor/bin/phpunit tests/Unit/FunctionTest.php + +# Run a specific test method +./vendor/bin/phpunit --filter test_runtime_value_resolves_enum_to_string +``` + +Tests use Orchestra Testbench. The test suite has unit tests in `tests/Unit/` and integration tests in `tests/Integration/`. Most development work uses unit tests with mocked AWS clients. + +## How the Code is Organized + +### The Main Players + +**`LambdaFunction`** is the abstract base class that users extend. Every function needs two methods: +- `handler()` - tells Lambda which file/function to run (e.g., `'image.handler'`) +- `package()` - lists files to include in the deployment ZIP + +Users call static methods like `MyFunction::execute($payload)` to run their functions. + +**`Manager`** (accessed via the `Sidecar` facade) does the actual work of invoking Lambda functions. It prepares payloads, calls the AWS SDK, and wraps responses in result objects. + +**`Deployment`** handles the deploy workflow: package the code, create or update the Lambda function, then optionally activate it by pointing an alias to the new version. + +**`Package`** builds the ZIP file for deployment. It collects files based on include/exclude patterns, computes a hash for change detection, and streams the ZIP directly to S3. + +### AWS Clients + +The `src/Clients/` directory has thin wrappers around AWS SDK clients: +- `LambdaClient` extends the SDK client and adds retry logic for Lambda's "Pending" state +- `S3Client` and `CloudWatchLogsClient` are simpler wrappers + +### Results + +When you execute a function: +- Sync calls return a `SettledResult` with the response body, logs, and error info +- Async calls return a `PendingResult` that resolves to `SettledResult` when you call `->settled()` + +### Runtime and Architecture + +`Runtime` and `Architecture` are PHP 8.1 backed enums. There are also deprecated `RuntimeConstants` and `ArchitectureConstants` classes for backwards compatibility. + +When working with these, use `runtimeValue()` and `architectureValue()` methods to get the string values - they handle both enum and string inputs. + +## Configuration + +Everything lives in `config/sidecar.php`: +- `functions` - array of `LambdaFunction` classes to deploy +- `env` - environment name, used to namespace function names (defaults to `APP_ENV`) +- `timeout`, `memory`, `storage` - default Lambda settings +- AWS credentials (`aws_key`, `aws_secret`, `aws_region`, `aws_bucket`, `execution_role`) + +Function names are automatically prefixed with app name and environment to avoid collisions. + +## Artisan Commands + +These are what users run (not needed for package development, but good to know): +- `sidecar:deploy` - packages and deploys functions to Lambda +- `sidecar:activate` - points the "active" alias to latest version +- `sidecar:deploy --activate` - both in one step +- `sidecar:warm` - pre-warms function instances to reduce cold starts +- `sidecar:configure` - interactive wizard to set up AWS resources + +## Events + +The package fires Laravel events you can hook into: +- `BeforeFunctionsDeployed` / `AfterFunctionsDeployed` +- `BeforeFunctionsActivated` / `AfterFunctionsActivated` +- `BeforeFunctionExecuted` / `AfterFunctionExecuted` + +## Things to Know + +- This package supports Laravel 10, 11, and 12 with PHP 8.1+ +- All source files use `declare(strict_types=1)` +- The codebase uses constructor property promotion and typed properties throughout +- Tests use Mockery for mocking AWS clients - check existing tests for patterns diff --git a/composer.json b/composer.json index ed48027..241513d 100644 --- a/composer.json +++ b/composer.json @@ -11,17 +11,17 @@ ], "require": { "php": "^8.1", - "illuminate/filesystem": "^8|^9|^10|^11|^12.0", - "illuminate/console": "^8|^9|^10|^11|^12.0", - "illuminate/support": "^8|^9|^10|^11|^12.0", + "illuminate/filesystem": "^10|^11|^12", + "illuminate/console": "^10|^11|^12", + "illuminate/support": "^10|^11|^12", "maennchen/zipstream-php": "^3.1", - "guzzlehttp/guzzle": "^6.5.8|^7.2", + "guzzlehttp/guzzle": "^7.2", "aws/aws-sdk-php": "^3.216.1" }, "require-dev": { - "orchestra/testbench": "^5|^6|^7|^8|^9|^10.0", + "orchestra/testbench": "^8.23|^9.5|^10", "mockery/mockery": "^1.3.3", - "phpunit/phpunit": ">=8.5.23|^9|^10" + "phpunit/phpunit": "^9|^10|^11" }, "autoload": { "psr-4": { diff --git a/config/sidecar.php b/config/sidecar.php index 53739af..48a64e2 100644 --- a/config/sidecar.php +++ b/config/sidecar.php @@ -46,7 +46,7 @@ * The default architecture your function runs on. * Available options are: x86_64, arm64 */ - 'architecture' => env('SIDECAR_ARCH', Architecture::X86_64), + 'architecture' => env('SIDECAR_ARCH', Architecture::X86_64->value), /* * The base path for your package files. If you e.g. keep diff --git a/docs/events.md b/docs/events.md index 5635198..ef54a66 100644 --- a/docs/events.md +++ b/docs/events.md @@ -1,13 +1,22 @@ # Events -Sidecar fires a few events related to deployment that you can hook into: +Sidecar fires events during deployment, activation, and execution that you can hook into. + +## Deployment & Activation Events - `BeforeFunctionsDeployed` - `AfterFunctionsDeployed` - `BeforeFunctionsActivated` - `AfterFunctionsActivated` -Each of these events has a public `functions` property that holds all the functions that are being deployed or activated. +Each of these events has a public `functions` property that holds all the functions being deployed or activated. You can use these to build packages, install dependencies, or clean up after deployment. + +## Execution Events + +- `BeforeFunctionExecuted` +- `AfterFunctionExecuted` + +These events fire every time a function is executed. `BeforeFunctionExecuted` has `function` and `payload` properties. `AfterFunctionExecuted` adds the `result` property. -You can use these events to build packages, install dependencies, or clean up after they are deployed. \ No newline at end of file +You can use these for logging, monitoring, or modifying payloads before they're sent to Lambda. \ No newline at end of file diff --git a/docs/functions/customization.md b/docs/functions/customization.md index 22d2875..6ff18a1 100644 --- a/docs/functions/customization.md +++ b/docs/functions/customization.md @@ -5,37 +5,52 @@ The only two things _required_ for a Sidecar function are the [package and the h ## Runtime -Lambda supports multiple languages through the use of runtimes. You can choose any of the following runtimes by returning its corresponding identifier: - -- Node.js 20: `nodejs20.x` -- Node.js 18: `nodejs18.x` -- Python 3.12: `python3.12` -- Python 3.11: `python3.11` -- Python 3.10: `python3.10` -- Python 3.9: `python3.9` -- Java 21: `java21` -- Java 17: `java17` -- Java 11: `java11` -- Java 8: `java8.al2` -- .NET 8: `dotnet8` -- .NET 6: `dotnet6` -- Ruby 3.3: `ruby3.3` -- Ruby 3.2: `ruby3.2` -- OS-only runtime (Amazon Linux 2023): `provided.al2023` -- OS-only runtime (Amazon Linux 2): `provided.al2` - -E.g. to use the Python 3.12 runtime, you would return `python3.12`: +Lambda supports multiple languages through the use of runtimes. Sidecar provides a `Runtime` enum with all supported runtimes, or you can return the runtime identifier as a string. + +Available runtimes include: + +- Node.js 24: `Runtime::NODEJS_24` or `'nodejs24.x'` +- Node.js 22: `Runtime::NODEJS_22` or `'nodejs22.x'` +- Node.js 20: `Runtime::NODEJS_20` or `'nodejs20.x'` +- Python 3.14: `Runtime::PYTHON_314` or `'python3.14'` +- Python 3.13: `Runtime::PYTHON_313` or `'python3.13'` +- Python 3.12: `Runtime::PYTHON_312` or `'python3.12'` +- Python 3.11: `Runtime::PYTHON_311` or `'python3.11'` +- Python 3.10: `Runtime::PYTHON_310` or `'python3.10'` +- Python 3.9: `Runtime::PYTHON_39` or `'python3.9'` +- Java 25: `Runtime::JAVA_25` or `'java25'` +- Java 21: `Runtime::JAVA_21` or `'java21'` +- Java 17: `Runtime::JAVA_17` or `'java17'` +- Java 11: `Runtime::JAVA_11` or `'java11'` +- Java 8: `Runtime::JAVA_8_LINUX2` or `'java8.al2'` +- .NET 9: `Runtime::DOT_NET_9` or `'dotnet9'` +- .NET 8: `Runtime::DOT_NET_8` or `'dotnet8'` +- Ruby 3.4: `Runtime::RUBY_34` or `'ruby3.4'` +- Ruby 3.3: `Runtime::RUBY_33` or `'ruby3.3'` +- Ruby 3.2: `Runtime::RUBY_32` or `'ruby3.2'` +- OS-only runtime (Amazon Linux 2023): `Runtime::PROVIDED_AL2023` or `'provided.al2023'` +- OS-only runtime (Amazon Linux 2): `Runtime::PROVIDED_AL2` or `'provided.al2'` + +You can use either the enum or a string: ```php +use Hammerstone\Sidecar\Runtime; + class ExampleFunction extends LambdaFunction { public function runtime() // [tl! focus:3] { + // Using the enum (recommended) + return Runtime::PYTHON_312; + + // Or using a string return 'python3.12'; } } ``` +The default runtime is `Runtime::NODEJS_20`. + Read more in the [AWS Documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). ## Memory diff --git a/docs/functions/deploying.md b/docs/functions/deploying.md index 962a010..db550b4 100644 --- a/docs/functions/deploying.md +++ b/docs/functions/deploying.md @@ -14,7 +14,7 @@ When you run that, you'll see an output log similar to the one below: ```text [Sidecar] Deploying App\Sidecar\OgImage to Lambda as `SC-Laravel-local-Sidecar-OgImage`. ↳ Environment: local - ↳ Runtime: nodejs12.x + ↳ Runtime: nodejs20.x ↳ Packaging function code. ↳ Creating a new zip file. ↳ Zip file created at s3://sidecar-us-east-2-XXX/sidecar/001-79a5915eaec296be04a0f4fb7cc80e40.zip diff --git a/docs/functions/performance.md b/docs/functions/performance.md index aef475f..022bab5 100644 --- a/docs/functions/performance.md +++ b/docs/functions/performance.md @@ -1,3 +1,36 @@ -# Performance +# Performance Tips + +Here are some tips for getting the best performance out of your Sidecar functions. + +## Reduce Cold Starts + +Cold starts happen when Lambda spins up a new container to handle your request. To minimize their impact: + +- **Use warming** - Configure `warmingConfig()` on your functions and schedule `sidecar:warm` to run regularly. See [Warming Functions](warming) for details. +- **Use pre-warming** - Add `--pre-warm` when activating to warm functions before they go live. +- **Keep functions warm** - Schedule `sidecar:warm` to run every 5 minutes to prevent containers from being frozen. + +## Optimize Your Package Size + +Smaller packages deploy faster and have quicker cold starts: + +- **Separate your node_modules** - Keep Lambda dependencies in a separate `package.json` from your main app. +- **Use NCC** - Compile Node.js handlers into a single file with [NCC](https://github.com/vercel/ncc). See [Handlers & Packages](handlers-and-packages#compiling-your-handler-with-ncc). +- **Only include what you need** - Be specific about what goes in your `package()` method. + +## Right-Size Memory + +Lambda allocates CPU proportionally to memory. More memory means more CPU: + +- **Profile your functions** - Use `$result->report()` to see memory usage and execution time. +- **Test different memory settings** - Sometimes paying for more memory results in faster execution and lower total cost. + +## Use Async When Possible + +If you don't need the result immediately: + +- **Use `executeAsync()`** - Let your code continue while Lambda runs in the background. +- **Use `executeMany()`** - Run multiple invocations in parallel instead of sequentially. +- **Use `executeAsEvent()`** - For fire-and-forget scenarios where you don't need the response. diff --git a/docs/overview.md b/docs/overview.md index d5f3a15..88075d8 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -3,25 +3,27 @@ Sidecar packages, deploys, and executes AWS Lambda functions from your Laravel application. {.text-xl .font-bold} -It works with _any_ Laravel 7, 8, 9 or 10 application, hosted _anywhere_, including your local machine. {.font-bold} +It works with _any_ Laravel 10, 11, or 12 application, hosted _anywhere_, including your local machine. {.font-bold} You can write functions in any of the following runtimes and execute them straight from PHP: +- Node.js 24 +- Node.js 22 - Node.js 20 -- Node.js 18 -- Node.js 16 +- Python 3.14 +- Python 3.13 - Python 3.12 - Python 3.11 - Python 3.10 - Python 3.9 -- Python 3.8 +- Java 25 - Java 21 - Java 17 - Java 11 - Java 8 +- .NET 9 - .NET 8 -- .NET 7 -- .NET 6 +- Ruby 3.4 - Ruby 3.3 - Ruby 3.2 - OS-only runtime (Amazon Linux 2023) diff --git a/src/Architecture.php b/src/Architecture.php index 02082f2..ec21a57 100644 --- a/src/Architecture.php +++ b/src/Architecture.php @@ -1,10 +1,11 @@ */ diff --git a/src/Clients/Configurations/AwsClientConfiguration.php b/src/Clients/Configurations/AwsClientConfiguration.php index a5d4c52..6eb4147 100644 --- a/src/Clients/Configurations/AwsClientConfiguration.php +++ b/src/Clients/Configurations/AwsClientConfiguration.php @@ -1,12 +1,14 @@ 'latest', diff --git a/src/Clients/LambdaClient.php b/src/Clients/LambdaClient.php index d8efe93..a598e20 100644 --- a/src/Clients/LambdaClient.php +++ b/src/Clients/LambdaClient.php @@ -1,5 +1,7 @@ */ diff --git a/src/Clients/S3Client.php b/src/Clients/S3Client.php index a2414a1..943f745 100644 --- a/src/Clients/S3Client.php +++ b/src/Clients/S3Client.php @@ -1,5 +1,7 @@ */ @@ -10,26 +12,14 @@ abstract class BaseAction { - /** - * @var Configure - */ - public $command; - - /** - * @var string - */ - public $region; - - public function __construct($region, Configure $command) - { - $this->region = $region; - - $this->command = $command; - } + public function __construct( + public ?string $region, + public Configure $command + ) {} - abstract public function invoke(); + abstract public function invoke(): mixed; - protected function progress($message) + protected function progress(string $message): void { $this->command->text("==> $message"); } diff --git a/src/Commands/Actions/CreateBucket.php b/src/Commands/Actions/CreateBucket.php index 084f9f1..67d8321 100644 --- a/src/Commands/Actions/CreateBucket.php +++ b/src/Commands/Actions/CreateBucket.php @@ -1,5 +1,7 @@ */ @@ -7,25 +9,17 @@ namespace Hammerstone\Sidecar\Commands\Actions; use Aws\S3\S3Client; +use Exception; use Illuminate\Support\Str; use Throwable; class CreateBucket extends BaseAction { - /** - * @var S3Client - */ - protected $client; - - /** - * @var string - */ - protected $bucket; - - /** - * @return string - */ - public function invoke() + protected S3Client $client; + + protected string $bucket; + + public function invoke(): string { $this->client = $this->command->client(S3Client::class); diff --git a/src/Commands/Actions/CreateDeploymentUser.php b/src/Commands/Actions/CreateDeploymentUser.php index 2dbf386..10c7a96 100644 --- a/src/Commands/Actions/CreateDeploymentUser.php +++ b/src/Commands/Actions/CreateDeploymentUser.php @@ -1,5 +1,7 @@ */ @@ -12,15 +14,9 @@ class CreateDeploymentUser extends BaseAction { - /** - * @var IamClient - */ - protected $client; - - /** - * @return array - */ - public function invoke() + protected IamClient $client; + + public function invoke(): array { $this->client = $this->command->client(IamClient::class); diff --git a/src/Commands/Actions/CreateExecutionRole.php b/src/Commands/Actions/CreateExecutionRole.php index 710b853..299811e 100644 --- a/src/Commands/Actions/CreateExecutionRole.php +++ b/src/Commands/Actions/CreateExecutionRole.php @@ -1,5 +1,7 @@ */ @@ -12,12 +14,9 @@ class CreateExecutionRole extends BaseAction { - /** - * @var IamClient - */ - protected $client; + protected IamClient $client; - public function invoke() + public function invoke(): string { $this->progress('Creating an execution role for your functions...'); diff --git a/src/Commands/Actions/DestroyAdminKeys.php b/src/Commands/Actions/DestroyAdminKeys.php index ba265c5..ac93d4e 100644 --- a/src/Commands/Actions/DestroyAdminKeys.php +++ b/src/Commands/Actions/DestroyAdminKeys.php @@ -1,5 +1,7 @@ */ @@ -11,23 +13,23 @@ class DestroyAdminKeys extends BaseAction { - public $key; + public ?string $key = null; - public function setKey($key) + public function setKey(string $key): static { $this->key = $key; return $this; } - public function invoke() + public function invoke(): mixed { $client = $this->command->client(IamClient::class); try { $user = $client->getUser(); } catch (Throwable $e) { - return; + return null; } $name = $user['User']['UserName']; @@ -52,7 +54,7 @@ public function invoke() if (!$this->command->confirm($question, $default = $isSidecar)) { $this->progress('Not deleting keys'); - return; + return null; } $this->progress('Deleting admin keys...'); @@ -67,5 +69,7 @@ public function invoke() } $this->progress('Admin keys deleted'); + + return null; } } diff --git a/src/Commands/Actions/DetermineRegion.php b/src/Commands/Actions/DetermineRegion.php index b7ccb56..cf0260f 100644 --- a/src/Commands/Actions/DetermineRegion.php +++ b/src/Commands/Actions/DetermineRegion.php @@ -1,30 +1,19 @@ */ namespace Hammerstone\Sidecar\Commands\Actions; -use Aws\S3\S3Client; use Illuminate\Support\Facades\File; +use Throwable; class DetermineRegion extends BaseAction { - /** - * @var S3Client - */ - protected $client; - - /** - * @var bool - */ - protected $isVapor; - - /** - * @return string - */ - public function invoke() + public function invoke(): string { $region = config('sidecar.aws_region'); diff --git a/src/Commands/Activate.php b/src/Commands/Activate.php index c3012db..5cdde0f 100644 --- a/src/Commands/Activate.php +++ b/src/Commands/Activate.php @@ -1,5 +1,7 @@ */ diff --git a/src/Commands/Configure.php b/src/Commands/Configure.php index 089bdb6..463e86f 100644 --- a/src/Commands/Configure.php +++ b/src/Commands/Configure.php @@ -1,5 +1,7 @@ */ @@ -17,39 +19,17 @@ class Configure extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ protected $signature = 'sidecar:configure'; - /** - * The console command description. - * - * @var string - */ protected $description = 'Interactively configure your Sidecar AWS environment variables'; - /** - * @var string - */ - protected $key; + protected ?string $key = null; - /** - * @var string - */ - protected $secret; + protected ?string $secret = null; - /** - * @var string - */ - protected $region; + protected ?string $region = null; - /** - * @var int - */ - protected $width = 75; + protected int $width = 75; /** * @throws Exception diff --git a/src/Commands/Deploy.php b/src/Commands/Deploy.php index 7c2382e..d856111 100644 --- a/src/Commands/Deploy.php +++ b/src/Commands/Deploy.php @@ -1,5 +1,7 @@ */ diff --git a/src/Commands/EnvironmentAwareCommand.php b/src/Commands/EnvironmentAwareCommand.php index b943d75..a925b5d 100644 --- a/src/Commands/EnvironmentAwareCommand.php +++ b/src/Commands/EnvironmentAwareCommand.php @@ -1,5 +1,7 @@ */ @@ -22,7 +24,7 @@ public function __construct() $this->getDefinition()->addOptions(Parser::parse($environment)[2]); } - public function overrideEnvironment() + public function overrideEnvironment(): void { if ($environment = $this->option('env')) { Sidecar::overrideEnvironment($environment); diff --git a/src/Commands/Install.php b/src/Commands/Install.php index 71cc09f..fea8483 100644 --- a/src/Commands/Install.php +++ b/src/Commands/Install.php @@ -1,5 +1,7 @@ */ diff --git a/src/Commands/Warm.php b/src/Commands/Warm.php index 5946d11..ec7b1de 100644 --- a/src/Commands/Warm.php +++ b/src/Commands/Warm.php @@ -1,5 +1,7 @@ */ diff --git a/src/Concerns/HandlesLogging.php b/src/Concerns/HandlesLogging.php index bc90921..b83302d 100644 --- a/src/Concerns/HandlesLogging.php +++ b/src/Concerns/HandlesLogging.php @@ -1,5 +1,7 @@ */ @@ -11,15 +13,9 @@ trait HandlesLogging { - /** - * @var array - */ - protected $loggers = []; + protected array $loggers = []; - /** - * @var bool - */ - protected $sublog = false; + protected bool $sublog = false; /** * @return $this diff --git a/src/Concerns/ManagesEnvironments.php b/src/Concerns/ManagesEnvironments.php index d832b1f..b31d96c 100644 --- a/src/Concerns/ManagesEnvironments.php +++ b/src/Concerns/ManagesEnvironments.php @@ -1,5 +1,7 @@ */ @@ -8,32 +10,20 @@ trait ManagesEnvironments { - /** - * @var string - */ - protected $environment; + protected ?string $environment = null; - /** - * @param string $environment - */ - public function overrideEnvironment($environment) + public function overrideEnvironment(string $environment): void { $this->environment = $environment; } - /** - * Clear the environment override. - */ - public function clearEnvironment() + public function clearEnvironment(): void { $this->environment = null; } - /** - * @return string - */ - public function getEnvironment() + public function getEnvironment(): string { - return $this->environment ?? config('sidecar.env') ?? config('app.env'); + return $this->environment ?? config('sidecar.env') ?? config('app.env', 'production'); } } diff --git a/src/Contracts/AwsClientConfiguration.php b/src/Contracts/AwsClientConfiguration.php index ea10ecc..295ffe4 100644 --- a/src/Contracts/AwsClientConfiguration.php +++ b/src/Contracts/AwsClientConfiguration.php @@ -1,5 +1,7 @@ */ @@ -17,15 +19,9 @@ class Deployment { - /** - * @var array - */ - protected $functions; + protected array $functions; - /** - * @var LambdaClient - */ - protected $lambda; + protected LambdaClient $lambda; /** * @return static @@ -105,10 +101,10 @@ public function activate($prewarm = false) protected function deploySingle(LambdaFunction $function) { Sidecar::log('Environment: ' . Sidecar::getEnvironment()); - Sidecar::log('Architecture: ' . $function->architecture()); + Sidecar::log('Architecture: ' . $function->architectureValue()); Sidecar::log('Package Type: ' . $function->packageType()); if ($function->packageType() === 'Zip') { - Sidecar::log('Runtime: ' . $function->runtime()); + Sidecar::log('Runtime: ' . $function->runtimeValue()); } $function->beforeDeployment(); diff --git a/src/Events/AfterFunctionExecuted.php b/src/Events/AfterFunctionExecuted.php index ecee279..b0c1f7c 100644 --- a/src/Events/AfterFunctionExecuted.php +++ b/src/Events/AfterFunctionExecuted.php @@ -1,5 +1,7 @@ */ @@ -12,27 +14,9 @@ class AfterFunctionExecuted { - /** - * @var LambdaFunction - */ - public $function; - - /** - * @var mixed - */ - public $payload; - - /** - * @var PendingResult|SettledResult - */ - public $result; - - public function __construct($function, $payload, $result) - { - $this->payload = $payload; - - $this->function = $function; - - $this->result = $result; - } + public function __construct( + public LambdaFunction $function, + public mixed $payload, + public PendingResult|SettledResult $result + ) {} } diff --git a/src/Events/AfterFunctionsActivated.php b/src/Events/AfterFunctionsActivated.php index 4cf0d8c..89919e4 100644 --- a/src/Events/AfterFunctionsActivated.php +++ b/src/Events/AfterFunctionsActivated.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class AfterFunctionsActivated { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Events/AfterFunctionsDeployed.php b/src/Events/AfterFunctionsDeployed.php index 2fef2f1..5ac01c6 100644 --- a/src/Events/AfterFunctionsDeployed.php +++ b/src/Events/AfterFunctionsDeployed.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class AfterFunctionsDeployed { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Events/BeforeFunctionExecuted.php b/src/Events/BeforeFunctionExecuted.php index 28237e8..460ede1 100644 --- a/src/Events/BeforeFunctionExecuted.php +++ b/src/Events/BeforeFunctionExecuted.php @@ -1,5 +1,7 @@ */ @@ -10,20 +12,8 @@ class BeforeFunctionExecuted { - /** - * @var LambdaFunction - */ - public $function; - - /** - * @var mixed - */ - public $payload; - - public function __construct($function, $payload) - { - $this->payload = $payload; - - $this->function = $function; - } + public function __construct( + public LambdaFunction $function, + public mixed $payload + ) {} } diff --git a/src/Events/BeforeFunctionsActivated.php b/src/Events/BeforeFunctionsActivated.php index d7f5ff4..e042939 100644 --- a/src/Events/BeforeFunctionsActivated.php +++ b/src/Events/BeforeFunctionsActivated.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class BeforeFunctionsActivated { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Events/BeforeFunctionsDeployed.php b/src/Events/BeforeFunctionsDeployed.php index 187ad4e..98753d0 100644 --- a/src/Events/BeforeFunctionsDeployed.php +++ b/src/Events/BeforeFunctionsDeployed.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class BeforeFunctionsDeployed { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Exceptions/ConfigurationException.php b/src/Exceptions/ConfigurationException.php index 19ee472..5598c95 100644 --- a/src/Exceptions/ConfigurationException.php +++ b/src/Exceptions/ConfigurationException.php @@ -1,5 +1,7 @@ */ diff --git a/src/Exceptions/FunctionNotFoundException.php b/src/Exceptions/FunctionNotFoundException.php index 5a84e56..4fff8aa 100644 --- a/src/Exceptions/FunctionNotFoundException.php +++ b/src/Exceptions/FunctionNotFoundException.php @@ -1,5 +1,7 @@ */ @@ -11,7 +13,7 @@ class FunctionNotFoundException extends SidecarException { - public static function make(LambdaFunction $function) + public static function make(LambdaFunction $function): static { $env = Sidecar::getEnvironment(); diff --git a/src/Exceptions/LambdaExecutionException.php b/src/Exceptions/LambdaExecutionException.php index c514928..a667595 100644 --- a/src/Exceptions/LambdaExecutionException.php +++ b/src/Exceptions/LambdaExecutionException.php @@ -1,5 +1,7 @@ */ diff --git a/src/Exceptions/NoFunctionsRegisteredException.php b/src/Exceptions/NoFunctionsRegisteredException.php index 896f40e..3324fd8 100644 --- a/src/Exceptions/NoFunctionsRegisteredException.php +++ b/src/Exceptions/NoFunctionsRegisteredException.php @@ -1,5 +1,7 @@ */ @@ -10,7 +12,7 @@ class NoFunctionsRegisteredException extends SidecarException { - public function __construct($message = '', $code = 0, ?Throwable $previous = null) + public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) { $message = "No Sidecar functions have been configured. \n" . "Please check your config/sidecar.php file to ensure you have registered your functions. \n" . diff --git a/src/Exceptions/SidecarException.php b/src/Exceptions/SidecarException.php index 82e2261..4a5fcfb 100644 --- a/src/Exceptions/SidecarException.php +++ b/src/Exceptions/SidecarException.php @@ -1,5 +1,7 @@ */ diff --git a/src/Finder.php b/src/Finder.php index 54462fe..401a19c 100644 --- a/src/Finder.php +++ b/src/Finder.php @@ -1,5 +1,7 @@ in($this->includedDirectories()); foreach ($finder->getIterator() as $file) { - if ($this->shouldExclude($file)) { + if ($this->shouldExclude($file->getPathname())) { continue; } diff --git a/src/LambdaFunction.php b/src/LambdaFunction.php index 6a40251..86252ce 100644 --- a/src/LambdaFunction.php +++ b/src/LambdaFunction.php @@ -1,5 +1,7 @@ */ @@ -230,22 +232,40 @@ public function toPendingResult(PromiseInterface $raw) * The runtime environment for the Lambda function. * * @see https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html - * - * @return string */ - public function runtime() + public function runtime(): Runtime|string { return Runtime::NODEJS_20; } + /** + * Resolve the runtime to a string value. + */ + public function runtimeValue(): string + { + $runtime = $this->runtime(); + + return $runtime instanceof Runtime ? $runtime->value : $runtime; + } + /** * The architecture for the Lambda function. - * - * @return string */ - public function architecture() + public function architecture(): Architecture|string { - return config('sidecar.architecture', Architecture::X86_64); + $arch = config('sidecar.architecture', Architecture::X86_64->value); + + return $arch instanceof Architecture ? $arch : $arch; + } + + /** + * Resolve the architecture to a string value. + */ + public function architectureValue(): string + { + $arch = $this->architecture(); + + return $arch instanceof Architecture ? $arch->value : $arch; } /** @@ -426,7 +446,7 @@ public function toDeploymentArray() { $config = [ 'FunctionName' => $this->nameWithPrefix(), - 'Runtime' => $this->runtime(), + 'Runtime' => $this->runtimeValue(), 'Role' => config('sidecar.execution_role'), 'Handler' => $this->normalizedHandler(), 'Code' => $this->packageType() === 'Zip' @@ -441,7 +461,7 @@ public function toDeploymentArray() 'Layers' => $this->layers(), 'Publish' => true, 'PackageType' => $this->packageType(), - 'Architectures' => [$this->architecture()], + 'Architectures' => [$this->architectureValue()], 'Tags' => $this->tags(), ]; diff --git a/src/Manager.php b/src/Manager.php index 303eaa6..81a4c88 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -1,5 +1,7 @@ */ @@ -25,10 +27,7 @@ class Manager { use HandlesLogging, Macroable, ManagesEnvironments; - /** - * @var string - */ - public $executionVersion = 'active'; + public string $executionVersion = 'active'; /** * @param null $callback diff --git a/src/Package.php b/src/Package.php index 67920f8..94f9076 100644 --- a/src/Package.php +++ b/src/Package.php @@ -1,5 +1,7 @@ */ @@ -25,40 +27,20 @@ class Package * a handler function. Use this constant instead. * * @see https://hammerstone.dev/sidecar/docs/main/functions/handlers-and-packages - * - * @var string */ public const CONTAINER_HANDLER = 'container'; - /** - * @var array - */ - protected $include = []; + protected array $include = []; - /** - * @var array - */ - protected $exactIncludes = []; + protected array $exactIncludes = []; - /** - * @var array - */ - protected $stringContents = []; + protected array $stringContents = []; - /** - * @var array - */ - protected $exclude = []; + protected array $exclude = []; - /** - * @var Collection|null - */ - protected $files; + protected ?Collection $files = null; - /** - * @var string - */ - protected $basePath; + protected ?string $basePath = null; /** * @param array $paths diff --git a/src/Providers/SidecarServiceProvider.php b/src/Providers/SidecarServiceProvider.php index 4c9d635..172d522 100644 --- a/src/Providers/SidecarServiceProvider.php +++ b/src/Providers/SidecarServiceProvider.php @@ -1,5 +1,7 @@ */ @@ -21,7 +23,7 @@ class SidecarServiceProvider extends ServiceProvider { - public function register() + public function register(): void { $this->app->singleton(Manager::class); @@ -42,12 +44,12 @@ public function register() }); } - protected function getAwsClientConfiguration() + protected function getAwsClientConfiguration(): array { return $this->app->make(AwsClientConfigurationContract::class)->getConfiguration(); } - public function boot() + public function boot(): void { if ($this->app->runningInConsole()) { $this->commands([ diff --git a/src/Region.php b/src/Region.php index 1b6ba93..5dab444 100644 --- a/src/Region.php +++ b/src/Region.php @@ -1,5 +1,7 @@ */ @@ -56,7 +58,7 @@ class Region const SA_EAST_1 = 'sa-east-1'; // South America (São Paulo) - public static function all() + public static function all(): array { return (new ReflectionClass(static::class))->getConstants(); } diff --git a/src/Results/PendingResult.php b/src/Results/PendingResult.php index 0a9dec2..720b0af 100644 --- a/src/Results/PendingResult.php +++ b/src/Results/PendingResult.php @@ -1,5 +1,7 @@ */ @@ -15,34 +17,14 @@ class PendingResult implements Responsable, ResultContract { - /** - * @var SettledResult - */ - protected $settled; + protected ?SettledResult $settled = null; - /** - * @var PromiseInterface - */ - protected $raw; + public function __construct( + protected PromiseInterface $raw, + protected LambdaFunction $function + ) {} - /** - * @var LambdaFunction - */ - protected $function; - - /** - * @param PromiseInterface $raw - */ - public function __construct($raw, LambdaFunction $function) - { - $this->raw = $raw; - $this->function = $function; - } - - /** - * @return SettledResult - */ - public function settled() + public function settled(): SettledResult { if ($this->settled) { return $this->settled; @@ -51,10 +33,7 @@ public function settled() return $this->settled = $this->function->toSettledResult($this->raw->wait()); } - /** - * @return PromiseInterface - */ - public function rawPromise() + public function rawPromise(): PromiseInterface { return $this->raw; } @@ -67,7 +46,7 @@ public function rawPromise() * * @throws Exception */ - public function toResponse($request) + public function toResponse(mixed $request) { return $this->settled()->toResponse($request); } diff --git a/src/Results/ResultContract.php b/src/Results/ResultContract.php index 91628fe..d6f74d3 100644 --- a/src/Results/ResultContract.php +++ b/src/Results/ResultContract.php @@ -1,27 +1,23 @@ */ namespace Hammerstone\Sidecar\Results; -use Hammerstone\Sidecar\LambdaFunction; use Illuminate\Http\Request; use Illuminate\Http\Response; interface ResultContract { - public function __construct($raw, LambdaFunction $function); - /** * @param Request $request * @return Response */ - public function toResponse($response); + public function toResponse(mixed $response); - /** - * @return SettledResult - */ - public function settled(); + public function settled(): SettledResult; } diff --git a/src/Results/SettledResult.php b/src/Results/SettledResult.php index bfbaa0d..c32a522 100644 --- a/src/Results/SettledResult.php +++ b/src/Results/SettledResult.php @@ -1,5 +1,7 @@ */ @@ -20,36 +22,16 @@ class SettledResult implements Responsable, ResultContract { - /** - * @var Result - */ - protected $raw; - - /** - * @var LambdaFunction - */ - protected $function; + protected array $report = []; - /** - * @var array - */ - protected $report = []; + protected ?string $requestId = null; - /** - * @var string - */ - protected $requestId; - - /** - * @var array - */ - protected $logs = []; - - public function __construct($raw, LambdaFunction $function) - { - $this->raw = $raw; - $this->function = $function; + protected array $logs = []; + public function __construct( + protected Result $raw, + protected LambdaFunction $function + ) { $this->logs = $this->parseLogs(); } @@ -73,10 +55,8 @@ public function rawAwsResult() * This is here as a little nicety for the developer, so that they * can call `settled` on either kind of result (PendingResult * or SettledResult) and get a SettledResult back. - * - * @return $this */ - public function settled() + public function settled(): SettledResult { return $this; } @@ -87,7 +67,7 @@ public function settled() * * @throws Exception */ - public function toResponse($request) + public function toResponse(mixed $request) { return $this->function->toResponse($request, $this); } @@ -211,9 +191,14 @@ public function errorAsString(int $numberOfBacktraces = 2) return $message; } - protected function parseLogs() + protected function parseLogs(): array { - $lines = base64_decode($this->raw->get('LogResult')); + $logResult = $this->raw->get('LogResult'); + if ($logResult === null) { + return []; + } + + $lines = base64_decode($logResult); $lines = explode("\n", $lines); $lines = array_map(function ($line) use (&$reportLineReached) { diff --git a/src/Runtime.php b/src/Runtime.php index 04aaa86..836561d 100644 --- a/src/Runtime.php +++ b/src/Runtime.php @@ -1,71 +1,68 @@ */ diff --git a/src/WarmingConfig.php b/src/WarmingConfig.php index ba8c066..2059294 100644 --- a/src/WarmingConfig.php +++ b/src/WarmingConfig.php @@ -1,5 +1,7 @@ */ @@ -8,13 +10,13 @@ class WarmingConfig { - public $instances = 0; + public int $instances = 0; - public $payload = [ + public array $payload = [ 'warming' => true ]; - public function __construct($instances = 0) + public function __construct(int $instances = 0) { $this->instances = $instances; } diff --git a/tests/Unit/ArchitectureTest.php b/tests/Unit/ArchitectureTest.php new file mode 100644 index 0000000..3984387 --- /dev/null +++ b/tests/Unit/ArchitectureTest.php @@ -0,0 +1,47 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit; + +use Hammerstone\Sidecar\Architecture; +use Hammerstone\Sidecar\ArchitectureConstants; + +class ArchitectureTest extends Base +{ + public function test_architecture_is_a_backed_enum() + { + $this->assertInstanceOf(\BackedEnum::class, Architecture::X86_64); + } + + public function test_x86_64_has_correct_value() + { + $this->assertEquals('x86_64', Architecture::X86_64->value); + } + + public function test_arm64_has_correct_value() + { + $this->assertEquals('arm64', Architecture::ARM_64->value); + } + + public function test_architecture_can_be_created_from_string() + { + $this->assertEquals(Architecture::X86_64, Architecture::from('x86_64')); + $this->assertEquals(Architecture::ARM_64, Architecture::from('arm64')); + } + + public function test_architecture_constants_class_exists_for_backwards_compatibility() + { + $this->assertTrue(class_exists(ArchitectureConstants::class)); + } + + public function test_architecture_constants_match_enum_values() + { + $this->assertEquals(Architecture::X86_64->value, ArchitectureConstants::X86_64); + $this->assertEquals(Architecture::ARM_64->value, ArchitectureConstants::ARM_64); + } +} diff --git a/tests/Unit/FunctionTest.php b/tests/Unit/FunctionTest.php index c1c5194..524619e 100644 --- a/tests/Unit/FunctionTest.php +++ b/tests/Unit/FunctionTest.php @@ -1,12 +1,18 @@ */ namespace Hammerstone\Sidecar\Tests\Unit; +use Hammerstone\Sidecar\Architecture; +use Hammerstone\Sidecar\Runtime; use Hammerstone\Sidecar\Tests\Unit\Support\EmptyTestFunction; +use Hammerstone\Sidecar\Tests\Unit\Support\EnumRuntimeTestFunction; +use Hammerstone\Sidecar\Tests\Unit\Support\StringRuntimeTestFunction; class FunctionTest extends Base { @@ -115,4 +121,73 @@ public function test_memory_and_timeout_and_storage_get_cast_to_ints() $this->assertSame(500, $array['MemorySize']); $this->assertSame(1024, $array['EphemeralStorage']['Size']); } + + public function test_default_runtime_returns_enum() + { + $function = new EmptyTestFunction; + + $this->assertInstanceOf(Runtime::class, $function->runtime()); + $this->assertEquals(Runtime::NODEJS_20, $function->runtime()); + } + + public function test_runtime_value_resolves_enum_to_string() + { + $function = new EnumRuntimeTestFunction; + + $this->assertInstanceOf(Runtime::class, $function->runtime()); + $this->assertEquals('python3.12', $function->runtimeValue()); + } + + public function test_runtime_value_passes_through_string() + { + $function = new StringRuntimeTestFunction; + + $this->assertIsString($function->runtime()); + $this->assertEquals('custom-runtime', $function->runtimeValue()); + } + + public function test_default_architecture_from_config() + { + config(['sidecar.architecture' => 'arm64']); + + $function = new EmptyTestFunction; + + $this->assertEquals('arm64', $function->architectureValue()); + } + + public function test_architecture_value_resolves_enum_to_string() + { + config(['sidecar.architecture' => Architecture::ARM_64]); + + $function = new EmptyTestFunction; + + $this->assertEquals('arm64', $function->architectureValue()); + } + + public function test_architecture_value_passes_through_string() + { + config(['sidecar.architecture' => 'x86_64']); + + $function = new EmptyTestFunction; + + $this->assertEquals('x86_64', $function->architectureValue()); + } + + public function test_deployment_array_contains_architecture() + { + config(['sidecar.architecture' => 'arm64']); + + $function = new EmptyTestFunction; + $array = $function->toDeploymentArray(); + + $this->assertEquals(['arm64'], $array['Architectures']); + } + + public function test_deployment_array_contains_runtime() + { + $function = new EmptyTestFunction; + $array = $function->toDeploymentArray(); + + $this->assertEquals('nodejs20.x', $array['Runtime']); + } } diff --git a/tests/Unit/LambdaClientTest.php b/tests/Unit/LambdaClientTest.php index 295dc0f..4e15c0a 100644 --- a/tests/Unit/LambdaClientTest.php +++ b/tests/Unit/LambdaClientTest.php @@ -7,7 +7,6 @@ namespace Hammerstone\Sidecar\Tests\Unit; use Aws\Lambda\Exception\LambdaException; -use Hammerstone\Sidecar\Architecture; use Hammerstone\Sidecar\Clients\LambdaClient; use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunction; use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunctionWithImage; @@ -180,9 +179,7 @@ public function test_update_existing_function() ], 'MemorySize' => 'test-MemorySize', 'Layers' => 'test-Layers', - 'Architectures' => [ - Architecture::X86_64 - ], + 'Architectures' => ['x86_64'], 'Tags' => [], ]); @@ -193,9 +190,7 @@ public function test_update_existing_function() 'S3Bucket' => 'test-bucket', 'S3Key' => 'test-key', 'Publish' => 'test-Publish', - 'Architectures' => [ - Architecture::X86_64 - ] + 'Architectures' => ['x86_64'] ]); $this->lambda->updateExistingFunction($function); @@ -222,9 +217,7 @@ public function test_update_existing_image_function() ], 'Layers' => [], 'PackageType' => 'Image', - 'Architectures' => [ - Architecture::X86_64 - ], + 'Architectures' => ['x86_64'], 'Tags' => [], ]); @@ -234,9 +227,7 @@ public function test_update_existing_image_function() 'FunctionName' => 'test-FunctionName', 'Publish' => 'test-Publish', 'ImageUri' => '123.dkr.ecr.us-west-2.amazonaws.com/image:latest', - 'Architectures' => [ - Architecture::X86_64 - ] + 'Architectures' => ['x86_64'] ]); $this->lambda->updateExistingFunction($function); diff --git a/tests/Unit/RuntimeTest.php b/tests/Unit/RuntimeTest.php new file mode 100644 index 0000000..46d0217 --- /dev/null +++ b/tests/Unit/RuntimeTest.php @@ -0,0 +1,102 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit; + +use Hammerstone\Sidecar\Runtime; +use Hammerstone\Sidecar\RuntimeConstants; + +class RuntimeTest extends Base +{ + public function test_runtime_is_a_backed_enum() + { + $this->assertInstanceOf(\BackedEnum::class, Runtime::NODEJS_20); + } + + public function test_nodejs_runtimes_have_correct_values() + { + $this->assertEquals('nodejs24.x', Runtime::NODEJS_24->value); + $this->assertEquals('nodejs22.x', Runtime::NODEJS_22->value); + $this->assertEquals('nodejs20.x', Runtime::NODEJS_20->value); + $this->assertEquals('nodejs18.x', Runtime::NODEJS_18->value); + $this->assertEquals('nodejs16.x', Runtime::NODEJS_16->value); + $this->assertEquals('nodejs14.x', Runtime::NODEJS_14->value); + } + + public function test_python_runtimes_have_correct_values() + { + $this->assertEquals('python3.14', Runtime::PYTHON_314->value); + $this->assertEquals('python3.13', Runtime::PYTHON_313->value); + $this->assertEquals('python3.12', Runtime::PYTHON_312->value); + $this->assertEquals('python3.11', Runtime::PYTHON_311->value); + $this->assertEquals('python3.10', Runtime::PYTHON_310->value); + $this->assertEquals('python3.9', Runtime::PYTHON_39->value); + $this->assertEquals('python3.8', Runtime::PYTHON_38->value); + $this->assertEquals('python3.7', Runtime::PYTHON_37->value); + } + + public function test_java_runtimes_have_correct_values() + { + $this->assertEquals('java25', Runtime::JAVA_25->value); + $this->assertEquals('java21', Runtime::JAVA_21->value); + $this->assertEquals('java17', Runtime::JAVA_17->value); + $this->assertEquals('java11', Runtime::JAVA_11->value); + $this->assertEquals('java8.al2', Runtime::JAVA_8_LINUX2->value); + $this->assertEquals('java8', Runtime::JAVA_8->value); + } + + public function test_dotnet_runtimes_have_correct_values() + { + $this->assertEquals('dotnet9', Runtime::DOT_NET_9->value); + $this->assertEquals('dotnet8', Runtime::DOT_NET_8->value); + $this->assertEquals('dotnet7', Runtime::DOT_NET_7->value); + $this->assertEquals('dotnet6', Runtime::DOT_NET_6->value); + } + + public function test_ruby_runtimes_have_correct_values() + { + $this->assertEquals('ruby3.4', Runtime::RUBY_34->value); + $this->assertEquals('ruby3.3', Runtime::RUBY_33->value); + $this->assertEquals('ruby3.2', Runtime::RUBY_32->value); + $this->assertEquals('ruby2.7', Runtime::RUBY_27->value); + } + + public function test_provided_runtimes_have_correct_values() + { + $this->assertEquals('provided.al2023', Runtime::PROVIDED_AL2023->value); + $this->assertEquals('provided.al2', Runtime::PROVIDED_AL2->value); + $this->assertEquals('provided', Runtime::PROVIDED->value); + } + + public function test_go_runtime_has_correct_value() + { + $this->assertEquals('go1.x', Runtime::GO_1X->value); + } + + public function test_runtime_can_be_created_from_string() + { + $this->assertEquals(Runtime::NODEJS_20, Runtime::from('nodejs20.x')); + $this->assertEquals(Runtime::PYTHON_312, Runtime::from('python3.12')); + } + + public function test_runtime_constants_class_exists_for_backwards_compatibility() + { + $this->assertTrue(class_exists(RuntimeConstants::class)); + } + + public function test_runtime_constants_match_enum_values() + { + $this->assertEquals(Runtime::NODEJS_24->value, RuntimeConstants::NODEJS_24); + $this->assertEquals(Runtime::NODEJS_22->value, RuntimeConstants::NODEJS_22); + $this->assertEquals(Runtime::NODEJS_20->value, RuntimeConstants::NODEJS_20); + $this->assertEquals(Runtime::PYTHON_312->value, RuntimeConstants::PYTHON_312); + $this->assertEquals(Runtime::JAVA_21->value, RuntimeConstants::JAVA_21); + $this->assertEquals(Runtime::DOT_NET_8->value, RuntimeConstants::DOT_NET_8); + $this->assertEquals(Runtime::RUBY_33->value, RuntimeConstants::RUBY_33); + } +} diff --git a/tests/Unit/Support/EnumRuntimeTestFunction.php b/tests/Unit/Support/EnumRuntimeTestFunction.php new file mode 100644 index 0000000..9ffc3a4 --- /dev/null +++ b/tests/Unit/Support/EnumRuntimeTestFunction.php @@ -0,0 +1,30 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit\Support; + +use Hammerstone\Sidecar\LambdaFunction; +use Hammerstone\Sidecar\Runtime; + +class EnumRuntimeTestFunction extends LambdaFunction +{ + public function handler() + { + return 'index.handler'; + } + + public function package() + { + return []; + } + + public function runtime(): Runtime|string + { + return Runtime::PYTHON_312; + } +} diff --git a/tests/Unit/Support/StringRuntimeTestFunction.php b/tests/Unit/Support/StringRuntimeTestFunction.php new file mode 100644 index 0000000..df81417 --- /dev/null +++ b/tests/Unit/Support/StringRuntimeTestFunction.php @@ -0,0 +1,30 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit\Support; + +use Hammerstone\Sidecar\LambdaFunction; +use Hammerstone\Sidecar\Runtime; + +class StringRuntimeTestFunction extends LambdaFunction +{ + public function handler() + { + return 'index.handler'; + } + + public function package() + { + return []; + } + + public function runtime(): Runtime|string + { + return 'custom-runtime'; + } +}