mirror of https://github.com/pulumi/pulumi.git
230 lines
5.5 KiB
Go
230 lines
5.5 KiB
Go
// Copyright 2016-2024, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package promise
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestZeroPanic(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var p Promise[int]
|
|
assert.PanicsWithValue(t, "Promise must be initialized", func() {
|
|
p.Result(context.Background()) //nolint:errcheck // Result is expected to panic
|
|
})
|
|
|
|
assert.PanicsWithValue(t, "Promise must be initialized", func() {
|
|
p.TryResult() //nolint:errcheck // TryResult is expected to panic
|
|
})
|
|
}
|
|
|
|
func TestFulfill(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
set := ps.Fulfill(42)
|
|
assert.True(t, set, "set should be true")
|
|
promise := ps.Promise()
|
|
i, err := promise.Result(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 42, i)
|
|
// Trying to fulfill again should fail
|
|
set = ps.Fulfill(43)
|
|
assert.False(t, set, "set should be false")
|
|
// Asking for the promise again should give the same promise
|
|
assert.Equal(t, promise, ps.Promise())
|
|
// Result should still be 42
|
|
i, err = promise.Result(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 42, i)
|
|
// Trying to reject should fail
|
|
set = ps.Reject(errors.New("boom"))
|
|
assert.False(t, set, "set should be false")
|
|
}
|
|
|
|
func TestMustFulfill(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
// First call should succeed
|
|
ps.MustFulfill(42)
|
|
// And this promise should now resolve to 42
|
|
i, err := ps.Promise().Result(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 42, i)
|
|
// Second call should panic
|
|
assert.PanicsWithValue(t, "CompletionSource already resolved", func() {
|
|
ps.MustFulfill(43)
|
|
})
|
|
}
|
|
|
|
func TestReject(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
boom := errors.New("boom")
|
|
set := ps.Reject(boom)
|
|
assert.True(t, set, "set should be true")
|
|
promise := ps.Promise()
|
|
_, err := promise.Result(context.Background())
|
|
require.Equal(t, boom, err)
|
|
// Trying to reject again should fail
|
|
set = ps.Reject(errors.New("bigger boom"))
|
|
assert.False(t, set, "set should be false")
|
|
// Asking for the promise again should give the same promise
|
|
assert.Equal(t, promise, ps.Promise())
|
|
// Result should still be boom
|
|
promise = ps.Promise()
|
|
_, err = promise.Result(context.Background())
|
|
require.Equal(t, boom, err)
|
|
// Trying to fulfill should fail
|
|
set = ps.Fulfill(42)
|
|
assert.False(t, set, "set should be false")
|
|
}
|
|
|
|
func TestMustReject(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
// First call should succeed
|
|
boom := errors.New("boom")
|
|
ps.MustReject(boom)
|
|
// And this promise should now resolve to boom
|
|
_, err := ps.Promise().Result(context.Background())
|
|
require.Equal(t, boom, err)
|
|
// Second call should panic
|
|
assert.PanicsWithValue(t, "CompletionSource already resolved", func() {
|
|
ps.MustReject(errors.New("boom again"))
|
|
})
|
|
}
|
|
|
|
func TestManyGets(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
ctx := context.Background()
|
|
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 10; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
got, err := ps.Promise().Result(ctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 42, got)
|
|
}()
|
|
}
|
|
|
|
ps.Fulfill(42)
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestAwaitCancelled(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
ps := &CompletionSource[int]{}
|
|
p := ps.Promise()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
|
|
_, err := p.Result(ctx)
|
|
assert.ErrorIs(t, err, context.Canceled)
|
|
}()
|
|
|
|
cancel()
|
|
<-done
|
|
|
|
// The await was cancelled, not the promise so we should be able to fulfill and then wait again.
|
|
set := ps.Fulfill(12)
|
|
assert.True(t, set, "set should be true")
|
|
|
|
i, err := p.Result(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 12, i)
|
|
}
|
|
|
|
func TestTryResult(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
p := ps.Promise()
|
|
|
|
// TryResult should return false if the promise is not yet resolved.
|
|
_, _, ok := p.TryResult()
|
|
assert.False(t, ok)
|
|
|
|
// Fulfilling the promise should return true.
|
|
set := ps.Fulfill(42)
|
|
assert.True(t, set, "set should be true")
|
|
|
|
// TryResult should now return true and the result.
|
|
i, err, ok := p.TryResult()
|
|
assert.True(t, ok)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 42, i)
|
|
}
|
|
|
|
func TestTryResultRace(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ps := &CompletionSource[int]{}
|
|
p := ps.Promise()
|
|
|
|
// Start a promise that will keep trying to get the result of p.
|
|
result := Run(func() (int, error) {
|
|
for {
|
|
i, err, ok := p.TryResult()
|
|
if ok {
|
|
return i, err
|
|
}
|
|
}
|
|
})
|
|
|
|
// Set the result of p, setting should return true.
|
|
go func() {
|
|
set := ps.Fulfill(42)
|
|
assert.True(t, set, "set should be true")
|
|
}()
|
|
|
|
// Wait for the result promise.
|
|
i, err := result.Result(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 42, i)
|
|
}
|
|
|
|
func TestRunError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Run should return a promise that is rejected if the function returns an error.
|
|
result := Run(func() (int, error) {
|
|
return 0, errors.New("boom")
|
|
})
|
|
|
|
_, err := result.Result(context.Background())
|
|
assert.Error(t, err)
|
|
}
|