| 1 | import * as AWS from "@/AWS"; |
| 2 | import { SecurityConfiguration } from "@/AWS/EMR/SecurityConfiguration.ts"; |
| 3 | import * as Provider from "@/Provider"; |
| 4 | import * as Test from "@/Test/Alchemy"; |
| 5 | import * as emr from "@distilled.cloud/aws/emr"; |
| 6 | import { expect } from "alchemy-test"; |
| 7 | import * as Effect from "effect/Effect"; |
| 8 | |
| 9 | const { test } = Test.make({ providers: AWS.providers() }); |
| 10 | |
| 11 | const configName = "alchemy-test-emr-security-configuration"; |
| 12 | |
| 13 | // Ungated typed-error probe: proves the distilled emr error union carries the |
| 14 | // SecurityConfigurationNotFound tag this provider's read/delete paths depend |
| 15 | // on (EMR overloads InvalidRequestException with a message-only distinction). |
| 16 | test.provider( |
| 17 | "describeSecurityConfiguration on a nonexistent name fails with SecurityConfigurationNotFound", |
| 18 | () => |
| 19 | Effect.gen(function* () { |
| 20 | const error = yield* Effect.flip( |
| 21 | emr.describeSecurityConfiguration({ |
| 22 | Name: "alchemy-emr-nonexistent-probe", |
| 23 | }), |
| 24 | ); |
| 25 | expect(error._tag).toBe("SecurityConfigurationNotFound"); |
| 26 | }), |
| 27 | { tags: ["provider:aws", "provider:aws:emr", "live"] }, |
| 28 | ); |
| 29 | |
| 30 | const imdsDocument = { |
| 31 | InstanceMetadataServiceConfiguration: { |
| 32 | MinimumInstanceMetadataServiceVersion: 2, |
| 33 | HttpPutResponseHopLimit: 1, |
| 34 | }, |
| 35 | }; |
| 36 | |
| 37 | const encryptionDocument = { |
| 38 | EncryptionConfiguration: { |
| 39 | EnableInTransitEncryption: false, |
| 40 | EnableAtRestEncryption: false, |
| 41 | }, |
| 42 | }; |
| 43 | |
| 44 | test.provider( |
| 45 | "lifecycle: create, update content in place, replace on rename, destroy", |
| 46 | (stack) => |
| 47 | Effect.gen(function* () { |
| 48 | yield* stack.destroy(); |
| 49 | |
| 50 | // Create. |
| 51 | const deployed = yield* stack.deploy( |
| 52 | Effect.gen(function* () { |
| 53 | return yield* SecurityConfiguration("Config", { |
| 54 | securityConfigurationName: configName, |
| 55 | securityConfiguration: imdsDocument, |
| 56 | }); |
| 57 | }), |
| 58 | ); |
| 59 | expect(deployed.securityConfigurationName).toBe(configName); |
| 60 | |
| 61 | // Out-of-band verification via distilled. |
| 62 | const created = yield* emr.describeSecurityConfiguration({ |
| 63 | Name: configName, |
| 64 | }); |
| 65 | expect(JSON.parse(created.SecurityConfiguration ?? "{}")).toEqual( |
| 66 | imdsDocument, |
| 67 | ); |
| 68 | |
| 69 | // Canonical list() coverage. |
| 70 | const provider = yield* Provider.findProvider(SecurityConfiguration); |
| 71 | const all = yield* provider.list(); |
| 72 | expect(all.some((c) => c.securityConfigurationName === configName)).toBe( |
| 73 | true, |
| 74 | ); |
| 75 | |
| 76 | // Update content in place — the document is immutable in EMR, so the |
| 77 | // provider converges by deleting and recreating under the same name |
| 78 | // (no replacement from the engine's perspective). |
| 79 | yield* stack.deploy( |
| 80 | Effect.gen(function* () { |
| 81 | return yield* SecurityConfiguration("Config", { |
| 82 | securityConfigurationName: configName, |
| 83 | securityConfiguration: encryptionDocument, |
| 84 | }); |
| 85 | }), |
| 86 | ); |
| 87 | const updated = yield* emr.describeSecurityConfiguration({ |
| 88 | Name: configName, |
| 89 | }); |
| 90 | expect(JSON.parse(updated.SecurityConfiguration ?? "{}")).toEqual( |
| 91 | encryptionDocument, |
| 92 | ); |
| 93 | |
| 94 | // Rename — diff returns replace; the new name exists, the old is gone. |
| 95 | yield* stack.deploy( |
| 96 | Effect.gen(function* () { |
| 97 | return yield* SecurityConfiguration("Config", { |
| 98 | securityConfigurationName: `${configName}-renamed`, |
| 99 | securityConfiguration: encryptionDocument, |
| 100 | }); |
| 101 | }), |
| 102 | ); |
| 103 | const renamed = yield* emr.describeSecurityConfiguration({ |
| 104 | Name: `${configName}-renamed`, |
| 105 | }); |
| 106 | expect(renamed.Name).toBe(`${configName}-renamed`); |
| 107 | const oldGone = yield* Effect.flip( |
| 108 | emr.describeSecurityConfiguration({ Name: configName }), |
| 109 | ); |
| 110 | expect(oldGone._tag).toBe("SecurityConfigurationNotFound"); |
| 111 | |
| 112 | // Destroy — verify gone out of band. |
| 113 | yield* stack.destroy(); |
| 114 | const afterDestroy = yield* Effect.flip( |
| 115 | emr.describeSecurityConfiguration({ Name: `${configName}-renamed` }), |
| 116 | ); |
| 117 | expect(afterDestroy._tag).toBe("SecurityConfigurationNotFound"); |
| 118 | }), |
| 119 | { tags: ["provider:aws", "provider:aws:emr", "live"], timeout: 120_000 }, |
| 120 | ); |