second first commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
bin/*.d.ts
|
||||
bin/*.js
|
||||
bin/*.js.map
|
||||
|
||||
lib/*.d.ts
|
||||
lib/*.js
|
||||
lib/*.js.map
|
||||
|
||||
docker-fns/*.d.ts
|
||||
docker-fns/*.js
|
||||
docker-fns/*.js.map
|
||||
|
||||
node_modules/
|
||||
coverage/
|
||||
cdk.out/
|
||||
cdk.context.json
|
||||
@@ -0,0 +1,37 @@
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npx tsc
|
||||
|
||||
FROM node:22-alpine
|
||||
RUN apk add --no-cache aws-cli bash python3 git jq docker-cli
|
||||
WORKDIR /app
|
||||
|
||||
# Install bats and helpers
|
||||
RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /opt/bats-core \
|
||||
&& git clone --depth 1 https://github.com/bats-core/bats-support.git /opt/bats-support \
|
||||
&& git clone --depth 1 https://github.com/bats-core/bats-assert.git /opt/bats-assert
|
||||
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/bin ./bin
|
||||
COPY --from=builder /app/lib ./lib
|
||||
COPY package.json cdk.json tsconfig.json ./
|
||||
COPY docker-fn/ docker-fn/
|
||||
COPY run.sh ./
|
||||
COPY run-bats-in-container.sh ./
|
||||
COPY test/ test/
|
||||
|
||||
ENV PATH="/app/node_modules/.bin:$PATH"
|
||||
ENV BATS_LIB_PATH=/opt
|
||||
ENV FLOCI_ENDPOINT=http://floci:4566
|
||||
ENV AWS_ENDPOINT_URL=http://floci:4566
|
||||
ENV AWS_ACCESS_KEY_ID=test
|
||||
ENV AWS_SECRET_ACCESS_KEY=test
|
||||
ENV AWS_DEFAULT_REGION=us-east-1
|
||||
ENV LOCALSTACK_HOSTNAME=floci
|
||||
ENV EDGE_PORT=4566
|
||||
|
||||
RUN chmod +x /app/run-bats-in-container.sh && mkdir -p /results
|
||||
ENTRYPOINT ["./run-bats-in-container.sh"]
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
import * as cdk from 'aws-cdk-lib';
|
||||
import { FlociTestStack } from '../lib/floci-stack';
|
||||
|
||||
const app = new cdk.App();
|
||||
new FlociTestStack(app, 'FlociTestStack', {
|
||||
env: {
|
||||
account: '000000000000',
|
||||
region: 'us-east-1',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"app": "node bin/app.js",
|
||||
"context": {
|
||||
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM public.ecr.aws/lambda/nodejs:20
|
||||
COPY index.js ${LAMBDA_TASK_ROOT}/
|
||||
CMD ["index.handler"]
|
||||
@@ -0,0 +1,10 @@
|
||||
exports.handler = async (event) => {
|
||||
const name = (event && event.name) ? event.name : "Floci";
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({
|
||||
message: `Hello, ${name}! (from CDK DockerImageFunction via emulated ECR)`,
|
||||
received: event,
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
import * as cdk from 'aws-cdk-lib';
|
||||
import * as s3 from 'aws-cdk-lib/aws-s3';
|
||||
import * as sqs from 'aws-cdk-lib/aws-sqs';
|
||||
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
|
||||
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
||||
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
||||
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
||||
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
|
||||
import * as path from 'path';
|
||||
import { Construct } from 'constructs';
|
||||
|
||||
export class FlociTestStack extends cdk.Stack {
|
||||
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||
super(scope, id, props);
|
||||
|
||||
const bucket = new s3.Bucket(this, 'TestBucket', {
|
||||
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
||||
});
|
||||
|
||||
const queue = new sqs.Queue(this, 'TestQueue', {
|
||||
queueName: 'floci-cdk-test-queue',
|
||||
visibilityTimeout: cdk.Duration.seconds(30),
|
||||
});
|
||||
|
||||
const table = new dynamodb.TableV2(this, 'TestTable', {
|
||||
tableName: 'floci-cdk-test-table',
|
||||
partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
|
||||
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
||||
});
|
||||
|
||||
// DynamoDB table with GSI and LSI — validates CloudFormation index provisioning
|
||||
const indexTable = new dynamodb.TableV2(this, 'IndexTestTable', {
|
||||
tableName: 'floci-cdk-index-table',
|
||||
partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
|
||||
sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
|
||||
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
||||
globalSecondaryIndexes: [
|
||||
{
|
||||
indexName: 'gsi-1',
|
||||
partitionKey: { name: 'gsiPk', type: dynamodb.AttributeType.STRING },
|
||||
sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
|
||||
projectionType: dynamodb.ProjectionType.ALL,
|
||||
},
|
||||
],
|
||||
localSecondaryIndexes: [
|
||||
{
|
||||
indexName: 'lsi-1',
|
||||
sortKey: { name: 'lsiSk', type: dynamodb.AttributeType.STRING },
|
||||
projectionType: dynamodb.ProjectionType.KEYS_ONLY,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const generatedSecret = new secretsmanager.CfnSecret(this, 'GeneratedSecret', {
|
||||
name: 'floci-cdk-generated-secret',
|
||||
generateSecretString: {
|
||||
secretStringTemplate: '{"username":"admin"}',
|
||||
generateStringKey: 'password',
|
||||
passwordLength: 24,
|
||||
excludeCharacters: 'abc',
|
||||
},
|
||||
});
|
||||
generatedSecret.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
|
||||
|
||||
// Custom Docker image Lambda — exercises ECR emulation end-to-end:
|
||||
// CDK builds the local Dockerfile, cdk-assets pushes it to the bootstrap
|
||||
// ECR repository (via Floci's emulated ECR + registry:2), and Floci's
|
||||
// Lambda runner pulls the image at invoke time.
|
||||
new lambda.DockerImageFunction(this, 'DockerHelloFn', {
|
||||
functionName: 'floci-cdk-docker-hello',
|
||||
code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '..', 'docker-fn')),
|
||||
timeout: cdk.Duration.seconds(15),
|
||||
memorySize: 256,
|
||||
});
|
||||
|
||||
// VPC + internal ALB — regression for issue #1297: CloudFormation must create the
|
||||
// EC2 VPC/subnets for real (in EC2), otherwise the ALB that references the
|
||||
// CDK-generated subnets fails with "The subnet ID '...' does not exist".
|
||||
const vpc = new ec2.Vpc(this, 'TestVpc', {
|
||||
vpcName: 'floci-cdk-vpc',
|
||||
maxAzs: 2,
|
||||
natGateways: 0,
|
||||
subnetConfiguration: [
|
||||
{ name: 'public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 },
|
||||
{ name: 'isolated', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 24 },
|
||||
],
|
||||
});
|
||||
|
||||
const alb = new elbv2.ApplicationLoadBalancer(this, 'TestAlb', {
|
||||
loadBalancerName: 'floci-cdk-alb',
|
||||
vpc,
|
||||
internetFacing: false,
|
||||
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
|
||||
});
|
||||
|
||||
new cdk.CfnOutput(this, 'BucketName', { value: bucket.bucketName });
|
||||
new cdk.CfnOutput(this, 'QueueUrl', { value: queue.queueUrl });
|
||||
new cdk.CfnOutput(this, 'TableName', { value: table.tableName });
|
||||
new cdk.CfnOutput(this, 'IndexTableName', { value: indexTable.tableName });
|
||||
new cdk.CfnOutput(this, 'GeneratedSecretName', { value: generatedSecret.name || 'floci-cdk-generated-secret' });
|
||||
new cdk.CfnOutput(this, 'VpcId', { value: vpc.vpcId });
|
||||
new cdk.CfnOutput(this, 'AlbArn', { value: alb.loadBalancerArn });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "compat-cdk",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"bin": {
|
||||
"app": "bin/app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"cdk": "cdk"
|
||||
},
|
||||
"dependencies": {
|
||||
"aws-cdk-lib": "2.264.0",
|
||||
"constructs": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.5.2",
|
||||
"aws-cdk": "2.1135.1",
|
||||
"aws-cdk-local": "^3.0.4",
|
||||
"typescript": "~5.7.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
report_dir="$(mktemp -d /tmp/bats-junit-XXXXXX)"
|
||||
trap 'rm -rf "$report_dir"' EXIT
|
||||
|
||||
set +e
|
||||
/opt/bats-core/bin/bats --report-formatter junit -o "$report_dir" test/
|
||||
status=$?
|
||||
set -e
|
||||
|
||||
if [ -f "$report_dir/report.xml" ]; then
|
||||
mv "$report_dir/report.xml" /results/junit.xml
|
||||
fi
|
||||
|
||||
exit "$status"
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Environment setup for Docker container
|
||||
export AWS_REGION=us-east-1
|
||||
export AWS_DEFAULT_REGION=us-east-1
|
||||
export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-test}"
|
||||
export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-test}"
|
||||
export FLOCI_ENDPOINT="${FLOCI_ENDPOINT:-http://localhost:4566}"
|
||||
export AWS_ENDPOINT_URL="$FLOCI_ENDPOINT"
|
||||
export AWS_ENDPOINT_URL_S3="$FLOCI_ENDPOINT"
|
||||
|
||||
# CDK-specific: derive hostname and port from the endpoint URL.
|
||||
read -r LOCALSTACK_HOSTNAME EDGE_PORT < <(python3 - "$FLOCI_ENDPOINT" <<'PY'
|
||||
import sys
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
endpoint = sys.argv[1]
|
||||
parsed = urlsplit(endpoint if "://" in endpoint else "//" + endpoint)
|
||||
hostname = parsed.hostname
|
||||
if not hostname:
|
||||
raise SystemExit(f"Cannot determine hostname from FLOCI_ENDPOINT: {endpoint}")
|
||||
try:
|
||||
port = parsed.port or 4566
|
||||
except ValueError as error:
|
||||
raise SystemExit(f"Invalid port in FLOCI_ENDPOINT: {endpoint}") from error
|
||||
print(hostname, port)
|
||||
PY
|
||||
)
|
||||
export LOCALSTACK_HOSTNAME EDGE_PORT
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Ensure bats is available
|
||||
if [ ! -d "$REPO_ROOT/lib/bats-core" ]; then
|
||||
echo "Error: bats-core not found. Run 'just setup-bats' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run bats tests
|
||||
exec "$REPO_ROOT/lib/run-bats-with-junit.sh" \
|
||||
"$SCRIPT_DIR/test/" \
|
||||
"${BATS_JUNIT_XML:-$SCRIPT_DIR/test-results/junit.xml}"
|
||||
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/env bats
|
||||
# CDK Compatibility Tests for floci
|
||||
|
||||
setup_file() {
|
||||
load 'test_helper/common-setup'
|
||||
|
||||
cd "$CDK_DIR"
|
||||
|
||||
echo "# === CDK Compatibility Test ===" >&3
|
||||
echo "# Endpoint: $FLOCI_ENDPOINT" >&3
|
||||
|
||||
echo "# --- Bootstrap ---" >&3
|
||||
run cdklocal bootstrap --force
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo "# Bootstrap failed: $output" >&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "# --- Deploy ---" >&3
|
||||
run cdklocal deploy --require-approval never
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo "# Deploy failed: $output" >&3
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
load 'test_helper/common-setup'
|
||||
|
||||
cd "$CDK_DIR"
|
||||
|
||||
echo "# --- Destroy ---" >&3
|
||||
cdklocal destroy --force || true
|
||||
}
|
||||
|
||||
setup() {
|
||||
load 'test_helper/common-setup'
|
||||
}
|
||||
|
||||
# --- Spot Checks ---
|
||||
|
||||
@test "CDK: S3 bucket exists" {
|
||||
run aws_cmd s3 ls
|
||||
assert_success
|
||||
bucket_count=$(echo "$output" | wc -l)
|
||||
[ "$bucket_count" -gt 0 ]
|
||||
}
|
||||
|
||||
@test "CDK: SQS queue exists" {
|
||||
run aws_cmd sqs list-queues
|
||||
assert_success
|
||||
queue_count=$(json_get "$output" '.QueueUrls | length')
|
||||
[ "$queue_count" -gt 0 ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB table exists" {
|
||||
run aws_cmd dynamodb list-tables
|
||||
assert_success
|
||||
table_count=$(json_get "$output" '.TableNames | length')
|
||||
[ "$table_count" -gt 0 ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB GSI exists on index table" {
|
||||
run aws_cmd dynamodb describe-table --table-name floci-cdk-index-table
|
||||
assert_success
|
||||
gsi_count=$(json_get "$output" '.Table.GlobalSecondaryIndexes | length')
|
||||
[ "$gsi_count" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB GSI name is gsi-1" {
|
||||
run aws_cmd dynamodb describe-table --table-name floci-cdk-index-table
|
||||
assert_success
|
||||
gsi_name=$(json_get "$output" '.Table.GlobalSecondaryIndexes[0].IndexName')
|
||||
[ "$gsi_name" = "gsi-1" ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB GSI projection ALL" {
|
||||
run aws_cmd dynamodb describe-table --table-name floci-cdk-index-table
|
||||
assert_success
|
||||
gsi_proj=$(json_get "$output" '.Table.GlobalSecondaryIndexes[0].Projection.ProjectionType')
|
||||
[ "$gsi_proj" = "ALL" ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB LSI exists on index table" {
|
||||
run aws_cmd dynamodb describe-table --table-name floci-cdk-index-table
|
||||
assert_success
|
||||
lsi_count=$(json_get "$output" '.Table.LocalSecondaryIndexes | length')
|
||||
[ "$lsi_count" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB LSI name is lsi-1" {
|
||||
run aws_cmd dynamodb describe-table --table-name floci-cdk-index-table
|
||||
assert_success
|
||||
lsi_name=$(json_get "$output" '.Table.LocalSecondaryIndexes[0].IndexName')
|
||||
[ "$lsi_name" = "lsi-1" ]
|
||||
}
|
||||
|
||||
@test "CDK: DynamoDB LSI projection KEYS_ONLY" {
|
||||
run aws_cmd dynamodb describe-table --table-name floci-cdk-index-table
|
||||
assert_success
|
||||
lsi_proj=$(json_get "$output" '.Table.LocalSecondaryIndexes[0].Projection.ProjectionType')
|
||||
[ "$lsi_proj" = "KEYS_ONLY" ]
|
||||
}
|
||||
|
||||
@test "CDK: CloudFormation stack CREATE_COMPLETE" {
|
||||
run aws_cmd cloudformation describe-stacks --stack-name FlociTestStack
|
||||
assert_success
|
||||
stack_status=$(json_get "$output" '.Stacks[0].StackStatus')
|
||||
[ "$stack_status" = "CREATE_COMPLETE" ]
|
||||
}
|
||||
|
||||
@test "CDK: Secrets Manager generated secret exists" {
|
||||
run aws_cmd secretsmanager describe-secret --secret-id floci-cdk-generated-secret
|
||||
assert_success
|
||||
secret_name=$(json_get "$output" '.Name')
|
||||
[ "$secret_name" = "floci-cdk-generated-secret" ]
|
||||
}
|
||||
|
||||
@test "CDK: GeneratedSecret username is admin" {
|
||||
run aws_cmd secretsmanager get-secret-value --secret-id floci-cdk-generated-secret
|
||||
assert_success
|
||||
username=$(json_get "$output" '.SecretString' | jq -r '.username')
|
||||
[ "$username" = "admin" ]
|
||||
}
|
||||
|
||||
@test "CDK: GeneratedSecret password length is 24" {
|
||||
run aws_cmd secretsmanager get-secret-value --secret-id floci-cdk-generated-secret
|
||||
assert_success
|
||||
password=$(json_get "$output" '.SecretString' | jq -r '.password')
|
||||
password_len=${#password}
|
||||
[ "$password_len" -eq 24 ]
|
||||
}
|
||||
|
||||
@test "CDK: GeneratedSecret password excludes abc" {
|
||||
run aws_cmd secretsmanager get-secret-value --secret-id floci-cdk-generated-secret
|
||||
assert_success
|
||||
password=$(json_get "$output" '.SecretString' | jq -r '.password')
|
||||
# Check that password does not contain a, b, or c
|
||||
[[ ! "$password" =~ [abc] ]]
|
||||
}
|
||||
|
||||
# --- DockerImageFunction (ECR + Lambda end-to-end) ---
|
||||
|
||||
@test "CDK: DockerImageFunction was created" {
|
||||
run aws_cmd lambda get-function --function-name floci-cdk-docker-hello
|
||||
assert_success
|
||||
package_type=$(json_get "$output" '.Configuration.PackageType')
|
||||
[ "$package_type" = "Image" ]
|
||||
image_uri=$(json_get "$output" '.Code.ImageUri')
|
||||
[ -n "$image_uri" ]
|
||||
# The stored ImageUri preserves the AWS-shaped form (CDK assets use this);
|
||||
# Floci's Lambda launcher rewrites it to the loopback registry at pull time.
|
||||
[[ "$image_uri" == *"dkr.ecr."*"amazonaws.com"* ]]
|
||||
[[ "$image_uri" == *"cdk-hnb659fds-container-assets"* ]]
|
||||
}
|
||||
|
||||
@test "CDK: DockerImageFunction invokes successfully" {
|
||||
# End-to-end invocation requires the Lambda runtime API server (host port range
|
||||
# floci.services.lambda.runtime-api-base-port) to be reachable from inside the
|
||||
# Lambda container. On native Linux Docker without Docker Desktop, this depends
|
||||
# on host firewall rules permitting traffic from the docker bridge to the host.
|
||||
# If your host blocks this, the test fails with Function.TimedOut — this is a
|
||||
# pre-existing Lambda networking constraint, not an ECR issue (it affects
|
||||
# Zip-packaged Lambdas too).
|
||||
payload_b64=$(printf '{"name":"world"}' | base64 -w 0 2>/dev/null || printf '{"name":"world"}' | base64)
|
||||
run aws_cmd lambda invoke \
|
||||
--function-name floci-cdk-docker-hello \
|
||||
--payload "$payload_b64" \
|
||||
/tmp/floci-cdk-docker-hello.out
|
||||
assert_success
|
||||
status_code=$(json_get "$output" '.StatusCode')
|
||||
[ "$status_code" = "200" ]
|
||||
|
||||
body=$(cat /tmp/floci-cdk-docker-hello.out)
|
||||
[[ "$body" == *"Hello, world!"* ]]
|
||||
[[ "$body" == *"emulated ECR"* ]]
|
||||
}
|
||||
|
||||
@test "CDK: DockerImageFunction asset image is in emulated ECR" {
|
||||
# CDK pushes to a bootstrap repo named cdk-hnb659fds-container-assets-<account>-<region>
|
||||
run aws_cmd ecr describe-repositories
|
||||
assert_success
|
||||
repos=$(echo "$output" | jq -r '.repositories[].repositoryName')
|
||||
[[ "$repos" == *"cdk-hnb659fds-container-assets"* ]]
|
||||
}
|
||||
|
||||
# --- VPC + ALB (regression: issue #1297) ---
|
||||
#
|
||||
# CloudFormation must create the CDK-generated AWS::EC2::VPC/Subnet resources for
|
||||
# real (in EC2). Before the fix they were stubbed, so describe-subnets did not
|
||||
# return them and the ALB referencing them failed with "The subnet ID '...' does
|
||||
# not exist". https://github.com/floci-io/floci/issues/1297
|
||||
|
||||
@test "CDK: VPC subnets were created in EC2" {
|
||||
run aws_cmd ec2 describe-subnets
|
||||
assert_success
|
||||
# The CDK VPC adds 4 subnets (2 public + 2 isolated) on top of the defaults.
|
||||
custom_subnets=$(echo "$output" | jq '[.Subnets[] | select(.DefaultForAz != true)] | length')
|
||||
[ "$custom_subnets" -ge 4 ]
|
||||
}
|
||||
|
||||
@test "CDK: internal ALB is active" {
|
||||
run aws_cmd elbv2 describe-load-balancers --names floci-cdk-alb
|
||||
assert_success
|
||||
state=$(json_get "$output" '.LoadBalancers[0].State.Code')
|
||||
[ "$state" = "active" ]
|
||||
scheme=$(json_get "$output" '.LoadBalancers[0].Scheme')
|
||||
[ "$scheme" = "internal" ]
|
||||
}
|
||||
|
||||
@test "CDK: ALB subnets exist in EC2" {
|
||||
run aws_cmd elbv2 describe-load-balancers --names floci-cdk-alb
|
||||
assert_success
|
||||
s1=$(json_get "$output" '.LoadBalancers[0].AvailabilityZones[0].SubnetId')
|
||||
s2=$(json_get "$output" '.LoadBalancers[0].AvailabilityZones[1].SubnetId')
|
||||
[[ "$s1" == subnet-* ]]
|
||||
[[ "$s2" == subnet-* ]]
|
||||
|
||||
run aws_cmd ec2 describe-subnets --subnet-ids "$s1" "$s2"
|
||||
assert_success
|
||||
count=$(json_get "$output" '.Subnets | length')
|
||||
[ "$count" -eq 2 ]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
@test "run.sh derives CDK host and port from a URL endpoint" {
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
mkdir -p "$workdir/compat-cdk" "$workdir/lib/bats-core"
|
||||
cp "$BATS_TEST_DIRNAME/../run.sh" "$workdir/compat-cdk/run.sh"
|
||||
cat > "$workdir/lib/run-bats-with-junit.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s|%s\n' "$LOCALSTACK_HOSTNAME" "$EDGE_PORT"
|
||||
EOF
|
||||
chmod +x "$workdir/lib/run-bats-with-junit.sh"
|
||||
|
||||
run env FLOCI_ENDPOINT="https://[::1]:8443/floci" bash "$workdir/compat-cdk/run.sh"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "::1|8443" ]
|
||||
}
|
||||
|
||||
@test "run.sh uses the default port for an endpoint without one" {
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
mkdir -p "$workdir/compat-cdk" "$workdir/lib/bats-core"
|
||||
cp "$BATS_TEST_DIRNAME/../run.sh" "$workdir/compat-cdk/run.sh"
|
||||
cat > "$workdir/lib/run-bats-with-junit.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s|%s\n' "$LOCALSTACK_HOSTNAME" "$EDGE_PORT"
|
||||
EOF
|
||||
chmod +x "$workdir/lib/run-bats-with-junit.sh"
|
||||
|
||||
run env FLOCI_ENDPOINT="floci" bash "$workdir/compat-cdk/run.sh"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "floci|4566" ]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# Common setup for CDK bats tests
|
||||
|
||||
CDK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
# Load bats helpers - support both local and Docker environments
|
||||
if [[ -d "${CDK_DIR}/../lib/bats-support" ]]; then
|
||||
load "${CDK_DIR}/../lib/bats-support/load"
|
||||
load "${CDK_DIR}/../lib/bats-assert/load"
|
||||
elif [[ -d "${CDK_DIR}/lib/bats-support" ]]; then
|
||||
load "${CDK_DIR}/lib/bats-support/load"
|
||||
load "${CDK_DIR}/lib/bats-assert/load"
|
||||
elif [[ -n "${BATS_LIB_PATH:-}" ]]; then
|
||||
load "${BATS_LIB_PATH}/bats-support/load"
|
||||
load "${BATS_LIB_PATH}/bats-assert/load"
|
||||
else
|
||||
echo "Error: Cannot find bats-support/bats-assert libraries" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Shared test helpers kept local to this module so Docker and local runs behave the same.
|
||||
export FLOCI_ENDPOINT="${FLOCI_ENDPOINT:-http://localhost:4566}"
|
||||
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}"
|
||||
export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-test}"
|
||||
export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-test}"
|
||||
export AWS_ENDPOINT_URL="$FLOCI_ENDPOINT"
|
||||
# aws-cdk-local v3 requires AWS_ENDPOINT_URL_S3; use Floci's virtual-hosted S3 domain so
|
||||
# <bucket>.s3.localhost.floci.io resolves (public DNS locally; Floci's embedded DNS in Docker).
|
||||
export AWS_ENDPOINT_URL_S3="${AWS_ENDPOINT_URL_S3:-http://s3.localhost.floci.io:4566}"
|
||||
|
||||
aws_cmd() {
|
||||
aws --endpoint-url "$FLOCI_ENDPOINT" --region "$AWS_DEFAULT_REGION" --output json "$@"
|
||||
}
|
||||
|
||||
json_get() {
|
||||
local json="$1"
|
||||
local path="$2"
|
||||
echo "$json" | jq -r "$path" 2>/dev/null || echo ""
|
||||
}
|
||||
|
||||
# CDK-specific environment
|
||||
export LOCALSTACK_HOSTNAME="${LOCALSTACK_HOSTNAME:-localhost}"
|
||||
export EDGE_PORT="${EDGE_PORT:-4566}"
|
||||
|
||||
# Override endpoint for Docker networking if needed
|
||||
if [ "$LOCALSTACK_HOSTNAME" = "floci" ]; then
|
||||
export FLOCI_ENDPOINT="http://floci:4566"
|
||||
export AWS_ENDPOINT_URL="http://floci:4566"
|
||||
fi
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"outDir": "./",
|
||||
"rootDir": "./",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"experimentalDecorators": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"typeRoots": ["./node_modules/@types"]
|
||||
},
|
||||
"exclude": ["node_modules", "cdk.out"]
|
||||
}
|
||||
Reference in New Issue
Block a user