@appthrust/kest 0.1.2 → 0.2.0

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 CHANGED
@@ -337,6 +337,7 @@ The top-level API surface available in every test callback.
337
337
  | ----------------------------------------------------------------------- | ------------------------------------------------ |
338
338
  | `apply(manifest, options?)` | Apply a Kubernetes manifest and register cleanup |
339
339
  | `applyStatus(manifest, options?)` | Apply a status subresource (server-side apply) |
340
+ | `delete(resource, options?)` | Delete a resource by API version, kind, and name |
340
341
  | `get(resource, options?)` | Fetch a resource by API version, kind, and name |
341
342
  | `assert(resource, options?)` | Fetch a resource and run assertions with retries |
342
343
  | `assertList(resource, options?)` | Fetch a list of resources and run assertions |
@@ -347,7 +348,7 @@ The top-level API surface available in every test callback.
347
348
 
348
349
  ### Namespace / Cluster
349
350
 
350
- Returned by `newNamespace()` and `useCluster()` respectively. They expose the same core methods (`apply`, `applyStatus`, `get`, `assert`, `assertList`, `newNamespace`) scoped to their namespace or cluster context.
351
+ Returned by `newNamespace()` and `useCluster()` respectively. They expose the same core methods (`apply`, `applyStatus`, `delete`, `get`, `assert`, `assertList`) scoped to their namespace or cluster context. `Cluster` additionally supports `newNamespace`.
351
352
 
352
353
  ### Action Options
353
354
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appthrust/kest",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Kubernetes E2E testing framework designed for humans and AI alike",
5
5
  "type": "module",
6
6
  "module": "ts/index.ts",
@@ -0,0 +1,25 @@
1
+ import type { K8sResource, K8sResourceReference } from "../apis";
2
+ import type { OneWayMutateDef } from "./types";
3
+
4
+ export const deleteResource = {
5
+ type: "oneWayMutate",
6
+ name: "Delete",
7
+ mutate:
8
+ ({ kubectl }) =>
9
+ async <T extends K8sResource>(resource: K8sResourceReference<T>) => {
10
+ await kubectl.delete(toKubectlType(resource), resource.name);
11
+ return undefined;
12
+ },
13
+ } satisfies OneWayMutateDef<K8sResourceReference, void>;
14
+
15
+ function toKubectlType<T extends K8sResource>(
16
+ resource: K8sResourceReference<T>
17
+ ): string {
18
+ const { kind, apiVersion } = resource;
19
+ const [group, version] = apiVersion.split("/");
20
+ if (version === undefined) {
21
+ // core group cannot include version in the type
22
+ return kind;
23
+ }
24
+ return [kind, version, group].filter(Boolean).join(".");
25
+ }
package/ts/apis/index.ts CHANGED
@@ -13,7 +13,8 @@ import type { $ as BunDollar } from "bun";
13
13
  * Some actions also register cleanup ("revert") handlers which run during
14
14
  * scenario cleanup. For example, {@link Scenario.apply} registers a revert that
15
15
  * deletes the applied resource. One-way mutations such as
16
- * {@link Scenario.applyStatus} do not register a revert.
16
+ * {@link Scenario.applyStatus} and {@link Scenario.delete} do not register a
17
+ * revert.
17
18
  */
18
19
 
19
20
  /**
@@ -88,6 +89,31 @@ export interface Scenario {
88
89
  options?: undefined | ActionOptions
89
90
  ): Promise<void>;
90
91
 
92
+ /**
93
+ * Deletes a Kubernetes resource using `kubectl delete`.
94
+ *
95
+ * This action is retried when it throws.
96
+ *
97
+ * Note: this is a one-way mutation and does not register a cleanup handler.
98
+ *
99
+ * @template T - The expected Kubernetes resource shape.
100
+ * @param resource - Group/version/kind and name of the resource to delete.
101
+ * @param options - Retry options such as timeout and polling interval.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * await s.delete({
106
+ * apiVersion: "v1",
107
+ * kind: "ConfigMap",
108
+ * name: "my-config",
109
+ * });
110
+ * ```
111
+ */
112
+ delete<T extends K8sResource>(
113
+ resource: K8sResourceReference<T>,
114
+ options?: undefined | ActionOptions
115
+ ): Promise<void>;
116
+
91
117
  /**
92
118
  * Fetches a Kubernetes resource and returns it as a typed object.
93
119
  *
@@ -370,6 +396,22 @@ export interface Cluster {
370
396
  options?: undefined | ActionOptions
371
397
  ): Promise<void>;
372
398
 
399
+ /**
400
+ * Deletes a Kubernetes resource in this cluster using `kubectl delete`.
401
+ *
402
+ * This action is retried when it throws.
403
+ *
404
+ * Note: this is a one-way mutation and does not register a cleanup handler.
405
+ *
406
+ * @template T - The expected Kubernetes resource shape.
407
+ * @param resource - Group/version/kind and name of the resource to delete.
408
+ * @param options - Retry options such as timeout and polling interval.
409
+ */
410
+ delete<T extends K8sResource>(
411
+ resource: K8sResourceReference<T>,
412
+ options?: undefined | ActionOptions
413
+ ): Promise<void>;
414
+
373
415
  /**
374
416
  * Fetches a Kubernetes resource by GVK and name.
375
417
  *
@@ -534,6 +576,22 @@ export interface Namespace {
534
576
  options?: undefined | ActionOptions
535
577
  ): Promise<void>;
536
578
 
579
+ /**
580
+ * Deletes a Kubernetes resource in this namespace using `kubectl delete`.
581
+ *
582
+ * This action is retried when it throws.
583
+ *
584
+ * Note: this is a one-way mutation and does not register a cleanup handler.
585
+ *
586
+ * @template T - The expected Kubernetes resource shape.
587
+ * @param resource - Group/version/kind and name of the resource to delete.
588
+ * @param options - Retry options such as timeout and polling interval.
589
+ */
590
+ delete<T extends K8sResource>(
591
+ resource: K8sResourceReference<T>,
592
+ options?: undefined | ActionOptions
593
+ ): Promise<void>;
594
+
537
595
  /**
538
596
  * Fetches a namespaced Kubernetes resource by GVK and name.
539
597
  *
@@ -3,6 +3,7 @@ import { applyNamespace } from "../actions/apply-namespace";
3
3
  import { applyStatus } from "../actions/apply-status";
4
4
  import { assert } from "../actions/assert";
5
5
  import { assertList } from "../actions/assert-list";
6
+ import { deleteResource } from "../actions/delete";
6
7
  import { exec } from "../actions/exec";
7
8
  import { get } from "../actions/get";
8
9
  import type { MutateDef, OneWayMutateDef, QueryDef } from "../actions/types";
@@ -31,6 +32,7 @@ export function createScenario(deps: CreateScenarioOptions): InternalScenario {
31
32
  return {
32
33
  apply: createMutateFn(deps, apply),
33
34
  applyStatus: createOneWayMutateFn(deps, applyStatus),
35
+ delete: createOneWayMutateFn(deps, deleteResource),
34
36
  exec: createMutateFn(deps, exec),
35
37
  get: createQueryFn(deps, get),
36
38
  assert: createQueryFn(deps, assert),
@@ -193,6 +195,7 @@ const createNewNamespaceFn =
193
195
  return {
194
196
  apply: createMutateFn(namespacedDeps, apply),
195
197
  applyStatus: createOneWayMutateFn(namespacedDeps, applyStatus),
198
+ delete: createOneWayMutateFn(namespacedDeps, deleteResource),
196
199
  get: createQueryFn(namespacedDeps, get),
197
200
  assert: createQueryFn(namespacedDeps, assert),
198
201
  assertList: createQueryFn(namespacedDeps, assertList),
@@ -212,6 +215,7 @@ const createUseClusterFn =
212
215
  return {
213
216
  apply: createMutateFn(clusterDeps, apply),
214
217
  applyStatus: createOneWayMutateFn(clusterDeps, applyStatus),
218
+ delete: createOneWayMutateFn(clusterDeps, deleteResource),
215
219
  get: createQueryFn(clusterDeps, get),
216
220
  assert: createQueryFn(clusterDeps, assert),
217
221
  assertList: createQueryFn(clusterDeps, assertList),