t3-code-android-nightly/.repos/alchemy-effect/packages/alchemy/test/AWS/AppRunner/AutoScalingConfiguration.test.ts
Julius Marminge 6f9cea00ae
chore(refs): sync Effect and Alchemy references to 4.0.1 and beta.80 (#16170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:22:30 -07:00

132 lines
4.9 KiB
TypeScript

import * as AWS from "@/AWS";
import { AutoScalingConfiguration } from "@/AWS/AppRunner";
import * as Test from "@/Test/Alchemy";
import * as apprunner from "@distilled.cloud/aws/apprunner";
import * as sts from "@distilled.cloud/aws/sts";
import { expect } from "alchemy-test";
import * as Effect from "effect/Effect";
import * as Schedule from "effect/Schedule";
const { test } = Test.make({ providers: AWS.providers() });
// Ungated typed-error probe: proves the distilled error union carries the
// not-found tag this provider's read/delete paths depend on.
test.provider(
"describeAutoScalingConfiguration on a nonexistent ARN fails with ResourceNotFoundException",
() =>
Effect.gen(function* () {
const { Account } = yield* sts.getCallerIdentity({});
const error = yield* Effect.flip(
apprunner.describeAutoScalingConfiguration({
AutoScalingConfigurationArn: `arn:aws:apprunner:us-west-2:${Account}:autoscalingconfiguration/alchemy-nonexistent-probe/1/00000000000000000000000000000000`,
}),
);
expect(error._tag).toBe("ResourceNotFoundException");
}),
{ tags: ["provider:aws", "provider:aws:apprunner", "live"] },
);
// No ACTIVE revision of the named configuration remains (deleted configs
// linger as INACTIVE for observability).
const assertConfigGone = (name: string) =>
Effect.gen(function* () {
const page = yield* apprunner.listAutoScalingConfigurations({
AutoScalingConfigurationName: name,
});
// App Runner returns lowercase statuses ("active") despite documenting
// uppercase — compare case-insensitively.
const active = (page.AutoScalingConfigurationSummaryList ?? []).filter(
(s) =>
s.AutoScalingConfigurationName === name &&
s.Status?.toUpperCase() === "ACTIVE",
);
if (active.length > 0) {
return yield* Effect.fail(
new Error(
`Auto scaling configuration '${name}' still has ${active.length} ACTIVE revision(s)`,
),
);
}
}).pipe(
Effect.retry({
schedule: Schedule.max([Schedule.fixed("3 seconds"), Schedule.recurs(8)]),
}),
);
// Auto scaling configurations are immutable revisions — fast and free, so
// the full lifecycle (create, no-op, revise, replace-by-name, destroy)
// runs ungated.
test.provider(
"create, revise, replace, and destroy an auto scaling configuration",
(stack) =>
Effect.gen(function* () {
// Clean slate in case a previous run died mid-flight.
yield* stack.destroy();
const props = {
autoScalingConfigurationName: "alchemy-test-asc",
maxConcurrency: 50,
minSize: 1,
maxSize: 3,
};
// Create.
const created = yield* stack.deploy(
AutoScalingConfiguration("Asc", props),
);
expect(created.autoScalingConfigurationName).toBe("alchemy-test-asc");
expect(created.autoScalingConfigurationArn).toContain(
":autoscalingconfiguration/alchemy-test-asc/",
);
expect(created.maxConcurrency).toBe(50);
expect(created.maxSize).toBe(3);
// Out-of-band verification via distilled.
const described = yield* apprunner.describeAutoScalingConfiguration({
AutoScalingConfigurationArn: created.autoScalingConfigurationArn,
});
expect(described.AutoScalingConfiguration.Status?.toUpperCase()).toBe(
"ACTIVE",
);
expect(described.AutoScalingConfiguration.MaxConcurrency).toBe(50);
expect(described.AutoScalingConfiguration.MinSize).toBe(1);
expect(described.AutoScalingConfiguration.MaxSize).toBe(3);
// No-op redeploy must not create a new revision.
const noop = yield* stack.deploy(AutoScalingConfiguration("Asc", props));
expect(noop.autoScalingConfigurationRevision).toBe(
created.autoScalingConfigurationRevision,
);
// Settings change → new revision under the same name.
const revised = yield* stack.deploy(
AutoScalingConfiguration("Asc", { ...props, maxConcurrency: 80 }),
);
expect(revised.autoScalingConfigurationName).toBe("alchemy-test-asc");
expect(revised.autoScalingConfigurationRevision).toBe(
created.autoScalingConfigurationRevision + 1,
);
expect(revised.autoScalingConfigurationArn).not.toBe(
created.autoScalingConfigurationArn,
);
expect(revised.maxConcurrency).toBe(80);
// Name change → replace; every revision of the old name is deleted.
const replaced = yield* stack.deploy(
AutoScalingConfiguration("Asc", {
...props,
autoScalingConfigurationName: "alchemy-test-asc-b",
}),
);
expect(replaced.autoScalingConfigurationName).toBe("alchemy-test-asc-b");
yield* assertConfigGone("alchemy-test-asc");
// Destroy and verify deletion out-of-band.
yield* stack.destroy();
yield* assertConfigGone("alchemy-test-asc-b");
}),
{
tags: ["provider:aws", "provider:aws:apprunner", "live"],
timeout: 120_000,
},
);