sách gpt4 ai đã đi

Angular 7 测试重试当模拟 http 请求无法实际重试时

In lại 作者:行者123 更新时间:2023-11-28 20:35:23 26 4
mua khóa gpt4 Nike

我有以下拦截器,每当获得任何 401(错误)响应时,它都会尝试使用 OAuth refresh_token.

基本上,刷新 token 是在第一个 401 请求上获取的,获取后,代码会等待 2.5 秒。在大多数情况下,第二个请求不会触发错误,但如果触发错误( token 无法刷新或其他),用户将被重定向到登录页面。

export class RefreshAuthenticationInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private tokenService: TokenService,
) {}

public intercept(request: HttpRequest, next: HttpHandler): Observable<>> {
return next.handle(request)
.pipe(
// this catches 401 requests and tries to refresh the auth token and try again.
retryWhen(errors => {
// this subject is returned to retryWhen
const subject = new Subject();

// didn't know a better way to keep track if this is the first
let first = true;

errors.subscribe((errorStatus) => {
// first time either pass the error through or get new token
if (first) {
this.authenticationService.authTokenGet('refresh_token', environment.clientId, environment.clientSecret, this.tokenService.getToken().refresh_token).subscribe((token: OauthAccessToken) => {
this.tokenService.save(token);
});

// second time still error means redirect to login
} khác {
this.router.navigateByUrl('/auth/login')
.then(() => subject.complete());

return;
}

// and of course make sure the second time is indeed seen as second time
first = false;

// trigger retry after 2,5 second to give ample time for token request to succeed
setTimeout(() => subject.next(), 2500);
});

trở lại chủ đề;
}),
}
}

问题出在测试中。一切正常,除了最终检查路由器是否实际导航到 /auth/login。不是,所以测试失败。

通过调试,我确定执行了 setTimeout 回调,但 subject.next() 似乎没有启动新请求。

我在某处读到,当通常在 http 模拟请求上使用 rxjs retry() 时,您应该再次刷新请求。这在下面的代码中被注释掉了,但给出了“无法刷新已取消的请求。”

    it('should catch 401 invalid_grant errors to try to refresh token the first time, redirect to login the second', fakeAsync(inject([HttpClient, HttpTestingController], (http: HttpClient, mock: HttpTestingController) => {
const oauthAccessToken: OauthAccessToken = {
// ...
};
authenticationService.authTokenGet.and.returnValue(of(oauthAccessToken));
tokenService.getToken.and.returnValue(oauthAccessToken);

// first request
http.get('/api');

const req = mock.expectOne('/api');
req.flush({error: 'invalid_grant'}, {
status: 401,
statusText: 'Unauthorized'
});

expect(authenticationService.authTokenGet).toHaveBeenCalled();

// second request
authenticationService.authTokenGet.calls.reset();

// req.flush({error: 'invalid_grant'}, {
// status: 401,
// statusText: 'Unauthorized'
// });

tick(2500);
expect(authenticationService.authTokenGet).not.toHaveBeenCalled();
expect(router.navigateByUrl).toHaveBeenCalledWith('/auth/login');

mock.verify();
})));

有谁知道如何解决这个测试?

PS:也欢迎任何关于代码本身的指针:)

1 Câu trả lời

最终我重构了代码,不使用上面的 first 技巧,这帮助我解决了问题。

对于那些在 retryWhen 和单元测试中苦苦挣扎的人,这是我的最终代码:

拦截器中的代码(简化)

retryWhen((errors: Observable) => errors.pipe(
flatMap((error, index) => {
// any other error than 401 with {error: 'invalid_grant'} should be ignored by this retryWhen
if (!error.status || error.status !== 401 || error.error.error !== 'invalid_grant') {
return throwError(error);
}

if (index === 0) {
// first time execute refresh token logic...
} khác {
this.router.navigateByUrl('/auth/login');
}

return of(error).pipe(delay(2500));
}),
take(2) // first request should refresh token and retry, if there's still an error the second time is the last time and should navigate to login
) ),

单元测试中的代码:

it('should catch 401 invalid_grant errors to try to refresh token the first time, redirect to login the second', fakeAsync(inject([HttpClient, HttpTestingController], (http: HttpClient, mock: HttpTestingController) => {    
// first request
http.get('/api').subscribe();

const req = mock.expectOne('/api');
req.flush({error: 'invalid_grant'}, {
status: 401,
statusText: 'Unauthorized'
});

// the expected delay of 2500 after the first retry
tick(2500);

// second request also unauthorized, should lead to redirect to /auth/login
const req2 = mock.expectOne('/api');
req2.flush({error: 'invalid_grant'}, {
status: 401,
statusText: 'Unauthorized'
});

expect(router.navigateByUrl).toHaveBeenCalledWith('/auth/login');

// somehow the take(2) will have another delay for another request, which is cancelled before it is executed.. maybe someone else would know how to fix this properly.. but I don't really care anymore at this point ;)
tick(2500);

const req3 = mock.expectOne('/api');
expect(req3.cancelled).toBeTruthy();

mock.verify();
})));

关于Angular 7 测试重试当模拟 http 请求无法实际重试时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55971604/

26 4 0
行者123
Hồ sơ cá nhân

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá Didi Taxi miễn phí
Mã giảm giá Didi Taxi
Giấy chứng nhận ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com