我有一个测试用例执行以下操作(开玩笑):
... (some setup code irrelevant to the issue)
await assertEndpointResponse("post", "cancel-subscription", HttpStatus.OK, inviterContext.axiosAuthHeaders);
await clock.advanceDays(40);
await delay(CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY);
await verifyGetSubscription(customer.id, SubscriptionType.DUO_MONTHLY, "canceled", inviterContext.axiosAuthHeaders)
...
基本上,订阅是在设置的早期使用测试时钟创建的,然后取消,然后我想验证它是否在我的数据库中取消。我相信代码的其余部分是正确的,因为它用于大量其他测试,并且出于同样的原因,我对时钟实现也充满信心。不过,下面提供了:
import Stripe from "stripe";
export default class StripeTestClock {
private stripe: Stripe;
private clockId?: string;
private frozenTime: number=0;
constructor(stripeInstance: Stripe) {
this.stripe = stripeInstance;
}
/**
* Creates a Stripe test clock starting at the given frozen time.
* If no frozen time is provided, it defaults to the current Unix timestamp.
*
* @param frozenTime - The initial time for the test clock (in seconds since Unix epoch).
* @returns The ID of the created test clock.
*/
async create(frozenTime: number = Math.floor(Date.now() / 1000)): Promise<string> {
if (this.clockId) {
console.warn("A test clock already exists. Reusing existing clock.");
return this.clockId;
}
try {
const clock = await this.stripe.testHelpers.testClocks.create({
frozen_time: frozenTime,
});
this.clockId = clock.id;
this.frozenTime = frozenTime;
return this.clockId;
} catch (error) {
console.error("Failed to create Stripe test clock:", (error as Error).message);
throw error;
}
}
/**
* Advances the test clock to a new frozen time.
*
* @param advanceToTime - The new time to advance the test clock to (in seconds since Unix epoch).
* @throws If the clock ID is not set or the Stripe API call fails.
*/
async advance(advanceToTime: number): Promise<void> {
if (!this.clockId) {
throw new Error("Cannot advance test clock: Clock ID is not set.");
}
try {
await this.stripe.testHelpers.testClocks.advance(this.clockId, {
frozen_time: advanceToTime,
});
this.frozenTime = advanceToTime; // Update the frozen time
} catch (error) {
console.error("Failed to advance Stripe test clock:", (error as Error).message);
throw error;
}
}
/**
* Advances the test clock by the specified number of days.
*
* @param days - The number of days to advance the clock.
* @throws If the clock ID is not set or the Stripe API call fails.
*/
async advanceDays(days: number): Promise<void> {
if (!this.clockId) {
throw new Error("Cannot advance test clock: Clock ID or is not set.");
}
const advanceToTime = this.frozenTime + days * 24 * 60 * 60; // Advance by days in seconds
await this.advance(advanceToTime);
}
/**
* Deletes the current test clock to clean up resources.
*
* @throws If the clock ID is not set or the Stripe API call fails.
*/
async delete(): Promise<void> {
if (!this.clockId) {
console.warn("No test clock to delete. Skipping deletion.");
return;
}
try {
await this.stripe.testHelpers.testClocks.del(this.clockId);
this.clockId = undefined;
this.frozenTime = 0;
} catch (error) {
console.error("Failed to delete Stripe test clock:", (error as Error).message);
throw error;
}
}
/**
* Retrieves the ID of the current test clock.
*
* @returns The ID of the current test clock, or undefined if no clock is set.
*/
getClockId(): string | undefined {
return this.clockId;
}
/**
* Retrieves the current frozen time of the test clock.
*
* @returns The current frozen time of the test clock, or undefined if no clock is set.
*/
getFrozenTime(): number {
return this.frozenTime;
}
/**
* Refreshes the current frozen time by fetching the latest test clock state.
*
* @throws If the clock ID is not set or the Stripe API call fails.
*/
async refreshFrozenTime(): Promise<void> {
if (!this.clockId) {
throw new Error("Cannot refresh frozen time: Clock ID is not set.");
}
try {
const clock = await this.stripe.testHelpers.testClocks.retrieve(this.clockId);
this.frozenTime = clock.frozen_time;
} catch (error) {
console.error("Failed to refresh frozen time:", (error as Error).message);
throw error;
}
}
}
cancel-susbcription
端点仅执行以下操作:
await stripe.subscriptions.update(subscriptionId, {
cancel_at_period_end: true
})
出于某种原因,我在
stripe.subscription.deleted
调用后大约 500 毫秒收到我的 delay(CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY)
webhook,无论我在延迟中输入了什么。例如,CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY
是 10 秒,以下是 Stripe 中发送的 webhook:
但是当我将该行更新为
delay(CLOCK_ADVANCE_DELAY)
时,我明白了
(5 秒 + 一点差异)。而在另一个方向上,
delay(CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY + 20000)
则有 30 秒的差异。为什么会出现这种情况?
根据描述,看起来可能是由于
subscriptions.update
延迟调用造成的。你可以检查一下你的代码,看看在 delay()
之前是否还有另一个 clock.advanceDays(40)
被调用?