Skip to content

Commit 58c3523

Browse files
test(pulse): add tests for price updates removal and max price IDs validation (#2676)
* feat: add tests for price updates removal and max price IDs validation Co-Authored-By: Tejas Badadare <tejas@dourolabs.xyz> * style: fix formatting in PulseScheduler.t.sol Co-Authored-By: Tejas Badadare <tejas@dourolabs.xyz> * test: address PR comments for price updates removal test Co-Authored-By: Tejas Badadare <tejas@dourolabs.xyz> * test: fix refs to mockParsePriceFeedUpdatesWithSlots --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Tejas Badadare <tejas@dourolabs.xyz> Co-authored-by: Tejas Badadare <tejasbadadare@gmail.com>
1 parent b58ad76 commit 58c3523

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,4 +2231,128 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
22312231

22322232
// Required to receive ETH when withdrawing funds
22332233
receive() external payable {}
2234+
2235+
function testUpdateSubscriptionRemovesPriceUpdatesForRemovedPriceIds()
2236+
public
2237+
{
2238+
// 1. Setup: Add subscription with 3 price feeds, update prices
2239+
uint8 numInitialFeeds = 3;
2240+
uint256 subscriptionId = addTestSubscriptionWithFeeds(
2241+
scheduler,
2242+
numInitialFeeds,
2243+
address(reader)
2244+
);
2245+
scheduler.addFunds{value: 1 ether}(subscriptionId);
2246+
2247+
// Get initial price IDs and create mock price feeds
2248+
bytes32[] memory initialPriceIds = createPriceIds(numInitialFeeds);
2249+
uint64 publishTime = SafeCast.toUint64(block.timestamp);
2250+
2251+
// Setup and perform initial price update
2252+
(
2253+
PythStructs.PriceFeed[] memory priceFeeds,
2254+
uint64[] memory slots
2255+
) = createMockPriceFeedsWithSlots(publishTime, numInitialFeeds);
2256+
mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots);
2257+
2258+
vm.prank(pusher);
2259+
scheduler.updatePriceFeeds(
2260+
subscriptionId,
2261+
createMockUpdateData(priceFeeds)
2262+
);
2263+
2264+
// Store the removed price ID for later use
2265+
bytes32 removedPriceId = initialPriceIds[numInitialFeeds - 1];
2266+
2267+
// 2. Action: Update subscription to remove the last price feed
2268+
(SchedulerState.SubscriptionParams memory params, ) = scheduler
2269+
.getSubscription(subscriptionId);
2270+
2271+
// Create new price IDs array without the last ID
2272+
bytes32[] memory newPriceIds = new bytes32[](numInitialFeeds - 1);
2273+
for (uint i = 0; i < newPriceIds.length; i++) {
2274+
newPriceIds[i] = initialPriceIds[i];
2275+
}
2276+
2277+
params.priceIds = newPriceIds;
2278+
2279+
vm.expectEmit();
2280+
emit SubscriptionUpdated(subscriptionId);
2281+
scheduler.updateSubscription(subscriptionId, params);
2282+
2283+
// 3. Verification:
2284+
// - Verify that the removed price ID is no longer part of the subscription's price IDs
2285+
(SchedulerState.SubscriptionParams memory updatedParams, ) = scheduler
2286+
.getSubscription(subscriptionId);
2287+
assertEq(
2288+
updatedParams.priceIds.length,
2289+
numInitialFeeds - 1,
2290+
"Subscription should have one less price ID"
2291+
);
2292+
2293+
bool removedPriceIdFound = false;
2294+
for (uint i = 0; i < updatedParams.priceIds.length; i++) {
2295+
if (updatedParams.priceIds[i] == removedPriceId) {
2296+
removedPriceIdFound = true;
2297+
break;
2298+
}
2299+
}
2300+
assertFalse(
2301+
removedPriceIdFound,
2302+
"Removed price ID should not be in the subscription's price IDs"
2303+
);
2304+
2305+
// - Querying all feeds should return only the remaining feeds
2306+
PythStructs.Price[] memory allPricesAfterUpdate = scheduler
2307+
.getPricesUnsafe(subscriptionId, new bytes32[](0));
2308+
assertEq(
2309+
allPricesAfterUpdate.length,
2310+
newPriceIds.length,
2311+
"Querying all should only return remaining feeds"
2312+
);
2313+
2314+
// - Verify that trying to get the price of the removed feed directly reverts
2315+
bytes32[] memory removedIdArray = new bytes32[](1);
2316+
removedIdArray[0] = removedPriceId;
2317+
vm.expectRevert(
2318+
abi.encodeWithSelector(
2319+
InvalidPriceId.selector,
2320+
removedPriceId,
2321+
bytes32(0)
2322+
)
2323+
);
2324+
scheduler.getPricesUnsafe(subscriptionId, removedIdArray);
2325+
}
2326+
2327+
function testUpdateSubscriptionRevertsWithTooManyPriceIds() public {
2328+
// 1. Setup: Create a subscription with a valid number of price IDs
2329+
uint8 initialNumFeeds = 2;
2330+
uint256 subscriptionId = addTestSubscriptionWithFeeds(
2331+
scheduler,
2332+
initialNumFeeds,
2333+
address(reader)
2334+
);
2335+
2336+
// 2. Prepare params with too many price IDs (MAX_PRICE_IDS_PER_SUBSCRIPTION + 1)
2337+
(SchedulerState.SubscriptionParams memory currentParams, ) = scheduler
2338+
.getSubscription(subscriptionId);
2339+
2340+
uint16 tooManyFeeds = uint16(
2341+
scheduler.MAX_PRICE_IDS_PER_SUBSCRIPTION()
2342+
) + 1;
2343+
bytes32[] memory tooManyPriceIds = createPriceIds(tooManyFeeds);
2344+
2345+
SchedulerState.SubscriptionParams memory newParams = currentParams;
2346+
newParams.priceIds = tooManyPriceIds;
2347+
2348+
// 3. Expect revert when trying to update with too many price IDs
2349+
vm.expectRevert(
2350+
abi.encodeWithSelector(
2351+
TooManyPriceIds.selector,
2352+
tooManyFeeds,
2353+
scheduler.MAX_PRICE_IDS_PER_SUBSCRIPTION()
2354+
)
2355+
);
2356+
scheduler.updateSubscription(subscriptionId, newParams);
2357+
}
22342358
}

0 commit comments

Comments
 (0)