1import * as AWS from "@/AWS";
2import { AccountPasswordPolicy } from "@/AWS/IAM";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as IAM from "@distilled.cloud/aws/iam";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11// The IAM account password policy is an account-wide singleton. Mutating it is
12// disruptive (it changes the real password requirements for every console
13// user), so this ungated probe never deploys/destroys the resource. It only
14// exercises `list()` and asserts the result is well-formed: the singleton get
15// returns either the one configured policy or `[]` (typed `NoSuchEntityException`
16// when no policy is set on the account).
17test.provider(
18 "list returns the account password policy singleton",
19 () =>
20 Effect.gen(function* () {
21 const provider = yield* Provider.findProvider(AccountPasswordPolicy);
22 const all = yield* provider.list();
23
24 expect(Array.isArray(all)).toBe(true);
25 // Account singleton: 0 (no policy set) or 1 (policy configured).
26 expect(all.length).toBeLessThanOrEqual(1);
27 for (const policy of all) {
28 expect(typeof policy).toBe("object");
29 expect(policy).not.toBeNull();
30 }
31 }),
32 { tags: ["provider:aws", "provider:aws:iam", "live"] },
33);
34
35// Full lifecycle against the dedicated testing account. The policy values are
36// permissive (min length 8, no forced expiry behavior beyond MaxPasswordAge)
37// so concurrently-running IAM login-profile tests using strong passwords are
38// unaffected, and `stack.destroy()` deletes the policy (resetting the account
39// to AWS defaults) at the end.
40test.provider(
41 "deploys MaxPasswordAge as whole wire days and deletes the policy",
42 (stack) =>
43 Effect.gen(function* () {
44 yield* stack.destroy();
45
46 yield* stack.deploy(
47 Effect.gen(function* () {
48 return yield* AccountPasswordPolicy("PasswordPolicy", {
49 MinimumPasswordLength: 8,
50 AllowUsersToChangePassword: true,
51 // Duration.Input prop — converted to whole wire days.
52 MaxPasswordAge: "90 days",
53 });
54 }),
55 );
56
57 // Out-of-band read: the Duration prop landed as 90 wire days.
58 const live = yield* IAM.getAccountPasswordPolicy({});
59 expect(live.PasswordPolicy.MaxPasswordAge).toBe(90);
60 expect(live.PasswordPolicy.MinimumPasswordLength).toBe(8);
61
62 yield* stack.destroy();
63
64 const afterDestroy = yield* IAM.getAccountPasswordPolicy({}).pipe(
65 Effect.option,
66 );
67 expect(afterDestroy._tag).toBe("None");
68 }),
69 { tags: ["provider:aws", "provider:aws:iam", "live"] },
70);