Skip to content

test(pulse): add tests for price updates removal and max price IDs validation #2676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 27, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2117,4 +2117,128 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {

// Required to receive ETH when withdrawing funds
receive() external payable {}

function testUpdateSubscriptionRemovesPriceUpdatesForRemovedPriceIds()
public
{
// 1. Setup: Add subscription with 3 price feeds, update prices
uint8 numInitialFeeds = 3;
uint256 subscriptionId = addTestSubscriptionWithFeeds(
scheduler,
numInitialFeeds,
address(reader)
);
scheduler.addFunds{value: 1 ether}(subscriptionId);

// Get initial price IDs and create mock price feeds
bytes32[] memory initialPriceIds = createPriceIds(numInitialFeeds);
uint64 publishTime = SafeCast.toUint64(block.timestamp);

// Setup and perform initial price update
(
PythStructs.PriceFeed[] memory priceFeeds,
uint64[] memory slots
) = createMockPriceFeedsWithSlots(publishTime, numInitialFeeds);
mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots);

vm.prank(pusher);
scheduler.updatePriceFeeds(
subscriptionId,
createMockUpdateData(priceFeeds)
);

// Store the removed price ID for later use
bytes32 removedPriceId = initialPriceIds[numInitialFeeds - 1];

// 2. Action: Update subscription to remove the last price feed
(SchedulerState.SubscriptionParams memory params, ) = scheduler
.getSubscription(subscriptionId);

// Create new price IDs array without the last ID
bytes32[] memory newPriceIds = new bytes32[](numInitialFeeds - 1);
for (uint i = 0; i < newPriceIds.length; i++) {
newPriceIds[i] = initialPriceIds[i];
}

params.priceIds = newPriceIds;

vm.expectEmit();
emit SubscriptionUpdated(subscriptionId);
scheduler.updateSubscription(subscriptionId, params);

// 3. Verification:
// - Verify that the removed price ID is no longer part of the subscription's price IDs
(SchedulerState.SubscriptionParams memory updatedParams, ) = scheduler
.getSubscription(subscriptionId);
assertEq(
updatedParams.priceIds.length,
numInitialFeeds - 1,
"Subscription should have one less price ID"
);

bool removedPriceIdFound = false;
for (uint i = 0; i < updatedParams.priceIds.length; i++) {
if (updatedParams.priceIds[i] == removedPriceId) {
removedPriceIdFound = true;
break;
}
}
assertFalse(
removedPriceIdFound,
"Removed price ID should not be in the subscription's price IDs"
);

// - Querying all feeds should return only the remaining feeds
PythStructs.Price[] memory allPricesAfterUpdate = scheduler
.getPricesUnsafe(subscriptionId, new bytes32[](0));
assertEq(
allPricesAfterUpdate.length,
newPriceIds.length,
"Querying all should only return remaining feeds"
);

// - Verify that trying to get the price of the removed feed directly reverts
bytes32[] memory removedIdArray = new bytes32[](1);
removedIdArray[0] = removedPriceId;
vm.expectRevert(
abi.encodeWithSelector(
InvalidPriceId.selector,
removedPriceId,
bytes32(0)
)
);
scheduler.getPricesUnsafe(subscriptionId, removedIdArray);
}

function testUpdateSubscriptionRevertsWithTooManyPriceIds() public {
// 1. Setup: Create a subscription with a valid number of price IDs
uint8 initialNumFeeds = 2;
uint256 subscriptionId = addTestSubscriptionWithFeeds(
scheduler,
initialNumFeeds,
address(reader)
);

// 2. Prepare params with too many price IDs (MAX_PRICE_IDS_PER_SUBSCRIPTION + 1)
(SchedulerState.SubscriptionParams memory currentParams, ) = scheduler
.getSubscription(subscriptionId);

uint16 tooManyFeeds = uint16(
scheduler.MAX_PRICE_IDS_PER_SUBSCRIPTION()
) + 1;
bytes32[] memory tooManyPriceIds = createPriceIds(tooManyFeeds);

SchedulerState.SubscriptionParams memory newParams = currentParams;
newParams.priceIds = tooManyPriceIds;

// 3. Expect revert when trying to update with too many price IDs
vm.expectRevert(
abi.encodeWithSelector(
TooManyPriceIds.selector,
tooManyFeeds,
scheduler.MAX_PRICE_IDS_PER_SUBSCRIPTION()
)
);
scheduler.updateSubscription(subscriptionId, newParams);
}
}
Loading