@intentius/chant-lexicon-aws 0.0.2
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 +438 -0
- package/package.json +30 -0
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +197 -0
- package/src/codegen/docs-cli.ts +3 -0
- package/src/codegen/docs.ts +1206 -0
- package/src/codegen/extensions.ts +171 -0
- package/src/codegen/fallback.ts +33 -0
- package/src/codegen/generate-cli.ts +17 -0
- package/src/codegen/generate-lexicon.ts +98 -0
- package/src/codegen/generate-typescript.ts +257 -0
- package/src/codegen/generate.test.ts +125 -0
- package/src/codegen/generate.ts +226 -0
- package/src/codegen/idempotency.test.ts +28 -0
- package/src/codegen/naming.ts +120 -0
- package/src/codegen/package.test.ts +60 -0
- package/src/codegen/package.ts +84 -0
- package/src/codegen/patches.ts +98 -0
- package/src/codegen/rollback.test.ts +80 -0
- package/src/codegen/rollback.ts +20 -0
- package/src/codegen/sam.ts +387 -0
- package/src/codegen/snapshot.test.ts +84 -0
- package/src/codegen/typecheck.test.ts +50 -0
- package/src/codegen/typecheck.ts +4 -0
- package/src/codegen/versions.ts +37 -0
- package/src/coverage.ts +14 -0
- package/src/generated/index.d.ts +160753 -0
- package/src/generated/index.ts +14396 -0
- package/src/generated/lexicon-aws.json +114563 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +181 -0
- package/src/import/generator.ts +349 -0
- package/src/import/parser.test.ts +200 -0
- package/src/import/parser.ts +350 -0
- package/src/import/roundtrip-fixtures.test.ts +78 -0
- package/src/import/roundtrip.test.ts +195 -0
- package/src/index.ts +63 -0
- package/src/integration.test.ts +129 -0
- package/src/intrinsics.test.ts +167 -0
- package/src/intrinsics.ts +223 -0
- package/src/lint/post-synth/cf-refs.ts +91 -0
- package/src/lint/post-synth/cor020.ts +72 -0
- package/src/lint/post-synth/ext001.test.ts +68 -0
- package/src/lint/post-synth/ext001.ts +222 -0
- package/src/lint/post-synth/post-synth.test.ts +280 -0
- package/src/lint/post-synth/waw010.ts +49 -0
- package/src/lint/post-synth/waw011.ts +49 -0
- package/src/lint/post-synth/waw013.ts +45 -0
- package/src/lint/post-synth/waw014.ts +50 -0
- package/src/lint/post-synth/waw015.ts +100 -0
- package/src/lint/rules/hardcoded-region.ts +43 -0
- package/src/lint/rules/iam-wildcard.ts +66 -0
- package/src/lint/rules/index.ts +7 -0
- package/src/lint/rules/rules.test.ts +175 -0
- package/src/lint/rules/s3-encryption.ts +69 -0
- package/src/lsp/completions.test.ts +72 -0
- package/src/lsp/completions.ts +18 -0
- package/src/lsp/hover.test.ts +53 -0
- package/src/lsp/hover.ts +53 -0
- package/src/nested-stack.test.ts +83 -0
- package/src/nested-stack.ts +125 -0
- package/src/plugin.test.ts +316 -0
- package/src/plugin.ts +514 -0
- package/src/pseudo.test.ts +55 -0
- package/src/pseudo.ts +29 -0
- package/src/serializer.test.ts +507 -0
- package/src/serializer.ts +333 -0
- package/src/spec/fetch.test.ts +27 -0
- package/src/spec/fetch.ts +107 -0
- package/src/spec/parse.test.ts +153 -0
- package/src/spec/parse.ts +202 -0
- package/src/testdata/load-fixtures.ts +17 -0
- package/src/testdata/roundtrip/conditions.json +21 -0
- package/src/testdata/roundtrip/intrinsic-calls.json +31 -0
- package/src/testdata/roundtrip/intrinsics.json +18 -0
- package/src/testdata/roundtrip/multi-resource.json +37 -0
- package/src/testdata/roundtrip/parameters.json +23 -0
- package/src/testdata/roundtrip/simple.json +12 -0
- package/src/testdata/sam-fixtures/api.yaml +14 -0
- package/src/testdata/sam-fixtures/application.yaml +13 -0
- package/src/testdata/sam-fixtures/function.yaml +22 -0
- package/src/testdata/sam-fixtures/graphql-api.yaml +13 -0
- package/src/testdata/sam-fixtures/http-api.yaml +15 -0
- package/src/testdata/sam-fixtures/layer-version.yaml +15 -0
- package/src/testdata/sam-fixtures/multi-type-a.yaml +23 -0
- package/src/testdata/sam-fixtures/multi-type-b.yaml +29 -0
- package/src/testdata/sam-fixtures/simple-table.yaml +12 -0
- package/src/testdata/sam-fixtures/state-machine.yaml +14 -0
- package/src/testdata/schemas/aws-dynamodb-table.json +126 -0
- package/src/testdata/schemas/aws-iam-role.json +85 -0
- package/src/testdata/schemas/aws-lambda-function.json +90 -0
- package/src/testdata/schemas/aws-s3-bucket.json +83 -0
- package/src/testdata/schemas/aws-sns-topic.json +71 -0
- package/src/validate-cli.ts +19 -0
- package/src/validate.ts +34 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { awsPlugin } from "./plugin";
|
|
3
|
+
import { isLexiconPlugin } from "@intentius/chant/lexicon";
|
|
4
|
+
|
|
5
|
+
describe("awsPlugin", () => {
|
|
6
|
+
// -----------------------------------------------------------------------
|
|
7
|
+
// Basic interface
|
|
8
|
+
// -----------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
test("satisfies isLexiconPlugin type guard", () => {
|
|
11
|
+
expect(isLexiconPlugin(awsPlugin)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("has correct name and serializer", () => {
|
|
15
|
+
expect(awsPlugin.name).toBe("aws");
|
|
16
|
+
expect(awsPlugin.serializer.name).toBe("aws");
|
|
17
|
+
expect(awsPlugin.serializer.rulePrefix).toBe("WAW");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// -----------------------------------------------------------------------
|
|
21
|
+
// Lint rules
|
|
22
|
+
// -----------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
test("returns lint rules", () => {
|
|
25
|
+
const rules = awsPlugin.lintRules!();
|
|
26
|
+
expect(rules).toHaveLength(3);
|
|
27
|
+
const ids = rules.map((r) => r.id);
|
|
28
|
+
expect(ids).toContain("WAW001");
|
|
29
|
+
expect(ids).toContain("WAW006");
|
|
30
|
+
expect(ids).toContain("WAW009");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// -----------------------------------------------------------------------
|
|
34
|
+
// Intrinsics / pseudo-parameters
|
|
35
|
+
// -----------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
test("returns intrinsics", () => {
|
|
38
|
+
const intrinsics = awsPlugin.intrinsics!();
|
|
39
|
+
expect(intrinsics.length).toBe(8);
|
|
40
|
+
const names = intrinsics.map((i) => i.name);
|
|
41
|
+
expect(names).toContain("Sub");
|
|
42
|
+
expect(names).toContain("Ref");
|
|
43
|
+
expect(names).toContain("GetAtt");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("returns pseudo-parameters", () => {
|
|
47
|
+
const params = awsPlugin.pseudoParameters!();
|
|
48
|
+
expect(params).toContain("AWS::StackName");
|
|
49
|
+
expect(params).toContain("AWS::Region");
|
|
50
|
+
expect(params).toContain("AWS::AccountId");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// -----------------------------------------------------------------------
|
|
54
|
+
// Template detection
|
|
55
|
+
// -----------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
test("detects CloudFormation templates", () => {
|
|
58
|
+
expect(
|
|
59
|
+
awsPlugin.detectTemplate!({ AWSTemplateFormatVersion: "2010-09-09" }),
|
|
60
|
+
).toBe(true);
|
|
61
|
+
|
|
62
|
+
expect(
|
|
63
|
+
awsPlugin.detectTemplate!({
|
|
64
|
+
Resources: { MyBucket: { Type: "AWS::S3::Bucket" } },
|
|
65
|
+
}),
|
|
66
|
+
).toBe(true);
|
|
67
|
+
|
|
68
|
+
expect(awsPlugin.detectTemplate!({})).toBe(false);
|
|
69
|
+
expect(awsPlugin.detectTemplate!(null)).toBe(false);
|
|
70
|
+
expect(awsPlugin.detectTemplate!("string")).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// -----------------------------------------------------------------------
|
|
74
|
+
// Template parsing / generation
|
|
75
|
+
// -----------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
test("returns a template parser", () => {
|
|
78
|
+
const parser = awsPlugin.templateParser!();
|
|
79
|
+
expect(parser).toBeDefined();
|
|
80
|
+
expect(typeof parser.parse).toBe("function");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("returns a template generator", () => {
|
|
84
|
+
const generator = awsPlugin.templateGenerator!();
|
|
85
|
+
expect(generator).toBeDefined();
|
|
86
|
+
expect(typeof generator.generate).toBe("function");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// -----------------------------------------------------------------------
|
|
90
|
+
// Skills
|
|
91
|
+
// -----------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
describe("skills", () => {
|
|
94
|
+
test("returns at least one skill", () => {
|
|
95
|
+
const skills = awsPlugin.skills!();
|
|
96
|
+
expect(skills.length).toBeGreaterThanOrEqual(1);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("aws-cloudformation skill has required fields", () => {
|
|
100
|
+
const skills = awsPlugin.skills!();
|
|
101
|
+
const cfnSkill = skills.find((s) => s.name === "aws-cloudformation");
|
|
102
|
+
expect(cfnSkill).toBeDefined();
|
|
103
|
+
expect(cfnSkill!.description.length).toBeGreaterThan(0);
|
|
104
|
+
expect(cfnSkill!.content.length).toBeGreaterThan(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("aws-cloudformation skill has triggers", () => {
|
|
108
|
+
const skills = awsPlugin.skills!();
|
|
109
|
+
const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
|
|
110
|
+
expect(cfnSkill.triggers).toBeDefined();
|
|
111
|
+
expect(cfnSkill.triggers!.length).toBeGreaterThanOrEqual(1);
|
|
112
|
+
|
|
113
|
+
const filePatternTrigger = cfnSkill.triggers!.find((t) => t.type === "file-pattern");
|
|
114
|
+
expect(filePatternTrigger).toBeDefined();
|
|
115
|
+
expect(filePatternTrigger!.value).toContain("*.aws.ts");
|
|
116
|
+
|
|
117
|
+
const contextTrigger = cfnSkill.triggers!.find((t) => t.type === "context");
|
|
118
|
+
expect(contextTrigger).toBeDefined();
|
|
119
|
+
expect(contextTrigger!.value).toBe("aws");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("aws-cloudformation skill has parameters", () => {
|
|
123
|
+
const skills = awsPlugin.skills!();
|
|
124
|
+
const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
|
|
125
|
+
expect(cfnSkill.parameters).toBeDefined();
|
|
126
|
+
expect(cfnSkill.parameters!.length).toBeGreaterThanOrEqual(1);
|
|
127
|
+
|
|
128
|
+
const resourceTypeParam = cfnSkill.parameters!.find((p) => p.name === "resourceType");
|
|
129
|
+
expect(resourceTypeParam).toBeDefined();
|
|
130
|
+
expect(resourceTypeParam!.description.length).toBeGreaterThan(0);
|
|
131
|
+
expect(resourceTypeParam!.type).toBe("string");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("aws-cloudformation skill has examples", () => {
|
|
135
|
+
const skills = awsPlugin.skills!();
|
|
136
|
+
const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
|
|
137
|
+
expect(cfnSkill.examples).toBeDefined();
|
|
138
|
+
expect(cfnSkill.examples!.length).toBeGreaterThanOrEqual(1);
|
|
139
|
+
|
|
140
|
+
const s3Example = cfnSkill.examples![0];
|
|
141
|
+
expect(s3Example.title.length).toBeGreaterThan(0);
|
|
142
|
+
expect(s3Example.output).toBeDefined();
|
|
143
|
+
expect(s3Example.output!).toContain("Bucket");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("skill content is valid markdown with frontmatter", () => {
|
|
147
|
+
const skills = awsPlugin.skills!();
|
|
148
|
+
const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
|
|
149
|
+
expect(cfnSkill.content).toContain("---");
|
|
150
|
+
expect(cfnSkill.content).toContain("# AWS CloudFormation with Chant");
|
|
151
|
+
expect(cfnSkill.content).toContain("## Common Resource Types");
|
|
152
|
+
expect(cfnSkill.content).toContain("## Best Practices");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// -----------------------------------------------------------------------
|
|
157
|
+
// Required lifecycle methods
|
|
158
|
+
// -----------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
describe("lifecycle methods", () => {
|
|
161
|
+
test("generate is a function", () => {
|
|
162
|
+
expect(typeof awsPlugin.generate).toBe("function");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("validate is a function", () => {
|
|
166
|
+
expect(typeof awsPlugin.validate).toBe("function");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("coverage is a function", () => {
|
|
170
|
+
expect(typeof awsPlugin.coverage).toBe("function");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("package is a function", () => {
|
|
174
|
+
expect(typeof awsPlugin.package).toBe("function");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("rollback is a function", () => {
|
|
178
|
+
expect(typeof awsPlugin.rollback).toBe("function");
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// -----------------------------------------------------------------------
|
|
183
|
+
// MCP tools
|
|
184
|
+
// -----------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
describe("mcpTools", () => {
|
|
187
|
+
test("returns at least one tool", () => {
|
|
188
|
+
const tools = awsPlugin.mcpTools!();
|
|
189
|
+
expect(tools.length).toBeGreaterThanOrEqual(1);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("diff tool has correct structure", () => {
|
|
193
|
+
const tools = awsPlugin.mcpTools!();
|
|
194
|
+
const diffTool = tools.find((t) => t.name === "diff");
|
|
195
|
+
expect(diffTool).toBeDefined();
|
|
196
|
+
expect(diffTool!.description.length).toBeGreaterThan(0);
|
|
197
|
+
expect(diffTool!.inputSchema.type).toBe("object");
|
|
198
|
+
expect(diffTool!.inputSchema.properties.path).toBeDefined();
|
|
199
|
+
expect(typeof diffTool!.handler).toBe("function");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("diff tool schema has path as required", () => {
|
|
203
|
+
const tools = awsPlugin.mcpTools!();
|
|
204
|
+
const diffTool = tools.find((t) => t.name === "diff")!;
|
|
205
|
+
expect(diffTool.inputSchema.required).toContain("path");
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// -----------------------------------------------------------------------
|
|
210
|
+
// MCP resources
|
|
211
|
+
// -----------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
describe("mcpResources", () => {
|
|
214
|
+
test("returns at least one resource", () => {
|
|
215
|
+
const resources = awsPlugin.mcpResources!();
|
|
216
|
+
expect(resources.length).toBeGreaterThanOrEqual(1);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("resource-catalog has correct structure", () => {
|
|
220
|
+
const resources = awsPlugin.mcpResources!();
|
|
221
|
+
const catalog = resources.find((r) => r.uri === "resource-catalog");
|
|
222
|
+
expect(catalog).toBeDefined();
|
|
223
|
+
expect(catalog!.name.length).toBeGreaterThan(0);
|
|
224
|
+
expect(catalog!.description.length).toBeGreaterThan(0);
|
|
225
|
+
expect(catalog!.mimeType).toBe("application/json");
|
|
226
|
+
expect(typeof catalog!.handler).toBe("function");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("resource-catalog handler returns JSON array of resources", async () => {
|
|
230
|
+
const resources = awsPlugin.mcpResources!();
|
|
231
|
+
const catalog = resources.find((r) => r.uri === "resource-catalog")!;
|
|
232
|
+
const content = await catalog.handler();
|
|
233
|
+
|
|
234
|
+
const parsed = JSON.parse(content) as Array<{ className: string; resourceType: string }>;
|
|
235
|
+
expect(Array.isArray(parsed)).toBe(true);
|
|
236
|
+
expect(parsed.length).toBeGreaterThan(100);
|
|
237
|
+
|
|
238
|
+
// Spot-check known resources
|
|
239
|
+
const bucket = parsed.find((r) => r.className === "Bucket");
|
|
240
|
+
expect(bucket).toBeDefined();
|
|
241
|
+
expect(bucket!.resourceType).toBe("AWS::S3::Bucket");
|
|
242
|
+
|
|
243
|
+
const table = parsed.find((r) => r.className === "Table");
|
|
244
|
+
expect(table).toBeDefined();
|
|
245
|
+
expect(table!.resourceType).toBe("AWS::DynamoDB::Table");
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// -----------------------------------------------------------------------
|
|
250
|
+
// LSP completionProvider
|
|
251
|
+
// -----------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
describe("completionProvider", () => {
|
|
254
|
+
test("is defined", () => {
|
|
255
|
+
expect(awsPlugin.completionProvider).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("returns resource completions for new prefix", () => {
|
|
259
|
+
const items = awsPlugin.completionProvider!({
|
|
260
|
+
uri: "file:///a.ts",
|
|
261
|
+
content: "const b = new Bucket",
|
|
262
|
+
position: { line: 0, character: 20 },
|
|
263
|
+
wordAtCursor: "Bucket",
|
|
264
|
+
linePrefix: "const b = new Bucket",
|
|
265
|
+
});
|
|
266
|
+
expect(items.length).toBeGreaterThan(0);
|
|
267
|
+
const bucket = items.find((i) => i.label === "Bucket");
|
|
268
|
+
expect(bucket).toBeDefined();
|
|
269
|
+
expect(bucket!.kind).toBe("resource");
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("returns empty for non-constructor context", () => {
|
|
273
|
+
const items = awsPlugin.completionProvider!({
|
|
274
|
+
uri: "file:///a.ts",
|
|
275
|
+
content: "const x = 42",
|
|
276
|
+
position: { line: 0, character: 13 },
|
|
277
|
+
wordAtCursor: "42",
|
|
278
|
+
linePrefix: "const x = 42",
|
|
279
|
+
});
|
|
280
|
+
expect(items).toHaveLength(0);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// -----------------------------------------------------------------------
|
|
285
|
+
// LSP hoverProvider
|
|
286
|
+
// -----------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
describe("hoverProvider", () => {
|
|
289
|
+
test("is defined", () => {
|
|
290
|
+
expect(awsPlugin.hoverProvider).toBeDefined();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("returns hover info for known resource", () => {
|
|
294
|
+
const info = awsPlugin.hoverProvider!({
|
|
295
|
+
uri: "file:///a.ts",
|
|
296
|
+
content: "new Bucket()",
|
|
297
|
+
position: { line: 0, character: 5 },
|
|
298
|
+
word: "Bucket",
|
|
299
|
+
lineText: "new Bucket()",
|
|
300
|
+
});
|
|
301
|
+
expect(info).toBeDefined();
|
|
302
|
+
expect(info!.contents).toContain("AWS::S3::Bucket");
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("returns undefined for unknown word", () => {
|
|
306
|
+
const info = awsPlugin.hoverProvider!({
|
|
307
|
+
uri: "file:///a.ts",
|
|
308
|
+
content: "xyz",
|
|
309
|
+
position: { line: 0, character: 1 },
|
|
310
|
+
word: "NotAResource12345",
|
|
311
|
+
lineText: "xyz",
|
|
312
|
+
});
|
|
313
|
+
expect(info).toBeUndefined();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|