From d6bd1c12633788ef8dc169d979b2455a0f63b008 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Mon, 24 Jun 2024 21:40:08 -0400 Subject: [PATCH] feat(vowTools): asVow should not wrap a vow as a vow --- packages/vow/src/tools.js | 2 +- packages/vow/src/vow-utils.js | 11 ++++++++--- packages/vow/test/asVow.test.js | 32 +++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/vow/src/tools.js b/packages/vow/src/tools.js index 950d9ca66370..2f0edad6bf6f 100644 --- a/packages/vow/src/tools.js +++ b/packages/vow/src/tools.js @@ -6,7 +6,7 @@ import { prepareWatchUtils } from './watch-utils.js'; import { makeAsVow } from './vow-utils.js'; /** @import {Zone} from '@agoric/base-zone' */ -/** @import {IsRetryableReason, Vow} from './types.js' */ +/** @import {IsRetryableReason} from './types.js' */ /** * @param {Zone} zone diff --git a/packages/vow/src/vow-utils.js b/packages/vow/src/vow-utils.js index 664bfa0698d7..2da8ccda1c50 100644 --- a/packages/vow/src/vow-utils.js +++ b/packages/vow/src/vow-utils.js @@ -85,12 +85,17 @@ export const makeAsVow = makeVowKit => { * @returns {Vow>} */ const asVow = fn => { - const kit = makeVowKit(); + let result; try { - kit.resolver.resolve(fn()); + result = fn(); } catch (e) { - kit.resolver.reject(e); + result = Promise.reject(e); + } + if (isVow(result)) { + return result; } + const kit = makeVowKit(); + kit.resolver.resolve(result); return kit.vow; }; return harden(asVow); diff --git a/packages/vow/test/asVow.test.js b/packages/vow/test/asVow.test.js index 25d220055844..3109c74e027c 100644 --- a/packages/vow/test/asVow.test.js +++ b/packages/vow/test/asVow.test.js @@ -1,14 +1,14 @@ // @ts-check import test from 'ava'; +import { E } from '@endo/far'; import { makeHeapZone } from '@agoric/base-zone/heap.js'; import { prepareVowTools } from '../src/tools.js'; -import { isVow } from '../src/vow-utils.js'; +import { getVowPayload, isVow } from '../src/vow-utils.js'; test('asVow takes a function that throws/returns synchronously and returns a vow', async t => { - const zone = makeHeapZone(); - const { watch, when, asVow } = prepareVowTools(zone); + const { watch, when, asVow } = prepareVowTools(makeHeapZone()); const fnThatThrows = () => { throw Error('fail'); @@ -16,11 +16,15 @@ test('asVow takes a function that throws/returns synchronously and returns a vow const vowWithRejection = asVow(fnThatThrows); t.true(isVow(vowWithRejection)); - await t.throwsAsync(when(vowWithRejection), { message: 'fail' }, 'failure '); + await t.throwsAsync( + when(vowWithRejection), + { message: 'fail' }, + 'error should propogate as promise rejection', + ); const isWatchAble = watch(asVow(fnThatThrows)); t.true(isVow(vowWithRejection)); - await t.throwsAsync(when(isWatchAble), { message: 'fail' }, 'failure '); + await t.throwsAsync(when(isWatchAble), { message: 'fail' }); const fnThatReturns = () => { return 'early return'; @@ -30,3 +34,21 @@ test('asVow takes a function that throws/returns synchronously and returns a vow t.is(await when(vowWithReturn), 'early return'); t.is(await when(watch(vowWithReturn)), 'early return'); }); + +test('asVow does not resolve a vow to a vow', async t => { + const { watch, when, asVow } = prepareVowTools(makeHeapZone()); + + const testVow = watch(Promise.resolve('payload')); + const testVowAsVow = asVow(() => testVow); + + const vowPayload = getVowPayload(testVowAsVow); + assert(vowPayload?.vowV0, 'testVowAsVow is a vow'); + const unwrappedOnce = await E(vowPayload.vowV0).shorten(); + t.false( + isVow(unwrappedOnce), + 'vows passed to asVow are not rewrapped as vows', + ); + t.is(unwrappedOnce, 'payload'); + + t.is(await when(testVow), await when(testVowAsVow), 'result is preserved'); +});