How to test NestJS interceptor with error catch/throw?

I am porting/rewriting a plain Express app to NestJS. The app is middleware that transforms/passes along requests to an endpoint. I have a fairly simple Express interceptor that sets an auth token header on outgoing requests, but if that fails/expires it will retry with an updated auth token.

@Injectable()
export class TokenInterceptor implements NestInterceptor {
  private readonly authUrl = 'https://example.come/tokens/OAuth';
  private token = '';

  constructor(private readonly http: HttpService) {
    //Get the token when the app starts up
    this.fetchToken().subscribe((token) => {
      this.token = token;
      this.http.axiosRef.defaults.headers.common.Authorization = this.token;
    });
  }

  public intercept(_context: ExecutionContext, next: CallHandler): Observable<unknown> {
    this.http.axiosRef.defaults.headers.common.Authorization = this.token;
    
    return next.handle().pipe(
      catchError(async (error: AxiosError) => {
        if (error.config) {
          //If we have an auth error, we probably just need to re-fetch the token
          if (error.response?.status === HttpStatus.UNAUTHORIZED && error.config.url !== this.authUrl) {
            //without second check, we can loop forever
            this.token = await lastValueFrom(this.fetchToken());
            this.http.axiosRef.defaults.headers.common.Authorization = this.token; //reset default to valid
            return of(error.request); //retry request
          }
        }

        //Some other error type, so we want to throw that back
        return throwError(() => {
          const msg = error.message;
          const code = error.status ?? 500;
          return new HttpException(msg, code, { cause: error });
        });
      }),
    );
  }

  private fetchToken(): Observable<string> {
    return this.http
      .post<ISharepointTokenResponseData>(
        this.authUrl,
        {/*special params here*/},
      )
      .pipe(map((res) => `Bearer ${res.data.access_token}`));
  }
}

How do I properly write unit tests for all of this? Here is what I have so far, comments added on tests that are not working

describe('TokenInterceptor', () => {
  let module: TestingModule;
  let interceptor: SharepointTokenInterceptor;
  const httpService = mockDeep<HttpService>();

  /**
   * @description Normally this would be in the `beforeEach` but we need to put a jest spy on http requests,
   * so Call this INSIDE each test AFTER setting up HTTP spies.
   * It has to be this way because the class constructor kicks off an initial HTTP request, which would happen before our test could run */
  const createTestingModule = async () => {
    module = await Test.createTestingModule({
      providers: [
        SharepointTokenInterceptor,
        {
          provide: HttpService,
          useValue: httpService,
        },
      ],
    }).compile();
    interceptor = module.get(SharepointTokenInterceptor);
  };

  const provideMockToken = (mockToken: string) => {
    jest.spyOn(httpService, 'post').mockReturnValueOnce(
      of({
        data: { access_token: mockToken },
      } as AxiosResponse<ISharepointTokenResponseData>),
    );
  };

  it('should fetch an auth token initially', async () => {
    const mockToken = 'my-fake-token';
    provideMockToken(mockToken);
    await createTestingModule();

    //Make sure the correct HTTP calls are made
    expect(httpService.post).toHaveBeenCalledTimes(1);
    expect(httpService.post).toHaveBeenCalledWith(
      'https://example.come/tokens/OAuth',
      {/*special params here*/},
    );

    //Make sure the value returned from the HTTP calls is properly set on the properties we expect
    // eslint-disable-next-line @typescript-eslint/dot-notation -- we are being naughty and accessing a private class member so we have to use string indexing to cheat!
    expect(interceptor['token']).toEqual(`Bearer ${mockToken}`);
    expect(httpService.axiosRef.defaults.headers.common.Authorization).toEqual(`Bearer ${mockToken}`);
  });

  it('should add auth headers to outgoing requests when we have an auth token', async () => {
    provideMockToken('originalToken');
    await createTestingModule();

    const executionCtxMock = mockDeep<ExecutionContext>();
    const nextMock: CallHandler = {
      handle: jest.fn(() => of()),
    };

    //Manually force a new token - this should never happen but this is just insurance to know that we do set the token on each outgoing request
    // eslint-disable-next-line @typescript-eslint/dot-notation -- we have to use string indexing to cheat!
    interceptor['token'] = `Bearer new-token`;
    interceptor.intercept(executionCtxMock, nextMock);

    expect(httpService.axiosRef.defaults.headers.common.Authorization).toEqual(`Bearer new-token`);
    expect(nextMock.handle).toHaveBeenCalledTimes(1);
    expect(nextMock.handle).toHaveBeenCalledWith();
  });

  describe('Error Handling', () => {
    beforeEach(async () => {
      provideMockToken('example-token');
      await createTestingModule();
    });

    //==================================================================
    //This test fails because it returns success in the observable, even though it's using throwError!
    //I am not sure what is going on here.
    //Is my test wrong, or am I re-throwing an error wrong in the interceptor?
    it('should pass the error through for non-401 errors', (done: jest.DoneCallback) => {
      const executionCtxMock = mockDeep<ExecutionContext>();
      const nextMock: CallHandler = {
        handle: () =>
          throwError(
            () =>
              new AxiosError('you screwed up!', '403', {
                //@ts-expect-error -- we don't need the headers for this test, so this is OK
                headers: {},
                url: 'https://ytmnd.com',
              }),
          ),
      };

      interceptor.intercept(executionCtxMock, nextMock).subscribe({
        next: (res) => {
          console.log(res); //This logs out the exception even though this is in the success block!
          //This is not supposed to succeed!
          expect(false).toEqual(true);
          done();
        },
        error: (err: unknown) => {
          expect(err).toEqual(new BadRequestException('you screwed up!'));
          done();
        },
      });
    });
    
    xit('should refetch the auth token when we get a 401 "unauthorized" response and add that new token to outgoing requests', (done: jest.DoneCallback) => {
      //Haven't gotten to this one yet     
    });

    xit('should NOT refetch the auth token when we get a 401 "unauthorized" response and the URL is the URL for making an auth request', (done: jest.DoneCallback) => {
      //Haven't gotten to this one yet
    });
  });
});

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật