import { provideHttpClient } from '@angular/common/http'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { SchoolApi } from './school-api'; describe('SchoolApi contract', () => { let api: SchoolApi; let http: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()], }); api = TestBed.inject(SchoolApi); http = TestBed.inject(HttpTestingController); }); afterEach(() => http.verify()); it('loads all resources from the same school', () => { const received = vi.fn(); api.schoolData(7).subscribe(received); const school = { id: 7, name: 'Studio', revision: 0, dancerIds: [], danceGroupIds: [], showIds: [], }; http .expectOne('/api/dance-schools/7/data') .flush({ school, dancers: [], groups: [], shows: [], coaches: [] }); expect(received).toHaveBeenCalledWith({ school, dancers: [], groups: [], shows: [], coaches: [], }); }); it('sends the loaded school revision without retrying a stale mutation', () => { api.schoolData(7).subscribe(); http .expectOne('/api/dance-schools/7/data') .flush({ school: { id: 7, revision: 4 }, dancers: [], groups: [], shows: [], coaches: [] }); const failure = vi.fn(); api.saveDancer(7, { name: 'Sam', birthDate: '2010-05-12' }).subscribe({ error: failure }); const request = http.expectOne('/api/dance-schools/7/dancers'); expect(request.request.headers.get('X-School-Revision')).toBe('4'); request.flush({ message: 'Herlaad de gegevens' }, { status: 409, statusText: 'Conflict' }); expect(failure).toHaveBeenCalledOnce(); }); it('updates an existing entity even when its id is zero', () => { api.saveSchool({ name: 'Renamed' }, 0).subscribe(); const request = http.expectOne('/api/dance-schools/0'); expect(request.request.method).toBe('PUT'); expect(request.request.body).toEqual({ name: 'Renamed' }); request.flush({ id: 0, name: 'Renamed', revision: 0, dancerIds: [], danceGroupIds: [], showIds: [], }); }); it('keeps group details separate from memberships', () => { api .saveGroup( 7, { category: 'Teens', type: 'REGULAR', startAge: 12, endAge: 18, styles: ['HIPHOP'] }, 0, ) .subscribe(); const request = http.expectOne('/api/dance-schools/7/dance-groups/0'); expect(request.request.method).toBe('PUT'); expect(request.request.body).not.toHaveProperty('dancerIds'); request.flush({}); api.membership(7, 0, 3, true).subscribe(); const add = http.expectOne('/api/dance-schools/7/dance-groups/0/dancers/3'); expect(add.request.method).toBe('PUT'); expect(add.request.body).toBeNull(); add.flush(null); api.membership(7, 0, 3, false).subscribe(); const remove = http.expectOne('/api/dance-schools/7/dance-groups/0/dancers/3'); expect(remove.request.method).toBe('DELETE'); remove.flush(null); }); it('propagates server errors instead of pretending a save succeeded', () => { const failure = vi.fn(); api.saveDancer(7, { name: 'Sam', birthDate: '2010-05-12' }).subscribe({ error: failure }); http .expectOne('/api/dance-schools/7/dancers') .flush({ message: 'Invalid data' }, { status: 400, statusText: 'Bad Request' }); expect(failure).toHaveBeenCalledOnce(); }); it('starts a background calculation without a solver-length HTTP timeout', () => { api.schedule(7, 3).subscribe(); const request = http.expectOne('/api/dance-schools/7/shows/3/schedule'); expect(request.request.method).toBe('POST'); expect(request.request.timeout).toBeUndefined(); request.flush({ optimal: false, message: 'Valid result; optimal balance not proven' }); }); });