@intentius/chant-lexicon-k8s 0.0.12
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/dist/integrity.json +32 -0
- package/dist/manifest.json +8 -0
- package/dist/meta.json +1413 -0
- package/dist/rules/hardcoded-namespace.ts +56 -0
- package/dist/rules/k8s-helpers.ts +149 -0
- package/dist/rules/wk8005.ts +59 -0
- package/dist/rules/wk8006.ts +68 -0
- package/dist/rules/wk8041.ts +73 -0
- package/dist/rules/wk8042.ts +48 -0
- package/dist/rules/wk8101.ts +65 -0
- package/dist/rules/wk8102.ts +42 -0
- package/dist/rules/wk8103.ts +45 -0
- package/dist/rules/wk8104.ts +69 -0
- package/dist/rules/wk8105.ts +45 -0
- package/dist/rules/wk8201.ts +55 -0
- package/dist/rules/wk8202.ts +46 -0
- package/dist/rules/wk8203.ts +46 -0
- package/dist/rules/wk8204.ts +54 -0
- package/dist/rules/wk8205.ts +56 -0
- package/dist/rules/wk8207.ts +45 -0
- package/dist/rules/wk8208.ts +45 -0
- package/dist/rules/wk8209.ts +45 -0
- package/dist/rules/wk8301.ts +51 -0
- package/dist/rules/wk8302.ts +46 -0
- package/dist/rules/wk8303.ts +96 -0
- package/dist/skills/chant-k8s.md +433 -0
- package/dist/types/index.d.ts +2934 -0
- package/package.json +30 -0
- package/src/actions/actions.test.ts +83 -0
- package/src/actions/apps.ts +23 -0
- package/src/actions/batch.ts +9 -0
- package/src/actions/core.ts +62 -0
- package/src/actions/index.ts +50 -0
- package/src/actions/networking.ts +15 -0
- package/src/actions/rbac.ts +13 -0
- package/src/codegen/docs-cli.ts +3 -0
- package/src/codegen/docs.ts +1147 -0
- package/src/codegen/generate-cli.ts +41 -0
- package/src/codegen/generate-lexicon.ts +69 -0
- package/src/codegen/generate-typescript.ts +97 -0
- package/src/codegen/generate.ts +144 -0
- package/src/codegen/naming.test.ts +63 -0
- package/src/codegen/naming.ts +187 -0
- package/src/codegen/package.ts +56 -0
- package/src/codegen/patches.ts +108 -0
- package/src/codegen/snapshot.test.ts +95 -0
- package/src/codegen/typecheck.test.ts +24 -0
- package/src/codegen/typecheck.ts +4 -0
- package/src/codegen/versions.ts +43 -0
- package/src/composites/autoscaled-service.ts +236 -0
- package/src/composites/composites.test.ts +1109 -0
- package/src/composites/cron-workload.ts +167 -0
- package/src/composites/index.ts +14 -0
- package/src/composites/namespace-env.ts +163 -0
- package/src/composites/node-agent.ts +224 -0
- package/src/composites/stateful-app.ts +134 -0
- package/src/composites/web-app.ts +180 -0
- package/src/composites/worker-pool.ts +230 -0
- package/src/coverage.test.ts +27 -0
- package/src/coverage.ts +35 -0
- package/src/crd/loader.ts +112 -0
- package/src/crd/parser.test.ts +217 -0
- package/src/crd/parser.ts +279 -0
- package/src/crd/types.ts +54 -0
- package/src/default-labels.test.ts +111 -0
- package/src/default-labels.ts +122 -0
- package/src/generated/index.d.ts +2934 -0
- package/src/generated/index.ts +203 -0
- package/src/generated/lexicon-k8s.json +1413 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +121 -0
- package/src/import/generator.ts +285 -0
- package/src/import/parser.test.ts +156 -0
- package/src/import/parser.ts +204 -0
- package/src/import/roundtrip.test.ts +86 -0
- package/src/index.ts +38 -0
- package/src/lint/post-synth/k8s-helpers.test.ts +219 -0
- package/src/lint/post-synth/k8s-helpers.ts +149 -0
- package/src/lint/post-synth/post-synth.test.ts +969 -0
- package/src/lint/post-synth/wk8005.ts +59 -0
- package/src/lint/post-synth/wk8006.ts +68 -0
- package/src/lint/post-synth/wk8041.ts +73 -0
- package/src/lint/post-synth/wk8042.ts +48 -0
- package/src/lint/post-synth/wk8101.ts +65 -0
- package/src/lint/post-synth/wk8102.ts +42 -0
- package/src/lint/post-synth/wk8103.ts +45 -0
- package/src/lint/post-synth/wk8104.ts +69 -0
- package/src/lint/post-synth/wk8105.ts +45 -0
- package/src/lint/post-synth/wk8201.ts +55 -0
- package/src/lint/post-synth/wk8202.ts +46 -0
- package/src/lint/post-synth/wk8203.ts +46 -0
- package/src/lint/post-synth/wk8204.ts +54 -0
- package/src/lint/post-synth/wk8205.ts +56 -0
- package/src/lint/post-synth/wk8207.ts +45 -0
- package/src/lint/post-synth/wk8208.ts +45 -0
- package/src/lint/post-synth/wk8209.ts +45 -0
- package/src/lint/post-synth/wk8301.ts +51 -0
- package/src/lint/post-synth/wk8302.ts +46 -0
- package/src/lint/post-synth/wk8303.ts +96 -0
- package/src/lint/rules/hardcoded-namespace.ts +56 -0
- package/src/lint/rules/rules.test.ts +69 -0
- package/src/lsp/completions.test.ts +64 -0
- package/src/lsp/completions.ts +20 -0
- package/src/lsp/hover.test.ts +69 -0
- package/src/lsp/hover.ts +68 -0
- package/src/package-cli.ts +28 -0
- package/src/plugin.test.ts +209 -0
- package/src/plugin.ts +915 -0
- package/src/serializer.test.ts +275 -0
- package/src/serializer.ts +278 -0
- package/src/spec/fetch.test.ts +24 -0
- package/src/spec/fetch.ts +68 -0
- package/src/spec/parse.test.ts +102 -0
- package/src/spec/parse.ts +477 -0
- package/src/testdata/manifests/configmap.yaml +7 -0
- package/src/testdata/manifests/deployment.yaml +22 -0
- package/src/testdata/manifests/full-app.yaml +61 -0
- package/src/testdata/manifests/secret.yaml +7 -0
- package/src/testdata/manifests/service.yaml +15 -0
- package/src/validate-cli.ts +21 -0
- package/src/validate.test.ts +29 -0
- package/src/validate.ts +46 -0
- package/src/variables.ts +36 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
7
|
+
const hasGenerated = existsSync(
|
|
8
|
+
join(pkgDir, "src", "generated", "lexicon-k8s.json"),
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
describe("LSP hover", () => {
|
|
12
|
+
test.skipIf(!hasGenerated)(
|
|
13
|
+
"returns hover info for known resource (Deployment)",
|
|
14
|
+
async () => {
|
|
15
|
+
const { k8sHover } = await import("./hover");
|
|
16
|
+
const result = k8sHover({
|
|
17
|
+
word: "Deployment",
|
|
18
|
+
position: { line: 0, character: 0 },
|
|
19
|
+
} as any);
|
|
20
|
+
|
|
21
|
+
expect(result).toBeDefined();
|
|
22
|
+
if (result) {
|
|
23
|
+
expect(typeof result).toBe("object");
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
test.skipIf(!hasGenerated)(
|
|
29
|
+
"returns undefined for unknown word",
|
|
30
|
+
async () => {
|
|
31
|
+
const { k8sHover } = await import("./hover");
|
|
32
|
+
const result = k8sHover({
|
|
33
|
+
word: "XyzNonExistent",
|
|
34
|
+
position: { line: 0, character: 0 },
|
|
35
|
+
} as any);
|
|
36
|
+
|
|
37
|
+
expect(result).toBeUndefined();
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
test.skipIf(!hasGenerated)("returns undefined for empty string", async () => {
|
|
42
|
+
const { k8sHover } = await import("./hover");
|
|
43
|
+
const result = k8sHover({
|
|
44
|
+
word: "",
|
|
45
|
+
position: { line: 0, character: 0 },
|
|
46
|
+
} as any);
|
|
47
|
+
|
|
48
|
+
expect(result).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test.skipIf(!hasGenerated)("hover includes resource info", async () => {
|
|
52
|
+
const { k8sHover } = await import("./hover");
|
|
53
|
+
const result = k8sHover({
|
|
54
|
+
word: "Service",
|
|
55
|
+
position: { line: 0, character: 0 },
|
|
56
|
+
} as any);
|
|
57
|
+
|
|
58
|
+
if (result && typeof result === "object") {
|
|
59
|
+
const content =
|
|
60
|
+
typeof result.contents === "string"
|
|
61
|
+
? result.contents
|
|
62
|
+
: typeof result.contents === "object" && "value" in result.contents
|
|
63
|
+
? result.contents.value
|
|
64
|
+
: JSON.stringify(result);
|
|
65
|
+
// Hover should mention the resource type
|
|
66
|
+
expect(content.length).toBeGreaterThan(0);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
package/src/lsp/hover.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
import type { HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
|
|
3
|
+
import { LexiconIndex, lexiconHover, type LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
let cachedIndex: LexiconIndex | null = null;
|
|
7
|
+
|
|
8
|
+
function getIndex(): LexiconIndex {
|
|
9
|
+
if (cachedIndex) return cachedIndex;
|
|
10
|
+
const data = require("../generated/lexicon-k8s.json") as Record<string, LexiconEntry>;
|
|
11
|
+
cachedIndex = new LexiconIndex(data);
|
|
12
|
+
return cachedIndex;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Provide hover information for Kubernetes resource types.
|
|
17
|
+
*/
|
|
18
|
+
export function k8sHover(ctx: HoverContext): HoverInfo | undefined {
|
|
19
|
+
return lexiconHover(ctx, getIndex(), resourceHover);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resourceHover(className: string, entry: LexiconEntry): HoverInfo | undefined {
|
|
23
|
+
const lines: string[] = [];
|
|
24
|
+
|
|
25
|
+
lines.push(`**${className}**`);
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push(`K8s resource type: \`${entry.resourceType}\``);
|
|
28
|
+
|
|
29
|
+
// Show apiVersion if stored in attrs
|
|
30
|
+
const apiVersion = entry.attrs?.apiVersion;
|
|
31
|
+
if (apiVersion) {
|
|
32
|
+
lines.push(`apiVersion: \`${apiVersion}\``);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Show GVK kind if stored in attrs
|
|
36
|
+
const gvkKind = entry.attrs?.gvkKind;
|
|
37
|
+
if (gvkKind) {
|
|
38
|
+
lines.push(`kind: \`${gvkKind}\``);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (entry.kind === "resource") {
|
|
42
|
+
lines.push("");
|
|
43
|
+
lines.push("*Resource — serialized as a top-level Kubernetes object*");
|
|
44
|
+
} else {
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push("*Property — used as a nested value in resource specs*");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (entry.attrs && Object.keys(entry.attrs).length > 0) {
|
|
50
|
+
const displayAttrs = Object.entries(entry.attrs).filter(
|
|
51
|
+
([k]) => k !== "apiVersion" && k !== "gvkKind",
|
|
52
|
+
);
|
|
53
|
+
if (displayAttrs.length > 0) {
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push("**Attributes:**");
|
|
56
|
+
for (const [key, value] of displayAttrs) {
|
|
57
|
+
lines.push(`- \`${key}\` \u2192 \`${value}\``);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (entry.primaryIdentifier && entry.primaryIdentifier.length > 0) {
|
|
63
|
+
lines.push("");
|
|
64
|
+
lines.push(`**Primary identifier:** ${entry.primaryIdentifier.map((p) => `\`${p}\``).join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { contents: lines.join("\n") };
|
|
68
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for Kubernetes lexicon packaging.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { packageLexicon } from "./codegen/package";
|
|
7
|
+
import { writeBundleSpec } from "@intentius/chant/codegen/package";
|
|
8
|
+
import { join, dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
|
|
11
|
+
const pkgDir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
15
|
+
const force = process.argv.includes("--force") || process.argv.includes("-f");
|
|
16
|
+
|
|
17
|
+
const { spec, stats } = await packageLexicon({ verbose, force });
|
|
18
|
+
|
|
19
|
+
const distDir = join(pkgDir, "..", "dist");
|
|
20
|
+
writeBundleSpec(spec, distDir);
|
|
21
|
+
|
|
22
|
+
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
main().catch((err) => {
|
|
26
|
+
console.error("Packaging failed:", err);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { k8sPlugin } from "./plugin";
|
|
6
|
+
|
|
7
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const hasGenerated = existsSync(
|
|
9
|
+
join(pkgDir, "src", "generated", "lexicon-k8s.json"),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
describe("k8sPlugin", () => {
|
|
13
|
+
test("name is k8s", () => {
|
|
14
|
+
expect(k8sPlugin.name).toBe("k8s");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("serializer is k8sSerializer", () => {
|
|
18
|
+
expect(k8sPlugin.serializer).toBeDefined();
|
|
19
|
+
expect(k8sPlugin.serializer.name).toBe("k8s");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("serializer rulePrefix is WK8", () => {
|
|
23
|
+
expect(k8sPlugin.serializer.rulePrefix).toBe("WK8");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("lintRules() returns array with WK8001", () => {
|
|
27
|
+
const rules = k8sPlugin.lintRules!();
|
|
28
|
+
expect(Array.isArray(rules)).toBe(true);
|
|
29
|
+
expect(rules.length).toBeGreaterThanOrEqual(1);
|
|
30
|
+
expect(rules.some((r) => r.id === "WK8001")).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("postSynthChecks() returns array of 20 checks", () => {
|
|
34
|
+
const checks = k8sPlugin.postSynthChecks!();
|
|
35
|
+
expect(Array.isArray(checks)).toBe(true);
|
|
36
|
+
expect(checks.length).toBe(20);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("intrinsics() returns empty array", () => {
|
|
40
|
+
const intrinsics = k8sPlugin.intrinsics!();
|
|
41
|
+
expect(intrinsics).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("initTemplates", () => {
|
|
45
|
+
const templateCases = [
|
|
46
|
+
{
|
|
47
|
+
id: undefined,
|
|
48
|
+
label: "default",
|
|
49
|
+
expectedConstructors: ["new Deployment", "new Service"],
|
|
50
|
+
unexpectedConstructors: ["new StatefulSet", "new HorizontalPodAutoscaler"],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "microservice",
|
|
54
|
+
label: "microservice",
|
|
55
|
+
expectedConstructors: [
|
|
56
|
+
"new Deployment",
|
|
57
|
+
"new Service",
|
|
58
|
+
"new HorizontalPodAutoscaler",
|
|
59
|
+
"new PodDisruptionBudget",
|
|
60
|
+
],
|
|
61
|
+
unexpectedConstructors: ["new StatefulSet"],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "stateful",
|
|
65
|
+
label: "stateful",
|
|
66
|
+
expectedConstructors: ["new StatefulSet", "new Service"],
|
|
67
|
+
unexpectedConstructors: ["new Deployment", "new HorizontalPodAutoscaler"],
|
|
68
|
+
},
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
for (const tc of templateCases) {
|
|
72
|
+
describe(`${tc.label} template`, () => {
|
|
73
|
+
test("returns src with non-empty infra.ts", () => {
|
|
74
|
+
const result = k8sPlugin.initTemplates!(tc.id);
|
|
75
|
+
expect(result.src).toBeDefined();
|
|
76
|
+
expect(typeof result.src["infra.ts"]).toBe("string");
|
|
77
|
+
expect(result.src["infra.ts"].length).toBeGreaterThan(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("contains expected resource constructors", () => {
|
|
81
|
+
const src = k8sPlugin.initTemplates!(tc.id).src["infra.ts"];
|
|
82
|
+
for (const ctor of tc.expectedConstructors) {
|
|
83
|
+
expect(src).toContain(ctor);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("does not contain unrelated constructors", () => {
|
|
88
|
+
const src = k8sPlugin.initTemplates!(tc.id).src["infra.ts"];
|
|
89
|
+
for (const ctor of tc.unexpectedConstructors) {
|
|
90
|
+
expect(src).not.toContain(ctor);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("has balanced braces and parentheses", () => {
|
|
95
|
+
const src = k8sPlugin.initTemplates!(tc.id).src["infra.ts"];
|
|
96
|
+
let braces = 0;
|
|
97
|
+
let parens = 0;
|
|
98
|
+
for (const ch of src) {
|
|
99
|
+
if (ch === "{") braces++;
|
|
100
|
+
else if (ch === "}") braces--;
|
|
101
|
+
else if (ch === "(") parens++;
|
|
102
|
+
else if (ch === ")") parens--;
|
|
103
|
+
}
|
|
104
|
+
expect(braces).toBe(0);
|
|
105
|
+
expect(parens).toBe(0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("has valid import statement", () => {
|
|
109
|
+
const src = k8sPlugin.initTemplates!(tc.id).src["infra.ts"];
|
|
110
|
+
expect(src).toMatch(/^import \{[^}]+\} from "@intentius\/chant-lexicon-k8s"/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("has at least one export", () => {
|
|
114
|
+
const src = k8sPlugin.initTemplates!(tc.id).src["infra.ts"];
|
|
115
|
+
expect(src).toContain("export const ");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
test("stateful template uses headless service", () => {
|
|
121
|
+
const src = k8sPlugin.initTemplates!("stateful").src["infra.ts"];
|
|
122
|
+
expect(src).toContain('clusterIP: "None"');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("detectTemplate() recognizes K8s YAML by apiVersion/kind", () => {
|
|
127
|
+
expect(k8sPlugin.detectTemplate!({ apiVersion: "apps/v1", kind: "Deployment" })).toBe(true);
|
|
128
|
+
expect(k8sPlugin.detectTemplate!({ apiVersion: "v1", kind: "Service" })).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("detectTemplate() rejects non-K8s data", () => {
|
|
132
|
+
expect(k8sPlugin.detectTemplate!({})).toBe(false);
|
|
133
|
+
expect(k8sPlugin.detectTemplate!(null)).toBe(false);
|
|
134
|
+
expect(k8sPlugin.detectTemplate!({ stages: [] })).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("skills() returns array with chant-k8s skill", () => {
|
|
138
|
+
const skills = k8sPlugin.skills!();
|
|
139
|
+
expect(Array.isArray(skills)).toBe(true);
|
|
140
|
+
expect(skills.length).toBeGreaterThanOrEqual(1);
|
|
141
|
+
expect(skills[0].name).toBe("chant-k8s");
|
|
142
|
+
expect(skills[0].content).toContain("Kubernetes Operational Playbook");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test.skipIf(!hasGenerated)("completionProvider() returns a function result", () => {
|
|
146
|
+
// Calling with minimal context should not throw
|
|
147
|
+
const result = k8sPlugin.completionProvider!({
|
|
148
|
+
prefix: "",
|
|
149
|
+
line: "",
|
|
150
|
+
position: { line: 0, character: 0 },
|
|
151
|
+
triggerKind: 1,
|
|
152
|
+
} as any);
|
|
153
|
+
expect(result).toBeDefined();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test.skipIf(!hasGenerated)("hoverProvider() returns function result", () => {
|
|
157
|
+
const result = k8sPlugin.hoverProvider!({
|
|
158
|
+
word: "Deployment",
|
|
159
|
+
position: { line: 0, character: 0 },
|
|
160
|
+
} as any);
|
|
161
|
+
// May return undefined if no generated files exist
|
|
162
|
+
expect(result !== null).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("templateParser() returns K8sParser", () => {
|
|
166
|
+
const parser = k8sPlugin.templateParser!();
|
|
167
|
+
expect(parser).toBeDefined();
|
|
168
|
+
expect(typeof parser.parse).toBe("function");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("templateGenerator() returns K8sGenerator", () => {
|
|
172
|
+
const generator = k8sPlugin.templateGenerator!();
|
|
173
|
+
expect(generator).toBeDefined();
|
|
174
|
+
expect(typeof generator.generate).toBe("function");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("mcpTools() returns diff tool", () => {
|
|
178
|
+
const tools = k8sPlugin.mcpTools!();
|
|
179
|
+
expect(Array.isArray(tools)).toBe(true);
|
|
180
|
+
expect(tools.some((t) => t.name === "diff")).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("mcpResources() returns resource-catalog and examples", () => {
|
|
184
|
+
const resources = k8sPlugin.mcpResources!();
|
|
185
|
+
expect(Array.isArray(resources)).toBe(true);
|
|
186
|
+
expect(resources.some((r) => r.uri === "resource-catalog")).toBe(true);
|
|
187
|
+
expect(resources.some((r) => r.uri === "examples/basic-deployment")).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("docs() method exists", () => {
|
|
191
|
+
expect(typeof k8sPlugin.docs).toBe("function");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("generate() method exists", () => {
|
|
195
|
+
expect(typeof k8sPlugin.generate).toBe("function");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("validate() method exists", () => {
|
|
199
|
+
expect(typeof k8sPlugin.validate).toBe("function");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("coverage() method exists", () => {
|
|
203
|
+
expect(typeof k8sPlugin.coverage).toBe("function");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("package() method exists", () => {
|
|
207
|
+
expect(typeof k8sPlugin.package).toBe("function");
|
|
208
|
+
});
|
|
209
|
+
});
|