@kensio/yulin 1.21.7 → 1.21.8
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.
- package/README.md +22 -2
- package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
- package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
- package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
- package/docs/README.md +7 -0
- package/docs/ai-skill/README.md +6 -1
- package/docs/sdk/README.md +15 -0
- package/docs/services/acm/README.md +24 -40
- package/docs/services/apigateway/README.md +52 -71
- package/docs/services/apigatewayv2/README.md +55 -74
- package/docs/services/athena/README.md +17 -26
- package/docs/services/backup/README.md +29 -39
- package/docs/services/bedrock/README.md +38 -52
- package/docs/services/cloudformation/README.md +43 -55
- package/docs/services/cloudfront/README.md +69 -95
- package/docs/services/cloudwatch/README.md +40 -54
- package/docs/services/cognito/README.md +30 -45
- package/docs/services/dynamodb/README.md +34 -51
- package/docs/services/ecr/README.md +36 -77
- package/docs/services/ecs/README.md +26 -46
- package/docs/services/elbv2/README.md +19 -29
- package/docs/services/eventbridge/README.md +14 -18
- package/docs/services/firehose/README.md +24 -32
- package/docs/services/glue/README.md +41 -78
- package/docs/services/iam/README.md +13 -15
- package/docs/services/kinesis/README.md +53 -93
- package/docs/services/kms/README.md +22 -32
- package/docs/services/lambda/README.md +53 -79
- package/docs/services/logs/README.md +41 -50
- package/docs/services/organizations/README.md +50 -85
- package/docs/services/personalize/README.md +28 -44
- package/docs/services/rekognition/README.md +26 -38
- package/docs/services/route53/README.md +17 -17
- package/docs/services/s3/README.md +47 -51
- package/docs/services/scheduler/README.md +41 -52
- package/docs/services/secretsmanager/README.md +27 -42
- package/docs/services/ses/README.md +24 -34
- package/docs/services/sns/README.md +18 -26
- package/docs/services/sqs/README.md +14 -14
- package/docs/services/ssm/README.md +13 -17
- package/docs/services/stepfunctions/README.md +18 -20
- package/docs/services/sts/README.md +32 -45
- package/docs/services/wafv2/README.md +12 -17
- package/docs/testing/README.md +228 -0
- package/docs/time/README.md +10 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Test suite setup
|
|
2
|
+
|
|
3
|
+
Run one Yulin environment for an application's test suite. Create the simulation, deploy the
|
|
4
|
+
application's infrastructure, and install AWS SDK interception once. Every test then interacts with
|
|
5
|
+
the same simulated account and resources.
|
|
6
|
+
|
|
7
|
+
This is the recommended setup. It matches the way a suite uses a shared AWS account or a
|
|
8
|
+
container-based simulator such as LocalStack. Creating a new Yulin environment for every test or
|
|
9
|
+
test file is supported, but it should be reserved for cases that need a blank simulated account.
|
|
10
|
+
|
|
11
|
+
The simulated clock is the main exception. A suite-wide `SimAws` has one clock, so a test that moves
|
|
12
|
+
it changes time for every resource in that environment. Keep the majority of tests in the shared
|
|
13
|
+
environment without changing its clock. Put clock-controlling tests in a smaller isolated group.
|
|
14
|
+
|
|
15
|
+
## Split the Vitest suite
|
|
16
|
+
|
|
17
|
+
Yulin holds state in the process that created it. Vitest must run the Yulin tests in one worker for
|
|
18
|
+
all shared files to reach the same environment. Give that project disabled file parallelism and file
|
|
19
|
+
isolation, then load a setup module before each test file.
|
|
20
|
+
|
|
21
|
+
The second project below matches files ending in `.clock.test.ts`. Those tests do not load the shared
|
|
22
|
+
setup and can create isolated Yulin environments:
|
|
23
|
+
|
|
24
|
+
```typescript testing-vitest-config
|
|
25
|
+
import { defineConfig } from "vitest/config";
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
test: {
|
|
29
|
+
environment: "node",
|
|
30
|
+
projects: [
|
|
31
|
+
{
|
|
32
|
+
extends: true,
|
|
33
|
+
test: {
|
|
34
|
+
name: "shared Yulin",
|
|
35
|
+
include: ["test/**/*.test.ts"],
|
|
36
|
+
exclude: ["test/**/*.clock.test.ts"],
|
|
37
|
+
fileParallelism: false,
|
|
38
|
+
isolate: false,
|
|
39
|
+
setupFiles: ["./test/setup-yulin.ts"],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
extends: true,
|
|
44
|
+
test: {
|
|
45
|
+
name: "isolated Yulin clock",
|
|
46
|
+
include: ["test/**/*.clock.test.ts"],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Vitest executes a `setupFiles` entry before every test file. With isolation disabled, modules
|
|
55
|
+
imported by that entry stay cached in the worker. Put the Yulin initialization in an imported module
|
|
56
|
+
to make it run once.
|
|
57
|
+
|
|
58
|
+
See Vitest's documentation for [`setupFiles`](https://vitest.dev/config/setupfiles),
|
|
59
|
+
[`fileParallelism`](https://vitest.dev/config/fileparallelism), and
|
|
60
|
+
[`isolate`](https://vitest.dev/config/isolate).
|
|
61
|
+
|
|
62
|
+
## Create and deploy the shared environment
|
|
63
|
+
|
|
64
|
+
Put the suite environment in a module such as `test/yulin-environment.ts`. Intercept client classes
|
|
65
|
+
used by the application and deploy its synthesized CDK cloud assembly:
|
|
66
|
+
|
|
67
|
+
```typescript testing-shared-yulin-environment
|
|
68
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
69
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
70
|
+
import type { SimAws } from "@kensio/yulin";
|
|
71
|
+
import { SimSdk } from "@kensio/yulin/sdk";
|
|
72
|
+
|
|
73
|
+
interface YulinTestEnvironment {
|
|
74
|
+
readonly simAws: SimAws;
|
|
75
|
+
readonly simSdk: SimSdk;
|
|
76
|
+
readonly uploadsBucketName: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type YulinTestGlobal = typeof globalThis & {
|
|
80
|
+
yulinEnvironment?: Promise<YulinTestEnvironment>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const testGlobal = globalThis as YulinTestGlobal;
|
|
84
|
+
|
|
85
|
+
// oxlint-disable-next-line unicorn-js/prefer-top-level-await -- The shared promise prevents setup from running again before another test file.
|
|
86
|
+
export const yulin = await (testGlobal.yulinEnvironment ??= startYulin());
|
|
87
|
+
|
|
88
|
+
async function startYulin(): Promise<YulinTestEnvironment> {
|
|
89
|
+
const simSdk = new SimSdk();
|
|
90
|
+
simSdk.intercept(DynamoDBClient);
|
|
91
|
+
simSdk.intercept(S3Client);
|
|
92
|
+
|
|
93
|
+
const stacks = await simSdk.simAws.cloudFormation().deployCdkOut({
|
|
94
|
+
directoryPath: "cdk.out",
|
|
95
|
+
stackNames: ["ApplicationStack"],
|
|
96
|
+
});
|
|
97
|
+
const appStack = stacks.get("ApplicationStack");
|
|
98
|
+
|
|
99
|
+
if (appStack === undefined) {
|
|
100
|
+
throw new Error("ApplicationStack was not deployed");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
process.once("exit", () => {
|
|
104
|
+
simSdk.restoreAll();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
simAws: simSdk.simAws,
|
|
109
|
+
simSdk,
|
|
110
|
+
uploadsBucketName: appStack.output("UploadsBucketName"),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Use the same templates that the application deploys. `deployCdkOut(...)` can deploy the whole cloud
|
|
116
|
+
assembly or the named application Stacks. Read generated resource names from stack outputs or
|
|
117
|
+
resource accessors after deployment.
|
|
118
|
+
|
|
119
|
+
The configured setup entry only needs to import that module:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// test/setup-yulin.ts
|
|
123
|
+
import "./yulin-environment.js";
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Do not put the initialization directly in `setup-yulin.ts`. Vitest executes that file for every test
|
|
127
|
+
file, even when isolation is disabled.
|
|
128
|
+
|
|
129
|
+
## Use the environment from every test
|
|
130
|
+
|
|
131
|
+
Application code continues to construct and send through ordinary AWS SDK clients. Class-level
|
|
132
|
+
interception routes all of them to the suite's Yulin environment.
|
|
133
|
+
|
|
134
|
+
A test that needs direct access can import the shared environment:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { randomUUID } from "node:crypto";
|
|
138
|
+
|
|
139
|
+
import {
|
|
140
|
+
GetObjectCommand,
|
|
141
|
+
PutObjectCommand,
|
|
142
|
+
S3Client,
|
|
143
|
+
} from "@aws-sdk/client-s3";
|
|
144
|
+
import { expect, it } from "vitest";
|
|
145
|
+
|
|
146
|
+
import { yulin } from "../yulin-environment.js";
|
|
147
|
+
|
|
148
|
+
it("stores an upload", async () => {
|
|
149
|
+
const key = `test-uploads/${randomUUID()}.txt`;
|
|
150
|
+
const s3 = new S3Client({ region: "eu-west-2" });
|
|
151
|
+
|
|
152
|
+
await s3.send(
|
|
153
|
+
new PutObjectCommand({
|
|
154
|
+
Bucket: yulin.uploadsBucketName,
|
|
155
|
+
Key: key,
|
|
156
|
+
Body: "an upload",
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const stored = await yulin.simAws
|
|
161
|
+
.region("eu-west-2")
|
|
162
|
+
.s3()
|
|
163
|
+
.getObject(
|
|
164
|
+
new GetObjectCommand({
|
|
165
|
+
Bucket: yulin.uploadsBucketName,
|
|
166
|
+
Key: key,
|
|
167
|
+
}),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
expect(await stored?.Body?.transformToString()).toBe("an upload");
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The `SimAws` object is mainly useful for preparing input state and reading state back in assertions.
|
|
175
|
+
Exercise the application through its normal interfaces whenever possible.
|
|
176
|
+
|
|
177
|
+
## Keep tests independent in shared state
|
|
178
|
+
|
|
179
|
+
Shared infrastructure does not require tests to depend on one another. Give each test's records,
|
|
180
|
+
object keys, user names, and other mutable data unique values. Read CloudFormation-generated names
|
|
181
|
+
from the deployed stack. Avoid assertions that assume the simulated account contains no other data.
|
|
182
|
+
|
|
183
|
+
Keep `beforeEach` for the records a test needs. A per-file `beforeAll` can prepare data used by every
|
|
184
|
+
test in that file. Leave the suite's stacks and SDK interception in place until the worker exits.
|
|
185
|
+
|
|
186
|
+
Tests run sequentially with `fileParallelism: false`. If a test uses `it.concurrent`, its data still
|
|
187
|
+
needs unique identifiers because those cases share the same environment at the same time.
|
|
188
|
+
|
|
189
|
+
## Give clock-controlling tests their own environment
|
|
190
|
+
|
|
191
|
+
Every service in a `SimAws` reads the same simulated clock. Calling `advanceBy(...)` can expire
|
|
192
|
+
credentials, delete resources whose retention period has passed, and run scheduled work anywhere in
|
|
193
|
+
the environment. Resetting the clock afterwards cannot reverse those changes.
|
|
194
|
+
|
|
195
|
+
Tests in the shared project should treat the clock as read-only. Put a test that calls `freeze()`,
|
|
196
|
+
`setTo(...)`, `advanceBy(...)`, or `resume()` in a `.clock.test.ts` file and create a fresh environment
|
|
197
|
+
inside the test:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { SimAws, SimFixedClock } from "@kensio/yulin";
|
|
201
|
+
import { it } from "vitest";
|
|
202
|
+
|
|
203
|
+
it("expires a session", async () => {
|
|
204
|
+
const simAws = new SimAws({
|
|
205
|
+
clock: new SimFixedClock(new Date("2026-09-04T09:00:00.000Z")),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Deploy only the infrastructure this clock-controlling test needs.
|
|
209
|
+
|
|
210
|
+
await simAws.clock().advanceBy({ minutes: 20 });
|
|
211
|
+
|
|
212
|
+
// Assert the behaviour after the time change.
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Create a `SimSdk` around that `SimAws` when application code uses SDK clients. Restore its
|
|
217
|
+
interceptions at the end of the test. The [simulated time guide](https://yulinsim.dev/time/) describes
|
|
218
|
+
what moving the clock runs and changes.
|
|
219
|
+
|
|
220
|
+
## When to create another environment
|
|
221
|
+
|
|
222
|
+
A fresh `SimAws` or `SimSdk` is useful when the empty environment is part of the behaviour under
|
|
223
|
+
test, or when the test needs to control simulated time. Yulin's own service unit tests are another
|
|
224
|
+
example because they test resource creation and account isolation directly.
|
|
225
|
+
|
|
226
|
+
Vitest workers cannot share an in-memory `SimAws`. A suite that keeps file parallelism creates one
|
|
227
|
+
environment per worker. Put Yulin-based application tests in a Vitest project with
|
|
228
|
+
`fileParallelism: false` when the rest of the unit suite should remain parallel.
|
package/docs/time/README.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
Each `SimAws` has its own clock. Yulin uses that clock for resource timestamps, expiry checks, and
|
|
4
4
|
scheduled work.
|
|
5
5
|
|
|
6
|
+
## Isolate tests that control time
|
|
7
|
+
|
|
8
|
+
A shared [test suite environment](https://yulinsim.dev/testing/) also has one shared clock. Most
|
|
9
|
+
tests should use that environment without calling `freeze()`, `setTo(...)`, `advanceBy(...)`, or
|
|
10
|
+
`resume()`.
|
|
11
|
+
|
|
12
|
+
Put tests that control simulated time in a separate test group. Give each of those tests its own
|
|
13
|
+
`SimAws` or `SimSdk`, along with the infrastructure it needs. A clock change then affects only that
|
|
14
|
+
test's environment. The rest of the suite can keep sharing one deployment and SDK interception.
|
|
15
|
+
|
|
6
16
|
## Start at a known time
|
|
7
17
|
|
|
8
18
|
A new simulation follows the system clock by default. Pass a `SimFixedClock` when a test needs an
|
package/llms.txt
CHANGED
|
@@ -57,4 +57,5 @@ The same pages are on the web at https://yulinsim.dev/ for whichever release is
|
|
|
57
57
|
- [Non-AWS dependencies](docs/non-aws-dependencies/README.md): Dependencies Yulin does not simulate usage docs
|
|
58
58
|
- [Serving on localhost](docs/serve/README.md): Serving simulated AWS on localhost usage docs
|
|
59
59
|
- [Simulated time](docs/time/README.md): Simulated time usage docs
|
|
60
|
+
- [Test suite setup](docs/testing/README.md): Sharing one Yulin environment across a test suite
|
|
60
61
|
- [Terraform](docs/terraform/README.md): Deploying Terraform into simulated AWS usage docs
|
package/package.json
CHANGED