@reventlessdev/reventless-aws 3.0.0-alpha.344 → 3.0.0-alpha.346
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/CHANGELOG.md +36 -0
- package/package.json +15 -12
- package/run-provision-accounts.mjs +3 -0
- package/run-provision-admin.mjs +3 -0
- package/scripts/ProvisionAccounts.res +325 -0
- package/scripts/ProvisionAccounts.res.mjs +337 -0
- package/scripts/ProvisionAdmin.res +275 -0
- package/scripts/ProvisionAdmin.res.mjs +289 -0
- package/scripts/ProvisionCognito.res +149 -0
- package/scripts/ProvisionCognito.res.mjs +98 -0
- package/scripts/ProvisionIdentity.res +84 -14
- package/scripts/ProvisionIdentity.res.mjs +72 -7
- package/scripts/ProvisionProvider.res +104 -0
- package/scripts/ProvisionProvider.res.mjs +123 -0
- package/src/Platform.res +358 -329
- package/src/Platform.res.mjs +24 -12
- package/src/Platform_Casts.res +13 -4
- package/src/Platform_Stack.res +119 -9
- package/src/Platform_Stack.res.mjs +42 -4
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +8 -4
- package/src/adapter/Api/AppSync_EventsApi.res +12 -14
- package/src/adapter/Api/AppSync_Resolver_Native.res +3 -4
- package/src/adapter/Api/AppSync_Resolver_Retrying.res +78 -70
- package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +11 -8
- package/src/adapter/Api/Platform_AdminScan_Ops.res +15 -14
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -2
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +40 -50
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +39 -32
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +5 -2
- package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +0 -1
- package/src/adapter/Auth/Auth_ActiveRoleStore.res +1 -2
- package/src/adapter/Auth/Auth_ActiveRoleStore_Schema.res +1 -4
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +5 -2
- package/src/adapter/Auth/Auth_Cognito.res +3 -9
- package/src/adapter/Auth/Auth_LoginIdentifier.res +66 -0
- package/src/adapter/Auth/Auth_LoginIdentifier.res.mjs +70 -0
- package/src/adapter/Auth/Auth_SignUpMode.res +78 -0
- package/src/adapter/Auth/Auth_SignUpMode.res.mjs +56 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +1 -4
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +3 -1
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +1 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +7 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +6 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +7 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +24 -18
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +9 -4
- package/src/adapter/CommandTopic/CommandTopicRegistry.res +1 -4
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +15 -15
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +9 -5
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +179 -213
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +5 -5
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +6 -2
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +14 -7
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +14 -7
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +6 -3
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +6 -1
- package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +13 -17
- package/src/adapter/EventLog/EventLogStorage_Postgres.res +7 -4
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +12 -7
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +4 -2
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +3 -2
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +20 -12
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +21 -9
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -3
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +6 -3
- package/src/adapter/Mcp/MCP_Lambda.res +31 -46
- package/src/adapter/Messaging/Messaging_Ses_Backend.res +11 -7
- package/src/adapter/Messaging/Messaging_Ses_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring_CloudWatch.res +16 -16
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +5 -10
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +4 -2
- package/src/adapter/Postgres/PgConnection.res +6 -5
- package/src/adapter/Postgres/PgMigration_Builder.res +4 -8
- package/src/adapter/Postgres/PgRuntime.res +4 -7
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res +9 -11
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +22 -21
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +114 -111
- package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +1 -7
- package/src/adapter/QueryDb/QueryDbStorage.res +4 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +23 -24
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +8 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +71 -73
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -2
- package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +1 -4
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +8 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +73 -81
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +72 -78
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +44 -67
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +44 -67
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +53 -82
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +47 -77
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +23 -11
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +5 -1
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -97
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +2 -7
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -10
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +24 -15
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +17 -37
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +31 -57
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +18 -24
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +1 -5
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +4 -3
- package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +4 -2
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +0 -1
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +3 -2
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +1 -2
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +4 -5
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +22 -10
- package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +8 -2
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +14 -31
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +16 -17
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +10 -9
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +46 -52
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +36 -41
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +31 -37
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +6 -7
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +4 -6
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +2 -6
- package/src/adapter/StateTopic/StateTopic_AppSync.res +21 -16
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +36 -32
- package/src/adapter/Task/TaskBucket_S3.res +6 -1
- package/src/adapter/Upload/Upload_Claim_S3.res +17 -20
- package/src/adapter/Upload/Upload_Claim_S3_Ops.res +5 -2
- package/src/adapter/Upload/Upload_Presign_S3.res +51 -49
- package/src/capability/Capability_Geocoding_AwsLocation.res +4 -1
- package/src/capability/Capability_Messaging.res +4 -5
- package/src/capability/Capability_Messaging_Ses.res +3 -1
- package/src/capability/Capability_ObjectStore_S3.res +1 -7
- package/src/components/Api/AppSync_Adapter.res +80 -109
- package/src/components/Api/AppSync_Adapter.res.mjs +26 -60
- package/src/components/Api/AppSync_SdlDecorate.res +118 -31
- package/src/components/Api/AppSync_SdlDecorate.res.mjs +45 -0
- package/src/components/AutomationSlice_Builder.res +6 -4
- package/src/components/ExtensionPoint_Builder.res +9 -10
- package/src/components/InboundTranslationSlice_Builder.res +7 -8
- package/src/components/OutboundTranslationSlice_Builder.res +14 -8
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/src/components/SideEffectHandler_Single.res +4 -4
- package/src/components/StateViewSlice_Builder.res +6 -4
- package/src/components/StateViewSlice_Builder_Stream.res +6 -4
- package/src/components/Task_Builder_PerBucket.res +10 -11
- package/src/plugin/cloner/ClonerRunner_Fargate.res +3 -10
- package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +1 -4
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +5 -5
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +19 -21
- package/src/plugin/runtime/PluginRuntime_Builder.res +167 -188
- package/src/plugin/runtime/Util_PluginMessage_Runtime.res +6 -5
- package/src/plugin/stack/Plugin_Stack.res +104 -103
- package/src/util/Util_AlarmSpec.res +2 -1
- package/src/util/Util_AppSync.res +8 -2
- package/src/util/Util_AppSync_Caller.res +15 -14
- package/src/util/Util_Bundle.res +46 -42
- package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
- package/src/util/Util_Cognito_Runtime.res +11 -7
- package/src/util/Util_DeadLetterQueue.res +12 -2
- package/src/util/Util_DynamoDb.res +5 -3
- package/src/util/Util_DynamoDbStream.res +7 -3
- package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
- package/src/util/Util_DynamoDb_Runtime.res +23 -41
- package/src/util/Util_HostUiDomain.res +6 -1
- package/src/util/Util_Kinesis_Runtime.res +10 -11
- package/src/util/Util_Lambda.res +12 -5
- package/src/util/Util_LambdaEnvBudget.res +5 -5
- package/src/util/Util_LambdaLogging.res +1 -2
- package/src/util/Util_LogGroup_Adopting.res +1 -4
- package/src/util/Util_ResourceNaming.res +1 -2
- package/src/util/Util_SNS.res +8 -3
- package/src/util/Util_SNS_FIFO.res +1 -1
- package/src/util/Util_SNS_Runtime.res +11 -10
- package/src/util/Util_SQS.res +4 -2
- package/src/util/Util_SQS_FIFO.res +5 -2
- package/src/util/Util_SQS_Runtime.res +37 -36
- package/src/util/Util_ShellConfig.res +3 -2
- package/src/util/Util_StaticBundle.res +1 -3
- package/src/util/Util_StoreLayout.res +4 -1
- package/tests/AWS_TagsTest.res +16 -5
- package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
- package/tests/AppSync_AdapterTest.res +286 -224
- package/tests/AppSync_AdapterTest.res.mjs +69 -0
- package/tests/AppSync_Resolver_RetryingTest.res +64 -44
- package/tests/AppSync_RetirementNarrowingTest.res +59 -46
- package/tests/AppSync_SdlDecorateTest.res +22 -20
- package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
- package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
- package/tests/Auth_ActiveRoleStoreTest.res +12 -9
- package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
- package/tests/Auth_CognitoTest.res +31 -46
- package/tests/Auth_LoginIdentifierTest.res +51 -0
- package/tests/Auth_LoginIdentifierTest.res.mjs +51 -0
- package/tests/Auth_SignUpModeTest.res +56 -0
- package/tests/Auth_SignUpModeTest.res.mjs +53 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
- package/tests/BakeConvergenceTest.res +22 -25
- package/tests/Capability_MessagingTest.res +28 -12
- package/tests/CommandTopicQueuePolicyTest.res +1 -2
- package/tests/CounterEntryPoint_OpsTest.res +3 -7
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
- package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
- package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
- package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
- package/tests/EventCollectorReadyGateTest.res +5 -4
- package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
- package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
- package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
- package/tests/HandlerFactoryDispatchTest.res +97 -70
- package/tests/MCP_LambdaTest.res +16 -27
- package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
- package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
- package/tests/PgPipeline_IntegrationTest.res +80 -66
- package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
- package/tests/PgQueryResolver_LambdaTest.res +41 -24
- package/tests/Platform_AdminScan_OpsTest.res +4 -1
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
- package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
- package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
- package/tests/ProvisionAccountsTest.res +142 -0
- package/tests/ProvisionAccountsTest.res.mjs +153 -0
- package/tests/ProvisionAdminTest.res +55 -0
- package/tests/ProvisionAdminTest.res.mjs +66 -0
- package/tests/ProvisionIdentityTest.res +91 -9
- package/tests/ProvisionIdentityTest.res.mjs +66 -1
- package/tests/QueryDbOwnerIndexTableTest.res +6 -4
- package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
- package/tests/QueryInterceptor_LambdaTest.res +11 -13
- package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
- package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
- package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
- package/tests/StateChangeDescriptorParityTest.res +28 -26
- package/tests/StateTopicKeySchemaTest.res +18 -15
- package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
- package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
- package/tests/Upload_ClaimTest.res +3 -3
- package/tests/Util_AwsErrorTest.res +4 -7
- package/tests/Util_BundleDependencyClosureTest.res +15 -3
- package/tests/Util_BundleEsmLoaderTest.res +3 -3
- package/tests/Util_BundleResolveTest.res +4 -6
- package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
- package/tests/Util_DcbMetricsTest.res +6 -6
- package/tests/Util_DynamoDbStreamTest.res +4 -5
- package/tests/Util_DynamoDbTest.res +3 -2
- package/tests/Util_LambdaEnvBudgetTest.res +22 -18
- package/tests/Util_LambdaLoggingTest.res +9 -8
- package/tests/Util_LogGroup_AdoptingTest.res +10 -5
- package/tests/Util_LogRetentionTest.res +6 -2
- package/tests/Util_OwnerScopeEnvTest.res +35 -8
- package/tests/Util_OwnerScopeEnvTest.res.mjs +13 -0
- package/tests/Util_SQS_RuntimeTest.res +5 -7
- package/tests/Util_ShellConfigTest.res +7 -17
- package/tests/Util_StaticBundleTest.res +3 -8
- package/tests/Util_StoreLayoutTest.res +17 -12
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
- package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
- package/tests/integration/DcbIntegrationHarness.res +8 -8
- package/tests/integration/EpCompositeSlice.res +16 -8
- package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
- package/tests/integration/SvsTestSlice_Projection.res +1 -3
- package/tests/util/Util_AlarmSpecTest.res +62 -49
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// What can be decided before an AWS call is made: which pool and which manifest
|
|
4
|
+
// the run is about, and which usernames a pool could never authenticate.
|
|
5
|
+
//
|
|
6
|
+
// The applying itself is not covered here — it is four SDK calls in a loop. What
|
|
7
|
+
// is covered is the preflight, because its whole purpose is to fail *before*
|
|
8
|
+
// half a cast exists, and the argument parsing, because getting it wrong
|
|
9
|
+
// provisions the right accounts into the wrong deployment.
|
|
10
|
+
//
|
|
11
|
+
// The manifest's own rules — what a second run does to a password already in the
|
|
12
|
+
// file — live with the format, in `spec`'s `AccountsManifestTest`.
|
|
13
|
+
|
|
14
|
+
module Provision = ProvisionAccounts
|
|
15
|
+
|
|
16
|
+
describe("ProvisionAccounts.parseArgs", () => {
|
|
17
|
+
testSync("no arguments leaves every choice unanswered", () =>
|
|
18
|
+
expect(Provision.parseArgs([]))->toEqual(
|
|
19
|
+
Ok({Provision.providerId: None, stack: None, file: None, help: false}),
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
testSync("a pool and a manifest parse", () =>
|
|
24
|
+
expect(
|
|
25
|
+
Provision.parseArgs(["--provider-id", "eu-west-1_AbCdEfGhI", "--file", "cast.yaml"]),
|
|
26
|
+
)->toEqual(
|
|
27
|
+
Ok({
|
|
28
|
+
Provision.providerId: Some("eu-west-1_AbCdEfGhI"),
|
|
29
|
+
stack: None,
|
|
30
|
+
file: Some("cast.yaml"),
|
|
31
|
+
help: false,
|
|
32
|
+
}),
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
testSync("--stack picks the deployment to read the pool id from", () =>
|
|
37
|
+
expect(Provision.parseArgs(["--stack", "beta"]))->toEqual(
|
|
38
|
+
Ok({Provision.providerId: None, stack: Some("beta"), file: None, help: false}),
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
// The reason unknown flags refuse rather than being skipped, and it lands
|
|
43
|
+
// harder here than on the first administrator: a typo'd `--provider-id` falls
|
|
44
|
+
// through to the environment and creates the whole cast in a different pool
|
|
45
|
+
// than the operator named.
|
|
46
|
+
testSync("an unknown flag refuses rather than being ignored", () =>
|
|
47
|
+
expect(Provision.parseArgs(["--pool", "eu-west-1_AbCdEfGhI"]))->toEqual(
|
|
48
|
+
Error(`unknown argument "--pool"`),
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
testSync("a flag with no value refuses", () =>
|
|
53
|
+
expect(Provision.parseArgs(["--provider-id"]))->toEqual(Error("--provider-id needs a value"))
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
testSync("-h asks for the usage", () =>
|
|
57
|
+
expect(Provision.parseArgs(["-h"]))->toEqual(
|
|
58
|
+
Ok({Provision.providerId: None, stack: None, file: None, help: true}),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Which pool a run writes to, which is the one decision that is expensive to get
|
|
64
|
+
// wrong: provisioning a cast into the wrong deployment succeeds, and leaves
|
|
65
|
+
// working accounts somewhere nobody is looking.
|
|
66
|
+
//
|
|
67
|
+
// The stack fallback is not exercised here — it shells out to `pulumi`, and a
|
|
68
|
+
// test that did would depend on a logged-in CLI and a deployed stack. What is
|
|
69
|
+
// covered is the precedence above it, and that every answer says where it came
|
|
70
|
+
// from, which is what makes a wrong-pool run visible rather than silent.
|
|
71
|
+
describe("ProvisionProvider.resolve", () => {
|
|
72
|
+
let withoutEnv = fn => {
|
|
73
|
+
let saved = NodeProcess.env->Dict.get(ProvisionProvider.envKey)
|
|
74
|
+
NodeProcess.env->Dict.delete(ProvisionProvider.envKey)
|
|
75
|
+
let outcome = fn()
|
|
76
|
+
switch saved {
|
|
77
|
+
| Some(value) => NodeProcess.env->Dict.set(ProvisionProvider.envKey, value)
|
|
78
|
+
| None => ()
|
|
79
|
+
}
|
|
80
|
+
outcome
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
testSync("an explicit id wins, and says so", () =>
|
|
84
|
+
expect(
|
|
85
|
+
withoutEnv(
|
|
86
|
+
() => ProvisionProvider.resolve(~given=Some("eu-west-1_Explicit"), ~stack=Some("alpha")),
|
|
87
|
+
),
|
|
88
|
+
)->toEqual(Ok(("eu-west-1_Explicit", ProvisionProvider.Flag)))
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// The flag beats the variable rather than the other way round: a CI shell that
|
|
92
|
+
// exports one for every stack must still be overridable for a single run.
|
|
93
|
+
testSync("an explicit id beats the environment", () => {
|
|
94
|
+
NodeProcess.env->Dict.set(ProvisionProvider.envKey, "eu-west-1_FromEnv")
|
|
95
|
+
let resolved = ProvisionProvider.resolve(~given=Some("eu-west-1_Explicit"), ~stack=None)
|
|
96
|
+
NodeProcess.env->Dict.delete(ProvisionProvider.envKey)
|
|
97
|
+
expect(resolved)->toEqual(Ok(("eu-west-1_Explicit", ProvisionProvider.Flag)))
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
testSync("the environment answers when no flag does", () => {
|
|
101
|
+
NodeProcess.env->Dict.set(ProvisionProvider.envKey, "eu-west-1_FromEnv")
|
|
102
|
+
let resolved = ProvisionProvider.resolve(~given=None, ~stack=None)
|
|
103
|
+
NodeProcess.env->Dict.delete(ProvisionProvider.envKey)
|
|
104
|
+
expect(resolved)->toEqual(Ok(("eu-west-1_FromEnv", ProvisionProvider.Environment)))
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// An empty variable is not an answer. Exporting it unset is the ordinary shape
|
|
108
|
+
// of a CI script whose earlier step did not run, and reading "" as the pool id
|
|
109
|
+
// would fail much later with a message about Cognito rather than about setup.
|
|
110
|
+
testSync("an empty environment variable is not an answer", () =>
|
|
111
|
+
expect({
|
|
112
|
+
NodeProcess.env->Dict.set(ProvisionProvider.envKey, " ")
|
|
113
|
+
let resolved = ProvisionProvider.resolve(~given=None, ~stack=Some("no-such-stack-here"))
|
|
114
|
+
NodeProcess.env->Dict.delete(ProvisionProvider.envKey)
|
|
115
|
+
resolved->Result.isError
|
|
116
|
+
})->toBe(true)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
testSync("each source describes itself", () => {
|
|
120
|
+
expect(ProvisionProvider.describe(Flag))->toBe("--provider-id")
|
|
121
|
+
expect(ProvisionProvider.describe(Environment))->toBe("REVENTLESS_IDENTITY_PROVIDER_ID")
|
|
122
|
+
expect(ProvisionProvider.describe(Stack("beta")))->toBe("stack beta")
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// A manifest usually names plain usernames — `shopper`, `merch` — and a pool
|
|
127
|
+
// created with `UsernameAttributes: [email]` will not take them. The accounts it
|
|
128
|
+
// makes instead are correctly created and cannot authenticate, which is a symptom
|
|
129
|
+
// that arrives at a login screen far from the run that caused it.
|
|
130
|
+
describe("ProvisionAccounts.looksLikeEmail", () => {
|
|
131
|
+
testSync("an address is one", () =>
|
|
132
|
+
expect(Provision.looksLikeEmail("me@example.com"))->toBe(true)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
testSync("a demo username is not", () => expect(Provision.looksLikeEmail("shopper"))->toBe(false))
|
|
136
|
+
|
|
137
|
+
// Not a validator — Cognito owns that. It answers one question: would this pool
|
|
138
|
+
// accept this string as a sign-in name. A local part with no domain would not.
|
|
139
|
+
testSync("an @ with no domain dot is not", () =>
|
|
140
|
+
expect(Provision.looksLikeEmail("admin@localhost"))->toBe(false)
|
|
141
|
+
)
|
|
142
|
+
})
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
4
|
+
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
5
|
+
import * as ProvisionAccounts$ReventlessAws from "../scripts/ProvisionAccounts.res.mjs";
|
|
6
|
+
import * as ProvisionProvider$ReventlessAws from "../scripts/ProvisionProvider.res.mjs";
|
|
7
|
+
|
|
8
|
+
globalThis.describe("ProvisionAccounts.parseArgs", () => {
|
|
9
|
+
globalThis.test("no arguments leaves every choice unanswered", () => {
|
|
10
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.parseArgs([])).toEqual({
|
|
11
|
+
TAG: "Ok",
|
|
12
|
+
_0: {
|
|
13
|
+
providerId: undefined,
|
|
14
|
+
stack: undefined,
|
|
15
|
+
file: undefined,
|
|
16
|
+
help: false
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
globalThis.test("a pool and a manifest parse", () => {
|
|
21
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.parseArgs([
|
|
22
|
+
"--provider-id",
|
|
23
|
+
"eu-west-1_AbCdEfGhI",
|
|
24
|
+
"--file",
|
|
25
|
+
"cast.yaml"
|
|
26
|
+
])).toEqual({
|
|
27
|
+
TAG: "Ok",
|
|
28
|
+
_0: {
|
|
29
|
+
providerId: "eu-west-1_AbCdEfGhI",
|
|
30
|
+
stack: undefined,
|
|
31
|
+
file: "cast.yaml",
|
|
32
|
+
help: false
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
globalThis.test("--stack picks the deployment to read the pool id from", () => {
|
|
37
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.parseArgs([
|
|
38
|
+
"--stack",
|
|
39
|
+
"beta"
|
|
40
|
+
])).toEqual({
|
|
41
|
+
TAG: "Ok",
|
|
42
|
+
_0: {
|
|
43
|
+
providerId: undefined,
|
|
44
|
+
stack: "beta",
|
|
45
|
+
file: undefined,
|
|
46
|
+
help: false
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
globalThis.test("an unknown flag refuses rather than being ignored", () => {
|
|
51
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.parseArgs([
|
|
52
|
+
"--pool",
|
|
53
|
+
"eu-west-1_AbCdEfGhI"
|
|
54
|
+
])).toEqual({
|
|
55
|
+
TAG: "Error",
|
|
56
|
+
_0: `unknown argument "--pool"`
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
globalThis.test("a flag with no value refuses", () => {
|
|
60
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.parseArgs(["--provider-id"])).toEqual({
|
|
61
|
+
TAG: "Error",
|
|
62
|
+
_0: "--provider-id needs a value"
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
globalThis.test("-h asks for the usage", () => {
|
|
66
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.parseArgs(["-h"])).toEqual({
|
|
67
|
+
TAG: "Ok",
|
|
68
|
+
_0: {
|
|
69
|
+
providerId: undefined,
|
|
70
|
+
stack: undefined,
|
|
71
|
+
file: undefined,
|
|
72
|
+
help: true
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
globalThis.describe("ProvisionProvider.resolve", () => {
|
|
79
|
+
let withoutEnv = fn => {
|
|
80
|
+
let saved = process.env[ProvisionProvider$ReventlessAws.envKey];
|
|
81
|
+
Stdlib_Dict.$$delete(process.env, ProvisionProvider$ReventlessAws.envKey);
|
|
82
|
+
let outcome = fn();
|
|
83
|
+
if (saved !== undefined) {
|
|
84
|
+
process.env[ProvisionProvider$ReventlessAws.envKey] = saved;
|
|
85
|
+
}
|
|
86
|
+
return outcome;
|
|
87
|
+
};
|
|
88
|
+
globalThis.test("an explicit id wins, and says so", () => {
|
|
89
|
+
globalThis.expect(withoutEnv(() => ProvisionProvider$ReventlessAws.resolve("eu-west-1_Explicit", "alpha"))).toEqual({
|
|
90
|
+
TAG: "Ok",
|
|
91
|
+
_0: [
|
|
92
|
+
"eu-west-1_Explicit",
|
|
93
|
+
"Flag"
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
globalThis.test("an explicit id beats the environment", () => {
|
|
98
|
+
process.env[ProvisionProvider$ReventlessAws.envKey] = "eu-west-1_FromEnv";
|
|
99
|
+
let resolved = ProvisionProvider$ReventlessAws.resolve("eu-west-1_Explicit", undefined);
|
|
100
|
+
Stdlib_Dict.$$delete(process.env, ProvisionProvider$ReventlessAws.envKey);
|
|
101
|
+
globalThis.expect(resolved).toEqual({
|
|
102
|
+
TAG: "Ok",
|
|
103
|
+
_0: [
|
|
104
|
+
"eu-west-1_Explicit",
|
|
105
|
+
"Flag"
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
globalThis.test("the environment answers when no flag does", () => {
|
|
110
|
+
process.env[ProvisionProvider$ReventlessAws.envKey] = "eu-west-1_FromEnv";
|
|
111
|
+
let resolved = ProvisionProvider$ReventlessAws.resolve(undefined, undefined);
|
|
112
|
+
Stdlib_Dict.$$delete(process.env, ProvisionProvider$ReventlessAws.envKey);
|
|
113
|
+
globalThis.expect(resolved).toEqual({
|
|
114
|
+
TAG: "Ok",
|
|
115
|
+
_0: [
|
|
116
|
+
"eu-west-1_FromEnv",
|
|
117
|
+
"Environment"
|
|
118
|
+
]
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
globalThis.test("an empty environment variable is not an answer", () => {
|
|
122
|
+
process.env[ProvisionProvider$ReventlessAws.envKey] = " ";
|
|
123
|
+
let resolved = ProvisionProvider$ReventlessAws.resolve(undefined, "no-such-stack-here");
|
|
124
|
+
globalThis.expect((Stdlib_Dict.$$delete(process.env, ProvisionProvider$ReventlessAws.envKey), Stdlib_Result.isError(resolved))).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
globalThis.test("each source describes itself", () => {
|
|
127
|
+
globalThis.expect(ProvisionProvider$ReventlessAws.describe("Flag")).toBe("--provider-id");
|
|
128
|
+
globalThis.expect(ProvisionProvider$ReventlessAws.describe("Environment")).toBe("REVENTLESS_IDENTITY_PROVIDER_ID");
|
|
129
|
+
globalThis.expect(ProvisionProvider$ReventlessAws.describe({
|
|
130
|
+
TAG: "Stack",
|
|
131
|
+
_0: "beta"
|
|
132
|
+
})).toBe("stack beta");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
globalThis.describe("ProvisionAccounts.looksLikeEmail", () => {
|
|
137
|
+
globalThis.test("an address is one", () => {
|
|
138
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.looksLikeEmail("me@example.com")).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
globalThis.test("a demo username is not", () => {
|
|
141
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.looksLikeEmail("shopper")).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
globalThis.test("an @ with no domain dot is not", () => {
|
|
144
|
+
globalThis.expect(ProvisionAccounts$ReventlessAws.looksLikeEmail("admin@localhost")).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
let Provision;
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
Provision,
|
|
152
|
+
}
|
|
153
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// The bootstrap script's decidable part: which pool and which person the run is
|
|
4
|
+
// about. Reachable at all only because the module defines `main` without calling
|
|
5
|
+
// it — `run-provision-admin.mjs` does that — which is the same reason its sibling
|
|
6
|
+
// is testable.
|
|
7
|
+
//
|
|
8
|
+
// The AWS calls themselves are not covered here; what is covered is everything
|
|
9
|
+
// that can be wrong *before* one is made. The credential this script hands out is
|
|
10
|
+
// no longer its own — the generator moved to `spec` so the manifest's
|
|
11
|
+
// platform-neutral half could reach it, and `Util_PasswordTest` holds the policy
|
|
12
|
+
// it has to satisfy.
|
|
13
|
+
|
|
14
|
+
module Provision = ProvisionAdmin
|
|
15
|
+
|
|
16
|
+
describe("ProvisionAdmin.parseArgs", () => {
|
|
17
|
+
testSync("both arguments parse", () =>
|
|
18
|
+
expect(
|
|
19
|
+
Provision.parseArgs(["--provider-id", "eu-west-1_AbCdEfGhI", "--email", "me@example.com"]),
|
|
20
|
+
)->toEqual(
|
|
21
|
+
Ok({
|
|
22
|
+
Provision.providerId: Some("eu-west-1_AbCdEfGhI"),
|
|
23
|
+
stack: None,
|
|
24
|
+
email: Some("me@example.com"),
|
|
25
|
+
help: false,
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
testSync("no arguments leaves both unanswered", () =>
|
|
31
|
+
expect(Provision.parseArgs([]))->toEqual(
|
|
32
|
+
Ok({Provision.providerId: None, stack: None, email: None, help: false}),
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// The reason unknown flags refuse rather than being skipped: a typo'd
|
|
37
|
+
// `--provider-id` would fall through to the environment and make the
|
|
38
|
+
// administrator in a different pool than the operator named — a successful run
|
|
39
|
+
// against the wrong deployment, which is worse than a failed one.
|
|
40
|
+
testSync("an unknown flag refuses rather than being ignored", () =>
|
|
41
|
+
expect(Provision.parseArgs(["--provider", "eu-west-1_AbCdEfGhI"]))->toEqual(
|
|
42
|
+
Error(`unknown argument "--provider"`),
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
testSync("a flag with no value refuses", () =>
|
|
47
|
+
expect(Provision.parseArgs(["--email"]))->toEqual(Error("--email needs a value"))
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
testSync("-h asks for the usage", () =>
|
|
51
|
+
expect(Provision.parseArgs(["-h"]))->toEqual(
|
|
52
|
+
Ok({Provision.providerId: None, stack: None, email: None, help: true}),
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as ProvisionAdmin$ReventlessAws from "../scripts/ProvisionAdmin.res.mjs";
|
|
4
|
+
|
|
5
|
+
globalThis.describe("ProvisionAdmin.parseArgs", () => {
|
|
6
|
+
globalThis.test("both arguments parse", () => {
|
|
7
|
+
globalThis.expect(ProvisionAdmin$ReventlessAws.parseArgs([
|
|
8
|
+
"--provider-id",
|
|
9
|
+
"eu-west-1_AbCdEfGhI",
|
|
10
|
+
"--email",
|
|
11
|
+
"me@example.com"
|
|
12
|
+
])).toEqual({
|
|
13
|
+
TAG: "Ok",
|
|
14
|
+
_0: {
|
|
15
|
+
providerId: "eu-west-1_AbCdEfGhI",
|
|
16
|
+
stack: undefined,
|
|
17
|
+
email: "me@example.com",
|
|
18
|
+
help: false
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
globalThis.test("no arguments leaves both unanswered", () => {
|
|
23
|
+
globalThis.expect(ProvisionAdmin$ReventlessAws.parseArgs([])).toEqual({
|
|
24
|
+
TAG: "Ok",
|
|
25
|
+
_0: {
|
|
26
|
+
providerId: undefined,
|
|
27
|
+
stack: undefined,
|
|
28
|
+
email: undefined,
|
|
29
|
+
help: false
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
globalThis.test("an unknown flag refuses rather than being ignored", () => {
|
|
34
|
+
globalThis.expect(ProvisionAdmin$ReventlessAws.parseArgs([
|
|
35
|
+
"--provider",
|
|
36
|
+
"eu-west-1_AbCdEfGhI"
|
|
37
|
+
])).toEqual({
|
|
38
|
+
TAG: "Error",
|
|
39
|
+
_0: `unknown argument "--provider"`
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
globalThis.test("a flag with no value refuses", () => {
|
|
43
|
+
globalThis.expect(ProvisionAdmin$ReventlessAws.parseArgs(["--email"])).toEqual({
|
|
44
|
+
TAG: "Error",
|
|
45
|
+
_0: "--email needs a value"
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
globalThis.test("-h asks for the usage", () => {
|
|
49
|
+
globalThis.expect(ProvisionAdmin$ReventlessAws.parseArgs(["-h"])).toEqual({
|
|
50
|
+
TAG: "Ok",
|
|
51
|
+
_0: {
|
|
52
|
+
providerId: undefined,
|
|
53
|
+
stack: undefined,
|
|
54
|
+
email: undefined,
|
|
55
|
+
help: true
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
let Provision;
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
Provision,
|
|
65
|
+
}
|
|
66
|
+
/* Not a pure module */
|
|
@@ -16,6 +16,8 @@ describe("ProvisionIdentity.parseArgs", () => {
|
|
|
16
16
|
Ok({
|
|
17
17
|
Provision.poolName: Provision.defaultPoolName,
|
|
18
18
|
providerId: None,
|
|
19
|
+
loginIdentifier: Auth_LoginIdentifier.Email,
|
|
20
|
+
signUpMode: Auth_SignUpMode.AdminOnly,
|
|
19
21
|
help: false,
|
|
20
22
|
}),
|
|
21
23
|
)
|
|
@@ -26,6 +28,8 @@ describe("ProvisionIdentity.parseArgs", () => {
|
|
|
26
28
|
Ok({
|
|
27
29
|
Provision.poolName: Provision.defaultPoolName,
|
|
28
30
|
providerId: Some("eu-west-1_AbCdEfGhI"),
|
|
31
|
+
loginIdentifier: Auth_LoginIdentifier.Email,
|
|
32
|
+
signUpMode: Auth_SignUpMode.AdminOnly,
|
|
29
33
|
help: false,
|
|
30
34
|
}),
|
|
31
35
|
)
|
|
@@ -33,13 +37,60 @@ describe("ProvisionIdentity.parseArgs", () => {
|
|
|
33
37
|
|
|
34
38
|
testSync("--name selects which pool to adopt by name", () =>
|
|
35
39
|
expect(Provision.parseArgs(["--name", "examples-dev"]))->toEqual(
|
|
36
|
-
Ok({
|
|
40
|
+
Ok({
|
|
41
|
+
Provision.poolName: "examples-dev",
|
|
42
|
+
providerId: None,
|
|
43
|
+
loginIdentifier: Auth_LoginIdentifier.Email,
|
|
44
|
+
signUpMode: Auth_SignUpMode.AdminOnly,
|
|
45
|
+
help: false,
|
|
46
|
+
}),
|
|
37
47
|
)
|
|
38
48
|
)
|
|
39
49
|
|
|
40
50
|
testSync("both together are accepted, and the id wins at resolve time", () =>
|
|
41
51
|
expect(Provision.parseArgs(["--name", "ignored", "--provider-id", "eu-west-1_x"]))->toEqual(
|
|
42
|
-
Ok({
|
|
52
|
+
Ok({
|
|
53
|
+
Provision.poolName: "ignored",
|
|
54
|
+
providerId: Some("eu-west-1_x"),
|
|
55
|
+
loginIdentifier: Auth_LoginIdentifier.Email,
|
|
56
|
+
signUpMode: Auth_SignUpMode.AdminOnly,
|
|
57
|
+
help: false,
|
|
58
|
+
}),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
testSync("--login-identifier picks the sign-in attribute", () =>
|
|
63
|
+
expect(
|
|
64
|
+
Provision.parseArgs(["--login-identifier", "emailOrPhone"])->Result.map(
|
|
65
|
+
a => a.loginIdentifier,
|
|
66
|
+
),
|
|
67
|
+
)->toEqual(Ok(Auth_LoginIdentifier.EmailOrPhone))
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
// 🚨 The sign-in attribute cannot be changed once the pool exists, so a typo
|
|
71
|
+
// that fell back to the default would create a pool the operator has to
|
|
72
|
+
// rebuild — and re-register every account in — to correct.
|
|
73
|
+
testSync("an unrecognised sign-in attribute is refused, never defaulted", () =>
|
|
74
|
+
expect(Provision.parseArgs(["--login-identifier", "e-mail"])->Result.isError)->toBe(true)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
testSync("--sign-up-mode opens the pool to self-registration", () =>
|
|
78
|
+
expect(
|
|
79
|
+
Provision.parseArgs(["--sign-up-mode", "selfService"])->Result.map(a => a.signUpMode),
|
|
80
|
+
)->toEqual(Ok(Auth_SignUpMode.SelfService))
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
// This one IS correctable later, unlike the sign-in attribute — it is refused
|
|
84
|
+
// because the mistake is silent, not because it is permanent. Defaulting past
|
|
85
|
+
// a typo leaves a pool that refuses every registration while the command line
|
|
86
|
+
// reads as though it allows them.
|
|
87
|
+
testSync("an unrecognised sign-up mode is refused, never defaulted", () =>
|
|
88
|
+
expect(Provision.parseArgs(["--sign-up-mode", "self-service"])->Result.isError)->toBe(true)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
testSync("sign-up mode defaults to admin-only, so an existing pool is unchanged", () =>
|
|
92
|
+
expect(Provision.parseArgs([])->Result.map(a => a.signUpMode))->toEqual(
|
|
93
|
+
Ok(Auth_SignUpMode.AdminOnly),
|
|
43
94
|
)
|
|
44
95
|
)
|
|
45
96
|
|
|
@@ -55,15 +106,19 @@ describe("ProvisionIdentity.parseArgs", () => {
|
|
|
55
106
|
// Same hazard from the other direction: a flag whose value was left off must
|
|
56
107
|
// not read as "no id given".
|
|
57
108
|
testSync("a flag with no value is refused", () =>
|
|
58
|
-
expect(Provision.parseArgs(["--provider-id"]))->toEqual(
|
|
59
|
-
Error("--provider-id needs a value"),
|
|
60
|
-
)
|
|
109
|
+
expect(Provision.parseArgs(["--provider-id"]))->toEqual(Error("--provider-id needs a value"))
|
|
61
110
|
)
|
|
62
111
|
|
|
63
112
|
testSync("--name with no value is refused too", () =>
|
|
64
113
|
expect(Provision.parseArgs(["--name"]))->toEqual(Error("--name needs a value"))
|
|
65
114
|
)
|
|
66
115
|
|
|
116
|
+
testSync("--login-identifier with no value is refused too", () =>
|
|
117
|
+
expect(Provision.parseArgs(["--login-identifier"]))->toEqual(
|
|
118
|
+
Error("--login-identifier needs a value"),
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
67
122
|
testSync("--help and -h both ask for the usage text", () =>
|
|
68
123
|
expect((
|
|
69
124
|
Provision.parseArgs(["--help"])->Result.map(a => a.help),
|
|
@@ -85,12 +140,14 @@ describe("ProvisionIdentity.parseArgs", () => {
|
|
|
85
140
|
// touches it — so its settings are not a detail an operator can revise cheaply
|
|
86
141
|
// once accounts exist in it.
|
|
87
142
|
describe("ProvisionIdentity.poolSettings", () => {
|
|
88
|
-
let settings = Provision.poolSettings(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
143
|
+
let settings = Provision.poolSettings(
|
|
144
|
+
~poolName="MyIdentity",
|
|
145
|
+
~loginIdentifier=Auth_LoginIdentifier.default,
|
|
146
|
+
~signUpMode=Auth_SignUpMode.default,
|
|
92
147
|
)
|
|
93
148
|
|
|
149
|
+
testSync("carries the name it was asked for", () => expect(settings.poolName)->toBe("MyIdentity"))
|
|
150
|
+
|
|
94
151
|
testSync("matches what auto mode declares: email sign-in, no MFA, admin-only", () =>
|
|
95
152
|
expect((
|
|
96
153
|
settings.usernameAttributes,
|
|
@@ -99,6 +156,31 @@ describe("ProvisionIdentity.poolSettings", () => {
|
|
|
99
156
|
))->toEqual((Some(["email"]), Some("OFF"), Some(true)))
|
|
100
157
|
)
|
|
101
158
|
|
|
159
|
+
testSync("a phone pool is created signing in on phone_number", () =>
|
|
160
|
+
expect(
|
|
161
|
+
Provision.poolSettings(
|
|
162
|
+
~poolName="MyIdentity",
|
|
163
|
+
~loginIdentifier=Auth_LoginIdentifier.Phone,
|
|
164
|
+
~signUpMode=Auth_SignUpMode.default,
|
|
165
|
+
).usernameAttributes,
|
|
166
|
+
)->toEqual(Some(["phone_number"]))
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
// The negation is the part worth pinning: this type asks who may sign up, and
|
|
170
|
+
// Cognito's field asks the opposite question. Reading it backwards would open a
|
|
171
|
+
// pool that was meant to stay closed.
|
|
172
|
+
testSync("selfService clears allowAdminCreateUserOnly, adminOnly sets it", () => {
|
|
173
|
+
let openPool = Provision.poolSettings(
|
|
174
|
+
~poolName="MyIdentity",
|
|
175
|
+
~loginIdentifier=Auth_LoginIdentifier.default,
|
|
176
|
+
~signUpMode=Auth_SignUpMode.SelfService,
|
|
177
|
+
)
|
|
178
|
+
expect((
|
|
179
|
+
openPool.adminCreateUserConfig->Option.flatMap(c => c.allowAdminCreateUserOnly),
|
|
180
|
+
settings.adminCreateUserConfig->Option.flatMap(c => c.allowAdminCreateUserOnly),
|
|
181
|
+
))->toEqual((Some(false), Some(true)))
|
|
182
|
+
})
|
|
183
|
+
|
|
102
184
|
testSync("keeps the 12-character password policy", () =>
|
|
103
185
|
expect(
|
|
104
186
|
settings.policies
|