live-data-refresh.service.spec.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import { fakeAsync, TestBed, tick } from '@angular/core/testing';
  2. import { of, Subject } from 'rxjs';
  3. import { ApiService } from './api.service';
  4. import { LiveDataRefreshService, LiveVersions } from './live-data-refresh.service';
  5. import { SessionService } from './session.service';
  6. describe('LiveDataRefreshService', () => {
  7. let changes: Subject<{ source: 'local' | 'broadcast'; changedAt: string }>;
  8. let post: jasmine.Spy;
  9. let loggedIn: boolean;
  10. beforeEach(() => {
  11. changes = new Subject();
  12. post = jasmine.createSpy('post');
  13. loggedIn = true;
  14. TestBed.configureTestingModule({
  15. providers: [
  16. LiveDataRefreshService,
  17. { provide: ApiService, useValue: { post, dataChanges$: changes } },
  18. { provide: SessionService, useValue: { isLoggedIn: () => loggedIn } },
  19. ],
  20. });
  21. });
  22. it('refreshes full data only after a five-second version change', fakeAsync(() => {
  23. post.and.returnValues(
  24. of(envelope('learning=v1')),
  25. of(envelope('learning=v2')),
  26. );
  27. const refresh = jasmine.createSpy('refresh');
  28. const stop = TestBed.inject(LiveDataRefreshService).watch({
  29. scopes: ['learning'],
  30. studentId: () => 42,
  31. onRefresh: refresh,
  32. });
  33. expect(post).toHaveBeenCalledTimes(1);
  34. expect(refresh).not.toHaveBeenCalled();
  35. tick(5_000);
  36. expect(post).toHaveBeenCalledTimes(2);
  37. expect(refresh).toHaveBeenCalledOnceWith('version');
  38. stop();
  39. }));
  40. it('refreshes immediately after a local or another-tab write', () => {
  41. post.and.returnValue(of(envelope('review=v1')));
  42. const refresh = jasmine.createSpy('refresh');
  43. const stop = TestBed.inject(LiveDataRefreshService).watch({
  44. scopes: ['review'],
  45. studentId: () => 42,
  46. onRefresh: refresh,
  47. });
  48. changes.next({ source: 'local', changedAt: new Date().toISOString() });
  49. changes.next({ source: 'broadcast', changedAt: new Date().toISOString() });
  50. expect(refresh.calls.allArgs()).toEqual([['local-write'], ['broadcast']]);
  51. stop();
  52. });
  53. it('stops checking after logout', fakeAsync(() => {
  54. post.and.returnValue(of(envelope('learning=v1')));
  55. const stop = TestBed.inject(LiveDataRefreshService).watch({
  56. scopes: ['learning'],
  57. studentId: () => 42,
  58. onRefresh: () => undefined,
  59. });
  60. expect(post).toHaveBeenCalledTimes(1);
  61. loggedIn = false;
  62. tick(10_000);
  63. expect(post).toHaveBeenCalledTimes(1);
  64. stop();
  65. }));
  66. });
  67. function envelope(version: string): { retcode: number; result: LiveVersions } {
  68. return {
  69. retcode: 0,
  70. result: {
  71. version,
  72. scopeVersions: { learning: version, review: version },
  73. refreshedAt: '2026-09-25T00:00:00.000Z',
  74. syncStatus: { state: 'current', lagSeconds: 0, pending: 0 },
  75. },
  76. };
  77. }