1import * as AWS from "@/AWS";
2import { AWSEnvironment } from "@/AWS/Environment";
3import { AccountAssignment, Group, PermissionSet } from "@/AWS/IdentityCenter";
4import * as Provider from "@/Provider";
5import * as Test from "@/Test/Alchemy";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11// Identity Center requires an enabled SSO instance / identity store in the
12// testing account. If unavailable, `resolveInstance` fails with:
13// Error: "Unable to resolve a single visible Identity Center instance; pass instanceArn explicitly"
14// The testing account is an organization management account where
15// `CreateInstance` fails with a typed ValidationException ("Organization
16// management account is not allowed to perform the operation."), so an
17// instance cannot be provisioned programmatically. Gate the live list test
18// behind ALCHEMY_TEST_IDENTITY_CENTER=1 so an entitled account runs it
19// unchanged.
20const SKIP_IDENTITY_CENTER = !process.env.ALCHEMY_TEST_IDENTITY_CENTER;
21
22// Canonical `list()` test for an account assignment (a fan-out collection:
23// instances -> permission sets -> accounts -> assignments). Deploy a real
24// permission set + group + assignment targeting the current account, resolve
25// the provider from context via the typed `findProvider`, call `list()`, and
26// assert the deployed assignment appears in the exhaustively-paginated result.
27test.provider.skipIf(SKIP_IDENTITY_CENTER)(
28 "list enumerates the deployed account assignment",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const { accountId } = yield* AWSEnvironment.current;
34
35 const assignment = yield* stack.deploy(
36 Effect.gen(function* () {
37 const permissionSet = yield* PermissionSet("ListPermissionSet", {
38 name: "alchemy-list-test-permission-set",
39 description: "Permission set used to verify list() enumeration",
40 sessionDuration: "1 hour",
41 });
42
43 const group = yield* Group("ListGroup", {
44 displayName: "alchemy-list-assignment-group",
45 description: "Group used to verify assignment list() enumeration",
46 });
47
48 return yield* AccountAssignment("ListAssignment", {
49 permissionSetArn: permissionSet.permissionSetArn,
50 principalType: "GROUP",
51 principalId: group.groupId,
52 targetId: accountId,
53 });
54 }),
55 );
56
57 const provider = yield* Provider.findProvider(AccountAssignment);
58 const all = yield* provider.list();
59
60 expect(
61 all.some(
62 (a) =>
63 a.permissionSetArn === assignment.permissionSetArn &&
64 a.principalId === assignment.principalId &&
65 a.targetId === assignment.targetId,
66 ),
67 ).toBe(true);
68
69 yield* stack.destroy();
70 }),
71 {
72 tags: ["provider:aws", "provider:aws:identitycenter", "live"],
73 timeout: 300_000,
74 },
75);
76
77// A `creating` row can persist without resolved Outputs (`targetId` from
78// `account.accountId`). Distilled `ListAccountAssignments` then fails with
79// `ParseError: Expected string at ["AccountId"]`. Guarded `read` must report
80// not-found without calling AWS.
81test.provider(
82 "read returns undefined when creating-state lost targetId",
83 () =>
84 Effect.gen(function* () {
85 const provider = yield* Provider.findProvider(AccountAssignment);
86 const result = yield* provider.read!({
87 id: "AccountAssignment",
88 fqn: "AccountAssignment",
89 instanceId: "test-instance",
90 olds: {
91 permissionSetArn:
92 "arn:aws:sso:::permissionSet/ssoins-example/ps-example",
93 principalId: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
94 principalType: "GROUP",
95 } as AccountAssignment["Props"],
96 output: undefined,
97 });
98 expect(result).toBeUndefined();
99 }),
100 { tags: ["provider:aws", "provider:aws:identitycenter", "live"] },
101);