Skip to content
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
17 changes: 17 additions & 0 deletions src/handlers/project/add/gateway/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { afterEach, describe, expect, test } from "bun:test";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expectError } from "../../../../testing";
import { createGatewayProjectTestHarness } from "../gateway-test-support";
import { InputValidationError, ResourceNotFoundError } from "../../../../errors";
Expand Down Expand Up @@ -152,6 +154,21 @@ describe("project add gateway", () => {
});
});

test("keeps a hyphenated target as is in the hyphen-separated deployed name", async () => {
const projectRoot = await inProject();
await writeFile(
join(projectRoot, "agentcore", "aws-targets.json"),
JSON.stringify([{ name: "prod-us1", account: "111122223333", region: "us-east-1" }]),
);
const name = `g${"x".repeat(80)}`;

await expectError(
run(["add", "gateway", "--name", name]),
`Gateway deployed name 'TestProject-prod-us1-${name}' is 102 characters. The maximum is 100.`,
InputValidationError,
);
});

test.each<
[label: string, args: string[], message: string, errorType?: Parameters<typeof expectError>[2]]
>([
Expand Down
13 changes: 13 additions & 0 deletions src/handlers/project/add/policy-engine/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ describe("project add policy-engine", () => {
);
});

test("writes a hyphenated target with underscores in the deployed name", async () => {
const projectRoot = await inProject();
await writeFile(
join(projectRoot, "agentcore", "aws-targets.json"),
JSON.stringify([{ name: "prod-us1", account: "111122223333", region: "us-east-1" }]),
);
const name = `E${"x".repeat(28)}`;

await expect(run(["add", "policy-engine", "--name", name])).rejects.toThrow(
`Policy Engine deployed name 'TestProject_prod_us1_${name}' is 50 characters. The maximum is 48.`,
);
});

test.each([
["defaults to enforce", [], "ENFORCE"],
["honors --attach-mode log-only", ["--attach-mode", "log-only"], "LOG_ONLY"],
Expand Down
5 changes: 3 additions & 2 deletions src/handlers/project/add/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type AddProjectResourceResultOptions = {
};

/**
The L3 deploys `<project><sep><target><sep><name>` and fails at synth past the service cap. This fails at add time using the longest declared target, or "default" when none is declared yet.
The L3 deploys `<project><sep><target><sep><name>` and fails at synth past the service cap. This fails at add time using the longest declared target, or "default" when none is declared yet. With the `_` separator the L3 writes the target's hyphens as underscores, which keeps the length.
**/
export function requireDeployedNameFits(
kind: string,
Expand All @@ -50,7 +50,8 @@ export function requireDeployedNameFits(
const longest = [DEFAULT_TARGET_NAME, ...targets.map((target) => target.name)].reduce((a, b) =>
b.length > a.length ? b : a,
);
const deployed = [projectName, longest, name].join(separator);
const target = separator === "_" ? longest.replaceAll("-", "_") : longest;
const deployed = [projectName, target, name].join(separator);
if (deployed.length <= maxLength) return;
throw new InputValidationError(
`${kind} deployed name '${deployed}' is ${deployed.length} characters. The maximum is ${maxLength}. The deployed name is <project>${separator}<target>${separator}<name>, checked with the longest declared target '${longest}'. Shorten the ${kind.toLowerCase()} name.`,
Expand Down
25 changes: 13 additions & 12 deletions src/projectSchemas/aws-targets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,19 @@ describe("AWS deployment targets", () => {
expect(AwsAccountIdSchema.safeParse(account).success).toBe(false);
});

test.each([
"",
"1default",
"-default",
"_default",
"has_underscore",
"has-hyphen",
"has spaces",
"has.dots",
])("rejects invalid target name %j", (name) => {
expect(DeploymentTargetNameSchema.safeParse(name).success).toBe(false);
});
test.each(["", "1default", "-default", "_default", "has_underscore", "has spaces", "has.dots"])(
"rejects invalid target name %j",
(name) => {
expect(DeploymentTargetNameSchema.safeParse(name).success).toBe(false);
},
);

test.each(["prod-us", "a-b-c", "us-east-1", "trailing-"])(
"accepts hyphenated target name %j, as the v0 CLI did",
(name) => {
expect(DeploymentTargetNameSchema.safeParse(name).success).toBe(true);
},
);

test("enforces target name and description lengths", () => {
expect(DeploymentTargetNameSchema.safeParse("a".repeat(64)).success).toBe(true);
Expand Down
6 changes: 3 additions & 3 deletions src/projectSchemas/aws-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export const DeploymentTargetNameSchema = z
.min(1)
.max(64)
/**
Letters and digits only: the target is the middle segment of every deployed resource name, so a separator inside it would make two targets compose the same name.
The v0 CLI's rule, so v0 targets such as `prod-us` keep working. The target is the middle segment of deployed resource names (`<project><sep><target><sep><name>`). Where the separator is `_` because the service disallows `-` (Runtime, Memory, Harness, ...), the CDK library writes the target's hyphens as underscores; `-` and `/` separated names (Gateway, ECR, Lambda, ...) and the `AgentCore-<project>-<target>` stack name keep them.
**/
.regex(
/^[a-zA-Z][a-zA-Z0-9]*$/,
"Name must start with a letter and contain only letters and digits",
/^[a-zA-Z][a-zA-Z0-9-]*$/,
"Name must start with a letter and contain only letters, digits, and hyphens",
)
.describe("Unique identifier for the deployment target");

Expand Down
Loading