Skip to content

Commit 7d07183

Browse files
feat: add tests for price updates removal and max price IDs validation
Co-Authored-By: Tejas Badadare <[email protected]>
1 parent dd73357 commit 7d07183

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

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

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,4 +2007,119 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
20072007

20082008
// Required to receive ETH when withdrawing funds
20092009
receive() external payable {}
2010+
2011+
function testUpdateSubscriptionRemovesPriceUpdatesForRemovedPriceIds() public {
2012+
// 1. Setup: Add subscription with 3 price feeds, update prices
2013+
uint8 numInitialFeeds = 3;
2014+
uint256 subscriptionId = addTestSubscriptionWithFeeds(
2015+
scheduler,
2016+
numInitialFeeds,
2017+
address(reader)
2018+
);
2019+
scheduler.addFunds{value: 1 ether}(subscriptionId);
2020+
2021+
// Get initial price IDs and create mock price feeds
2022+
bytes32[] memory initialPriceIds = createPriceIds(numInitialFeeds);
2023+
uint64 publishTime = SafeCast.toUint64(block.timestamp);
2024+
2025+
// Setup and perform initial price update
2026+
(PythStructs.PriceFeed[] memory priceFeeds, uint64[] memory slots) =
2027+
createMockPriceFeedsWithSlots(publishTime, numInitialFeeds);
2028+
mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots);
2029+
2030+
vm.prank(pusher);
2031+
scheduler.updatePriceFeeds(subscriptionId, createMockUpdateData(priceFeeds));
2032+
2033+
// Store the removed price ID for later use
2034+
bytes32 removedPriceId = initialPriceIds[numInitialFeeds - 1];
2035+
2036+
// 2. Action: Update subscription to remove the last price feed
2037+
(SchedulerState.SubscriptionParams memory params, ) = scheduler.getSubscription(subscriptionId);
2038+
2039+
// Create new price IDs array without the last ID
2040+
bytes32[] memory newPriceIds = new bytes32[](numInitialFeeds - 1);
2041+
for (uint i = 0; i < newPriceIds.length; i++) {
2042+
newPriceIds[i] = initialPriceIds[i];
2043+
}
2044+
2045+
params.priceIds = newPriceIds;
2046+
2047+
vm.expectEmit();
2048+
emit SubscriptionUpdated(subscriptionId);
2049+
scheduler.updateSubscription(subscriptionId, params);
2050+
2051+
// 3. Verification:
2052+
// - Verify that the removed price ID is no longer part of the subscription's price IDs
2053+
(SchedulerState.SubscriptionParams memory updatedParams, ) = scheduler.getSubscription(subscriptionId);
2054+
assertEq(
2055+
updatedParams.priceIds.length,
2056+
numInitialFeeds - 1,
2057+
"Subscription should have one less price ID"
2058+
);
2059+
2060+
bool removedPriceIdFound = false;
2061+
for (uint i = 0; i < updatedParams.priceIds.length; i++) {
2062+
if (updatedParams.priceIds[i] == removedPriceId) {
2063+
removedPriceIdFound = true;
2064+
break;
2065+
}
2066+
}
2067+
assertFalse(
2068+
removedPriceIdFound,
2069+
"Removed price ID should not be in the subscription's price IDs"
2070+
);
2071+
2072+
// - Querying all feeds should return only the remaining feeds
2073+
PythStructs.Price[] memory allPricesAfterUpdate = scheduler
2074+
.getPricesUnsafe(subscriptionId, new bytes32[](0));
2075+
assertEq(
2076+
allPricesAfterUpdate.length,
2077+
newPriceIds.length,
2078+
"Querying all should only return remaining feeds"
2079+
);
2080+
2081+
// - Verify that the removed price ID is not included in the returned prices
2082+
for (uint i = 0; i < allPricesAfterUpdate.length; i++) {
2083+
// We can't directly check the price ID from the Price struct
2084+
// But we can verify that the number of returned prices matches the number of price IDs
2085+
// in the updated subscription, which indirectly confirms that removed price IDs are not included
2086+
assertEq(
2087+
allPricesAfterUpdate.length,
2088+
updatedParams.priceIds.length,
2089+
"Number of returned prices should match number of price IDs in subscription"
2090+
);
2091+
}
2092+
}
2093+
2094+
2095+
2096+
function testUpdateSubscriptionRevertsWithTooManyPriceIds() public {
2097+
// 1. Setup: Create a subscription with a valid number of price IDs
2098+
uint8 initialNumFeeds = 2;
2099+
uint256 subscriptionId = addTestSubscriptionWithFeeds(
2100+
scheduler,
2101+
initialNumFeeds,
2102+
address(reader)
2103+
);
2104+
2105+
// 2. Prepare params with too many price IDs (MAX_PRICE_IDS_PER_SUBSCRIPTION + 1)
2106+
(SchedulerState.SubscriptionParams memory currentParams, ) = scheduler
2107+
.getSubscription(subscriptionId);
2108+
2109+
uint16 tooManyFeeds = uint16(scheduler.MAX_PRICE_IDS_PER_SUBSCRIPTION()) + 1;
2110+
bytes32[] memory tooManyPriceIds = createPriceIds(tooManyFeeds);
2111+
2112+
SchedulerState.SubscriptionParams memory newParams = currentParams;
2113+
newParams.priceIds = tooManyPriceIds;
2114+
2115+
// 3. Expect revert when trying to update with too many price IDs
2116+
vm.expectRevert(
2117+
abi.encodeWithSelector(
2118+
TooManyPriceIds.selector,
2119+
tooManyFeeds,
2120+
scheduler.MAX_PRICE_IDS_PER_SUBSCRIPTION()
2121+
)
2122+
);
2123+
scheduler.updateSubscription(subscriptionId, newParams);
2124+
}
20102125
}

0 commit comments

Comments
 (0)