/// <summary> /// Entry point to test a Pulumi application. Deployment will /// instantiate a new stack instance based on the type passed as TStack /// type parameter. This method creates no real resources. /// Note: Currently, unit tests that call <see cref="TestAsync{TStack}"/> /// must run serially; parallel execution is not supported. /// </summary> /// <param name="mocks">Hooks to mock the engine calls.</param> /// <param name="options">Optional settings for the test run.</param> /// <typeparam name="TStack">The type of the stack to test.</typeparam> /// <returns>Test result containing created resources and errors, if any.</returns> public static async Task <ImmutableArray <Resource> > TestAsync <TStack>(IMocks mocks, TestOptions?options = null) where TStack : Stack, new() { var engine = new MockEngine(); var monitor = new MockMonitor(mocks); Deployment deployment; lock (_instanceLock) { if (_instance != null) { throw new NotSupportedException($"Multiple executions of {nameof(TestAsync)} must run serially. Please configure your unit test suite to run tests one-by-one."); } deployment = new Deployment(engine, monitor, options); Instance = new DeploymentInstance(deployment); } try { await deployment._runner.RunAsync <TStack>(); return(engine.Errors.Count switch { 1 => throw new RunException(engine.Errors.Single()), int v when v > 1 => throw new AggregateException(engine.Errors.Select(e => new RunException(e))), _ => monitor.Resources.ToImmutableArray() }); }
// this method *must* remain marked async // in order to protect the scope of the AsyncLocal Deployment.Instance we cannot elide the task (return it early) // if the task is returned early and not awaited, than it is possible for any code that runs before the eventual await // to be executed synchronously and thus have multiple calls to one of the Run methods affecting each others Deployment.Instance internal static async Task <int> CreateRunnerAndRunAsync( Func <Deployment> deploymentFactory, Func <IRunner, Task <int> > runAsync) { var deployment = deploymentFactory(); Instance = new DeploymentInstance(deployment); return(await runAsync(deployment._runner).ConfigureAwait(false)); }