@kensio/yulin 1.21.7 → 1.21.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +22 -2
  2. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +2 -0
  3. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +1 -1
  4. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts +2 -0
  5. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +4 -0
  6. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -5
  7. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.d.ts +2 -0
  8. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js +2 -0
  9. package/dist/service/lambda/command/event-source-mapping/sim-lambda-event-source-mapping-commands.d.ts +2 -0
  10. package/dist/service/lambda/destination/sim-lambda-destination-targets.d.ts +2 -1
  11. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.d.ts +0 -4
  12. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +10 -15
  13. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.d.ts +1 -6
  14. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.js +9 -27
  15. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.d.ts +19 -0
  16. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.js +27 -0
  17. package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.d.ts +1 -1
  18. package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.js +3 -1
  19. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.d.ts +13 -0
  20. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.js +27 -0
  21. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.d.ts +15 -0
  22. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.js +26 -0
  23. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.d.ts +1 -0
  24. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.js +7 -1
  25. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.d.ts +17 -0
  26. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.js +12 -0
  27. package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.d.ts +15 -0
  28. package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.js +28 -0
  29. package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.d.ts +19 -0
  30. package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.js +23 -0
  31. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.d.ts +3 -0
  32. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.js +19 -0
  33. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.d.ts +15 -0
  34. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.js +38 -0
  35. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.d.ts +41 -0
  36. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.js +17 -0
  37. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.d.ts +21 -0
  38. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.js +39 -0
  39. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.d.ts +7 -37
  40. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.js +22 -62
  41. package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.d.ts +4 -39
  42. package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.js +12 -64
  43. package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.d.ts +10 -3
  44. package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.js +12 -3
  45. package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.d.ts +7 -0
  46. package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.js +38 -0
  47. package/dist/service/lambda/event-source/stream/kinesis/sim-kinesis-event-source-streams.js +1 -0
  48. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-reader.js +1 -0
  49. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.d.ts +1 -0
  50. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.js +7 -0
  51. package/dist/service/lambda/event-source/stream/sim-lambda-event-source-streams.d.ts +1 -0
  52. package/dist/service/lambda/index.d.ts +1 -0
  53. package/dist/service/lambda/sim-lambda-commands.js +1 -0
  54. package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
  55. package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
  56. package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
  57. package/docs/README.md +7 -0
  58. package/docs/ai-skill/README.md +6 -1
  59. package/docs/sdk/README.md +15 -0
  60. package/docs/services/acm/README.md +24 -40
  61. package/docs/services/apigateway/README.md +52 -71
  62. package/docs/services/apigatewayv2/README.md +55 -74
  63. package/docs/services/athena/README.md +17 -26
  64. package/docs/services/backup/README.md +29 -39
  65. package/docs/services/bedrock/README.md +38 -52
  66. package/docs/services/cloudformation/README.md +43 -55
  67. package/docs/services/cloudfront/README.md +69 -95
  68. package/docs/services/cloudwatch/README.md +40 -54
  69. package/docs/services/cognito/README.md +30 -45
  70. package/docs/services/dynamodb/README.md +34 -51
  71. package/docs/services/ecr/README.md +36 -77
  72. package/docs/services/ecs/README.md +26 -46
  73. package/docs/services/elbv2/README.md +19 -29
  74. package/docs/services/eventbridge/README.md +14 -18
  75. package/docs/services/firehose/README.md +24 -32
  76. package/docs/services/glue/README.md +41 -78
  77. package/docs/services/iam/README.md +13 -15
  78. package/docs/services/kinesis/README.md +53 -93
  79. package/docs/services/kms/README.md +22 -32
  80. package/docs/services/lambda/README.md +112 -85
  81. package/docs/services/logs/README.md +41 -50
  82. package/docs/services/organizations/README.md +50 -85
  83. package/docs/services/personalize/README.md +28 -44
  84. package/docs/services/rekognition/README.md +26 -38
  85. package/docs/services/route53/README.md +17 -17
  86. package/docs/services/s3/README.md +47 -51
  87. package/docs/services/scheduler/README.md +41 -52
  88. package/docs/services/secretsmanager/README.md +27 -42
  89. package/docs/services/ses/README.md +24 -34
  90. package/docs/services/sns/README.md +18 -26
  91. package/docs/services/sqs/README.md +14 -14
  92. package/docs/services/ssm/README.md +13 -17
  93. package/docs/services/stepfunctions/README.md +18 -20
  94. package/docs/services/sts/README.md +32 -45
  95. package/docs/services/wafv2/README.md +12 -17
  96. package/docs/testing/README.md +228 -0
  97. package/docs/time/README.md +10 -0
  98. package/llms.txt +1 -0
  99. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ import { isRecord } from "../../../util/type-guard/record.js";
2
+ import { SimLambdaDestinationArn } from "../destination/sim-lambda-destination-arn.js";
3
+ import { SimLambdaInvalidParameterValueException } from "../error/sim-lambda.error.js";
4
+ /** Validate the same destination shape for SDK and CloudFormation requests. */
5
+ export function simLambdaStreamDestinationConfig(value, sourceKind) {
6
+ if (value === undefined)
7
+ return undefined;
8
+ if (sourceKind === "sqs") {
9
+ throw new SimLambdaInvalidParameterValueException("DestinationConfig is not supported for SQS event source mappings.");
10
+ }
11
+ if (!isRecord(value) ||
12
+ Object.keys(value).some((key) => key !== "OnFailure")) {
13
+ throw invalid();
14
+ }
15
+ const failure = value["OnFailure"];
16
+ if (failure === undefined)
17
+ return {};
18
+ if (!isRecord(failure) ||
19
+ Object.keys(failure).some((key) => key !== "Destination")) {
20
+ throw invalid();
21
+ }
22
+ const destination = failure["Destination"];
23
+ if (destination === undefined)
24
+ return { OnFailure: {} };
25
+ if (typeof destination !== "string")
26
+ throw invalid();
27
+ if (destination !== "") {
28
+ const arn = SimLambdaDestinationArn.of(destination);
29
+ if ((arn.service !== "sqs" && arn.service !== "sns") ||
30
+ arn.resource.endsWith(".fifo")) {
31
+ throw invalid();
32
+ }
33
+ }
34
+ return { OnFailure: { Destination: destination } };
35
+ }
36
+ function invalid() {
37
+ return new SimLambdaInvalidParameterValueException("DestinationConfig supports only OnFailure with a standard SQS queue or SNS topic ARN.");
38
+ }
@@ -40,6 +40,7 @@ export class SimKinesisEventSourceStreams {
40
40
  }, { caller });
41
41
  return {
42
42
  records: output.Records,
43
+ shardId: request.shardId,
43
44
  next: { kind: "iterator", shardIterator: output.NextShardIterator },
44
45
  drained: false,
45
46
  };
@@ -29,6 +29,7 @@ export class SimDynamoDbEventSourceStreamReader {
29
29
  const next = output.NextShardIterator;
30
30
  return {
31
31
  records: output.Records ?? [],
32
+ shardId: await this.shard.shardId(request),
32
33
  next: simDynamoDbEventSourceNextPosition(next, position),
33
34
  drained: next === undefined,
34
35
  };
@@ -23,5 +23,6 @@ export declare class SimDynamoDbEventSourceStreamShard {
23
23
  * is the shard.
24
24
  */
25
25
  iteratorFor(request: SimLambdaEventSourceStreamRequest, position: SimLambdaEventSourceStreamNamedPosition): Promise<string>;
26
+ shardId(request: SimLambdaEventSourceStreamRequest): Promise<string>;
26
27
  private describe;
27
28
  }
@@ -49,6 +49,13 @@ export class SimDynamoDbEventSourceStreamShard {
49
49
  }
50
50
  return iterator.ShardIterator;
51
51
  }
52
+ async shardId(request) {
53
+ const description = await this.describe(request);
54
+ const shardId = description.Shards?.[0]?.ShardId;
55
+ if (shardId === undefined)
56
+ refuse(request, "reports no shard to read records from");
57
+ return shardId;
58
+ }
52
59
  async describe(request) {
53
60
  const { caller, streamArn } = request;
54
61
  const described = await this.commands.describeStream({ input: { StreamArn: streamArn } }, { caller });
@@ -84,6 +84,7 @@ export interface SimLambdaEventSourceStreamReadRequest extends SimLambdaEventSou
84
84
  export interface SimLambdaEventSourceStreamProgressBatch {
85
85
  readonly next: SimLambdaEventSourceStreamPosition;
86
86
  readonly drained: boolean;
87
+ readonly shardId?: string;
87
88
  }
88
89
  /**
89
90
  * What one read of a DynamoDB stream came back with.
@@ -16,3 +16,4 @@ export type { SimLambdaDynamoDbAttributeValue, SimLambdaDynamoDbImage, } from ".
16
16
  export { SimLambdaStreamCascadeError } from "./event-source/stream/sim-lambda-stream-cascade.error.js";
17
17
  export type { SimLambdaDestinationRecord, SimLambdaDestinationRequestContext, SimLambdaDestinationResponseContext, } from "./destination/sim-lambda-destination-record.js";
18
18
  export type { SimLambdaInvocationCondition } from "./function/event-invoke/sim-lambda-event-invoke-config.js";
19
+ export type { SimLambdaStreamFailureRecord, SimLambdaStreamFailureCondition, } from "./event-source/poll/sim-lambda-stream-failure-record.js";
@@ -113,6 +113,7 @@ export class SimLambdaCommands {
113
113
  background,
114
114
  });
115
115
  this.eventSourceMappings = new SimLambdaEventSourceMappingCommands({
116
+ destinations,
116
117
  accountRegionScope,
117
118
  pollers: new SimLambdaEventSourcePollers({
118
119
  functions: this.functionLookup,
@@ -30,6 +30,15 @@ export declare class SimSesAuthorizer {
30
30
  * ARN the identity is about to have.
31
31
  */
32
32
  authorizeIdentity(action: string, emailIdentity: string, caller?: SimAwsCaller): SimAwsResolvedCaller;
33
+ /**
34
+ * Ensure the caller may send from one email identity and address.
35
+ *
36
+ * SES supplies the bare sender address as `ses:FromAddress`. A policy can
37
+ * use this key to grant one address under a verified domain identity.
38
+ *
39
+ * https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html#sending-authorization-policy-example-from
40
+ */
41
+ authorizeSendEmail(emailIdentity: string, fromAddress: string, caller?: SimAwsCaller): SimAwsResolvedCaller;
33
42
  /**
34
43
  * Ensure the caller may perform an action on one email template.
35
44
  *
@@ -40,6 +40,17 @@ export class SimSesAuthorizer {
40
40
  authorizeIdentity(action, emailIdentity, caller) {
41
41
  return this.authorizeResource(action, simSesIdentityArn(this.#accountRegionScope, emailIdentity), caller);
42
42
  }
43
+ /**
44
+ * Ensure the caller may send from one email identity and address.
45
+ *
46
+ * SES supplies the bare sender address as `ses:FromAddress`. A policy can
47
+ * use this key to grant one address under a verified domain identity.
48
+ *
49
+ * https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html#sending-authorization-policy-example-from
50
+ */
51
+ authorizeSendEmail(emailIdentity, fromAddress, caller) {
52
+ return this.authorizeResource("ses:SendEmail", simSesIdentityArn(this.#accountRegionScope, emailIdentity), caller, { "ses:FromAddress": fromAddress });
53
+ }
43
54
  /**
44
55
  * Ensure the caller may perform an action on one email template.
45
56
  *
@@ -73,8 +84,13 @@ export class SimSesAuthorizer {
73
84
  authorizeNoResource(action, caller) {
74
85
  return this.authorizeResource(action, noResource, caller);
75
86
  }
76
- authorizeResource(action, resource, caller) {
77
- const decision = this.#iam.authorize({ action, resource, caller });
87
+ authorizeResource(action, resource, caller, conditionContext) {
88
+ const decision = this.#iam.authorize({
89
+ action,
90
+ resource,
91
+ caller,
92
+ conditionContext,
93
+ });
78
94
  if (decision.isDenied) {
79
95
  throw new SimIamAccessDenied({
80
96
  principal: decision.caller.principal,
@@ -42,8 +42,9 @@ export class SimSesSendEmail {
42
42
  handle(command, options) {
43
43
  const input = command.input;
44
44
  const fromEmailAddress = requiredSimSesFromAddress(input.FromEmailAddress);
45
+ const bareFromAddress = simSesBareAddress(fromEmailAddress);
45
46
  refuseUnsimulatedSendInput(input);
46
- this.#authorizer.authorizeIdentity("ses:SendEmail", this.#identities.covering(simSesBareAddress(fromEmailAddress)), options?.caller);
47
+ this.#authorizer.authorizeSendEmail(this.#identities.covering(bareFromAddress), bareFromAddress, options?.caller);
47
48
  const destination = readDestination(input.Destination);
48
49
  const recipients = allRecipients(destination);
49
50
  if (recipients.length === 0) {
package/docs/README.md CHANGED
@@ -15,6 +15,12 @@ npm install --save-dev @kensio/yulin
15
15
 
16
16
  ## Choose how to use Yulin
17
17
 
18
+ Start with [test suite setup](https://yulinsim.dev/testing/) when adding Yulin to an application's
19
+ tests. Create one simulated environment, deploy the application's infrastructure, and install SDK
20
+ interception for the whole suite. Tests then use that shared environment as they would use an AWS
21
+ account or a container-based simulator. Put the smaller set of tests that control simulated time in
22
+ an isolated group because the clock belongs to the environment too.
23
+
18
24
  Start with [AWS SDK interception](https://yulinsim.dev/sdk/) when the code under test already uses an
19
25
  AWS SDK client. Yulin intercepts the client's `send` calls and returns responses from a simulated
20
26
  service. The application code continues to use the AWS SDK normally.
@@ -81,4 +87,5 @@ credential, or run other work that depends on time passing.
81
87
  - [Non-AWS dependencies](https://yulinsim.dev/non-aws-dependencies/ "Dependencies Yulin does not simulate usage docs")
82
88
  - [Serving on localhost](https://yulinsim.dev/serve/ "Serving simulated AWS on localhost usage docs")
83
89
  - [Simulated time](https://yulinsim.dev/time/ "Simulated time usage docs")
90
+ - [Test suite setup](https://yulinsim.dev/testing/ "Sharing one Yulin environment across a test suite")
84
91
  - [Terraform](https://yulinsim.dev/terraform/ "Deploying Terraform into simulated AWS usage docs")
@@ -37,10 +37,15 @@ The skill tells an agent how to:
37
37
  - control simulated time
38
38
  - inspect simulated state in assertions
39
39
  - match simulated service errors by `name`
40
- - share an expensive deployment across tests in one file
40
+ - share one simulated deployment and SDK interception across a whole test suite
41
+ - give tests that control simulated time their own isolated Yulin environment
41
42
  - invoke code through a simulated Lambda function with its configured role and environment
42
43
  - treat unsupported behaviour as a gap to report, not behaviour to guess
43
44
 
45
+ The [test suite setup guide](https://yulinsim.dev/testing/) states the default explicitly. An agent
46
+ should set Yulin up once for the suite and let tests share it as they would share an AWS account or a
47
+ container-based simulator. Tests that move the shared simulated clock are the main exception.
48
+
44
49
  ## Give the agent access to the API docs
45
50
 
46
51
  The skill covers testing choices. The service guides document Yulin's APIs and supported AWS
@@ -3,6 +3,18 @@
3
3
  `SimSdk` routes AWS SDK for JavaScript v3 commands to Yulin. Use it to test code that already sends
4
4
  commands through AWS SDK clients.
5
5
 
6
+ ## Install interception in suite setup
7
+
8
+ Application tests should normally create one `SimSdk` in Vitest suite setup and keep its class
9
+ interceptions installed for the whole suite. Every SDK client then reaches the same simulated state,
10
+ including clients created in different test files.
11
+
12
+ The [test suite setup guide](https://yulinsim.dev/testing/) shows how to share one in-process
13
+ environment across Vitest files. It also covers the worker and isolation settings this requires.
14
+ A separate `SimSdk` for each test or file remains supported when a case needs an empty environment.
15
+ Tests that control the simulation's clock should use a separate `SimSdk` so their time changes cannot
16
+ affect the shared suite.
17
+
6
18
  ## Intercept a client
7
19
 
8
20
  Create a `SimSdk`, intercept a client class, and run the code under test:
@@ -147,6 +159,9 @@ Restore an interception before later code needs the client's original `send` met
147
159
  - Call `simSdk.restoreAll()` to restore every client intercepted by that `SimSdk`.
148
160
  - Declare `SimSdk` or an interception handle with `using` to restore it when the scope ends.
149
161
 
162
+ A suite-wide interception stays installed until the test worker exits. Do not restore it in a
163
+ per-file `afterAll`, since later files use the same interception and simulated state.
164
+
150
165
  ## Limit the intercepted commands
151
166
 
152
167
  An interception handles every command by default. Pass an allow list when a test should accept only
@@ -1,8 +1,8 @@
1
1
  # Simulated ACM
2
2
 
3
- Yulin includes a simulated AWS Certificate Manager (ACM) for tests and local development. In this
4
- guide, you'll request certificates, configure DNS validation, filter certificate lists, and use ACM
5
- with simulated CloudFormation.
3
+ Yulin simulates AWS Certificate Manager (ACM) in memory. You can request and inspect certificates,
4
+ exercise DNS validation against simulated Route53, and deploy certificate resources through
5
+ simulated CloudFormation.
6
6
 
7
7
  ## Prerequisites
8
8
 
@@ -12,9 +12,8 @@ with simulated CloudFormation.
12
12
 
13
13
  ## Request a certificate
14
14
 
15
- 1. Create a `SimAws` instance and get a simulated ACM client.
16
- 2. Call `requestCertificate` with a `RequestCertificateCommand`.
17
- 3. Call `listCertificates` with a `ListCertificatesCommand` to confirm the certificate exists.
15
+ Create a `SimAws` instance, request a certificate, then list the certificates in that account and
16
+ region:
18
17
 
19
18
  ```typescript sim-acm-request-certificate
20
19
  /**
@@ -145,9 +144,8 @@ for (const validation of domainValidationOptions) {
145
144
  }
146
145
  ```
147
146
 
148
- For DNS validation, simulated ACM returns CNAME validation records for the primary domain and each
149
- subject alternative name. The records are deterministic, which makes them suitable for assertions in
150
- tests.
147
+ For DNS validation, ACM returns a deterministic CNAME record for the primary domain and each subject
148
+ alternative name. Tests can assert on these records directly.
151
149
 
152
150
  For EMAIL validation, the validation method is recorded but no DNS resource record is returned.
153
151
 
@@ -187,11 +185,8 @@ console.log(validation?.ResourceRecord);
187
185
 
188
186
  ## Wait for certificate issuance
189
187
 
190
- Requested certificates start in `PENDING_VALIDATION` status. Simulated ACM schedules background work
191
- to move them to `ISSUED`.
192
-
193
- If your test needs the issued state, wait for background tasks to complete before describing the
194
- certificate.
188
+ Requested certificates start as `PENDING_VALIDATION`. Wait for background tasks before asserting
189
+ that a certificate is `ISSUED`.
195
190
 
196
191
  > **Note:** Where a simulated Route53 hosted zone covers the certificate domain, issuance waits for
197
192
  > DNS validation first.
@@ -233,19 +228,14 @@ console.log(describeOutput.Certificate?.IssuedAt);
233
228
 
234
229
  ## Validate a certificate against simulated Route53
235
230
 
236
- Real ACM issues a DNS-validated certificate only once the CNAME it requests is resolvable. Simulated
237
- ACM does the same, but only where the simulation can answer for the domain.
238
-
239
- By default, the rules are:
231
+ ACM requires the validation CNAME when simulated Route53 is authoritative for the domain. The
232
+ default rules are:
240
233
 
241
234
  - If a simulated Route53 hosted zone covers the certificate domain, the certificate waits for its validation record.
242
235
  - If no hosted zone covers the domain, the certificate is issued as soon as background tasks drain.
243
236
 
244
- Templates commonly reference hosted zones managed by another team or another tool. Those
245
- certificates keep working here because the simulation holds no zone for their domain.
246
-
247
- [Two methods override that default](#override-when-validation-is-required) where it doesn't suit
248
- your test.
237
+ This lets templates refer to externally managed hosted zones without leaving certificates pending.
238
+ [Two methods override the default](#override-when-validation-is-required).
249
239
 
250
240
  ```typescript sim-acm-dns-validation
251
241
  /**
@@ -322,11 +312,9 @@ const issuedOutput = await simAws.acm().describeCertificate(
322
312
  console.log(issuedOutput.Certificate?.Status); // ISSUED
323
313
  ```
324
314
 
325
- Each domain on a certificate is validated separately. A certificate with subject alternative names
326
- is issued only once every domain that needs DNS validation has its record. Domains that are not
327
- covered by any hosted zone don't need anything published for them. Until all domains are validated,
328
- `DescribeCertificateCommand` reports `SUCCESS` for validated domains and `PENDING_VALIDATION` for
329
- the rest.
315
+ Each domain is validated separately. A certificate is issued when every domain covered by a
316
+ simulated hosted zone has its validation record. `DescribeCertificateCommand` reports `SUCCESS` for
317
+ validated domains and `PENDING_VALIDATION` for the rest.
330
318
 
331
319
  Hosted zones are looked up across every simulated account, matching real ACM validating against
332
320
  public DNS. A certificate in one account can be validated by a hosted zone in another.
@@ -554,18 +542,16 @@ await scopedAcm.requestCertificate(
554
542
  );
555
543
  ```
556
544
 
557
- Each `SimAws` instance has its own isolated state. Create a fresh instance per test or share one across related local setup.
545
+ Each `SimAws` instance has its own isolated state. Application tests should normally use the instance
546
+ from their shared [test suite setup](https://yulinsim.dev/testing/). A fresh instance remains useful
547
+ when a test specifically needs an empty simulated account.
558
548
 
559
549
  ## Register a certificate with a chosen ARN
560
550
 
561
- `RequestCertificateCommand` allocates its own certificate ARN, as real ACM does, and takes none from
562
- you. When something else already decided the ARN, register the certificate as part of your test setup
563
- instead.
564
-
565
- The usual reason is a CDK app that creates its certificate in one stack and uses it in another. The
566
- ARN crosses between the two as a plain string, and the stack using it carries that ARN into its
567
- synthesized template. Simulated CloudFront checks the certificate before it creates a Distribution,
568
- so registering the certificate first lets the template deploy as it is, with no rewriting.
551
+ `RequestCertificateCommand` allocates the ARN. If a fixture or another stack already provides the
552
+ ARN, register that certificate during test setup. This is useful when one CDK stack creates a
553
+ certificate and another uses its ARN. Simulated CloudFront can then resolve the certificate without
554
+ changing the synthesized template.
569
555
 
570
556
  ```typescript sim-acm-register-certificate
571
557
  /**
@@ -791,9 +777,7 @@ A `HostedZoneId` that names a hosted zone the simulator doesn't hold is skipped
791
777
 
792
778
  If a hosted zone covers the domain but the validation record never appears, the stack fails rather than hanging. Real CloudFormation sits in `CREATE_IN_PROGRESS` for hours before timing out, which is of little use in a test. The resource fails immediately and names the record it waited for.
793
779
 
794
- ## Available functionality
795
-
796
- Simulated ACM supports:
780
+ ## Supported operations
797
781
 
798
782
  - `RequestCertificateCommand`, `DescribeCertificateCommand`, and `ListCertificatesCommand`
799
783
  - DNS validation against records in simulated Route53