| 1 | import * as AWS from "@/AWS"; |
| 2 | import * as Test from "@/Test/Alchemy"; |
| 3 | import * as ElastiCache from "@distilled.cloud/aws/elasticache"; |
| 4 | import { expect } from "alchemy-test"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | import * as Schedule from "effect/Schedule"; |
| 7 | import { |
| 8 | assertReplicationGroupGone, |
| 9 | getProvisionedNetwork, |
| 10 | shareProvisionedNetwork, |
| 11 | } from "./ProvisionedFixture.ts"; |
| 12 | |
| 13 | const { test, beforeAll, afterAll } = Test.make({ |
| 14 | providers: AWS.providers(), |
| 15 | }); |
| 16 | shareProvisionedNetwork({ beforeAll, afterAll }); |
| 17 | const listTagsWhenAvailable = (resourceName: string) => |
| 18 | ElastiCache.listTagsForResource({ ResourceName: resourceName }).pipe( |
| 19 | Effect.retry({ |
| 20 | while: (error) => |
| 21 | (error as { _tag?: string })._tag === |
| 22 | "InvalidReplicationGroupStateFault", |
| 23 | schedule: Schedule.max([ |
| 24 | Schedule.fixed("15 seconds"), |
| 25 | Schedule.recurs(100), |
| 26 | ]), |
| 27 | }), |
| 28 | ); |
| 29 | |
| 30 | test.provider.skipIf(!process.env.AWS_TEST_SLOW)( |
| 31 | "updates replication-group tags and VPC security groups in place", |
| 32 | (stack) => |
| 33 | Effect.gen(function* () { |
| 34 | yield* stack.destroy(); |
| 35 | const deploy = (phase: "one" | "two") => |
| 36 | stack.deploy( |
| 37 | Effect.gen(function* () { |
| 38 | const net = yield* getProvisionedNetwork; |
| 39 | const replacementSecurityGroup = yield* AWS.EC2.SecurityGroup( |
| 40 | "ReplacementSecurityGroup", |
| 41 | { |
| 42 | vpcId: net.vpcId, |
| 43 | description: "ElastiCache replacement integration-test access", |
| 44 | tags: { fixture: "elasticache-provisioned" }, |
| 45 | }, |
| 46 | ); |
| 47 | const cache = yield* AWS.ElastiCache.ReplicationGroup("Cache", { |
| 48 | description: "alchemy configuration cache", |
| 49 | engine: "valkey", |
| 50 | nodeType: "cache.t4g.micro", |
| 51 | subnetGroupName: net.subnetGroupName, |
| 52 | securityGroupIds: [ |
| 53 | phase === "one" |
| 54 | ? net.securityGroupId |
| 55 | : replacementSecurityGroup.groupId, |
| 56 | ], |
| 57 | replicasPerNodeGroup: 0, |
| 58 | transitEncryptionEnabled: true, |
| 59 | tags: |
| 60 | phase === "one" |
| 61 | ? { |
| 62 | fixture: "elasticache-provisioned", |
| 63 | phase: "one", |
| 64 | remove: "me", |
| 65 | } |
| 66 | : { fixture: "elasticache-provisioned", phase: "two" }, |
| 67 | }); |
| 68 | return { |
| 69 | cache, |
| 70 | activeSecurityGroupId: |
| 71 | phase === "one" |
| 72 | ? net.securityGroupId |
| 73 | : replacementSecurityGroup.groupId, |
| 74 | }; |
| 75 | }), |
| 76 | ); |
| 77 | |
| 78 | const created = yield* deploy("one"); |
| 79 | const createdTags = yield* listTagsWhenAvailable( |
| 80 | created.cache.replicationGroupArn, |
| 81 | ); |
| 82 | expect(createdTags.TagList).toContainEqual({ |
| 83 | Key: "phase", |
| 84 | Value: "one", |
| 85 | }); |
| 86 | expect(createdTags.TagList).toContainEqual({ |
| 87 | Key: "remove", |
| 88 | Value: "me", |
| 89 | }); |
| 90 | |
| 91 | const updated = yield* deploy("two"); |
| 92 | expect(updated.cache.replicationGroupId).toBe( |
| 93 | created.cache.replicationGroupId, |
| 94 | ); |
| 95 | const updatedTags = yield* listTagsWhenAvailable( |
| 96 | updated.cache.replicationGroupArn, |
| 97 | ); |
| 98 | expect(updatedTags.TagList).toContainEqual({ |
| 99 | Key: "phase", |
| 100 | Value: "two", |
| 101 | }); |
| 102 | expect(updatedTags.TagList).not.toContainEqual({ |
| 103 | Key: "remove", |
| 104 | Value: "me", |
| 105 | }); |
| 106 | |
| 107 | const group = yield* ElastiCache.describeReplicationGroups({ |
| 108 | ReplicationGroupId: updated.cache.replicationGroupId, |
| 109 | }); |
| 110 | const memberId = group.ReplicationGroups?.[0]?.MemberClusters?.[0]; |
| 111 | if (!memberId) |
| 112 | return yield* Effect.fail( |
| 113 | new Error("replication group has no member cluster"), |
| 114 | ); |
| 115 | const member = yield* ElastiCache.describeCacheClusters({ |
| 116 | CacheClusterId: memberId, |
| 117 | }); |
| 118 | expect( |
| 119 | member.CacheClusters?.[0]?.SecurityGroups?.map( |
| 120 | (sg) => sg.SecurityGroupId, |
| 121 | ), |
| 122 | ).toEqual([updated.activeSecurityGroupId]); |
| 123 | |
| 124 | yield* stack.destroy(); |
| 125 | yield* assertReplicationGroupGone(updated.cache.replicationGroupId); |
| 126 | }), |
| 127 | { |
| 128 | tags: [ |
| 129 | "provider:aws", |
| 130 | "provider:aws:ec2", |
| 131 | "provider:aws:elasticache", |
| 132 | "live", |
| 133 | ], |
| 134 | timeout: 2_700_000, |
| 135 | }, |
| 136 | ); |