Skip to content
Merged
10 changes: 10 additions & 0 deletions src/api_auth_guard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@
disconnect: jest.fn().mockResolvedValue(undefined),
getProductions: jest.fn().mockResolvedValue([]),
getProductionsLength: jest.fn().mockResolvedValue(0)
} as any;

Check warning on line 21 in src/api_auth_guard.test.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

const mockProductionManager = {
load: jest.fn().mockResolvedValue(undefined),
on: jest.fn(),
once: jest.fn(),
emit: jest.fn()
} as any;

Check warning on line 28 in src/api_auth_guard.test.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

const mockIngestManager = {
load: jest.fn().mockResolvedValue(undefined),
startPolling: jest.fn()
} as any;

Check warning on line 33 in src/api_auth_guard.test.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

const buildServer = () =>
api({
Expand Down Expand Up @@ -73,6 +73,16 @@
method: 'PATCH',
url: '/api/v1/session/sess-1',
payload: { sdpAnswer: 'v=0' }
},
{
method: 'PATCH',
url: '/api/v1/production/1/line/line-a/whep-source',
payload: { pinnedSessionId: null }
},
{
method: 'PATCH',
url: '/api/v1/session/sess-1/video-source',
payload: { pinnedSessionId: null }
}
];

Expand All @@ -97,7 +107,7 @@
const server = await buildServer();

const response = await server.inject({
method: method as any,

Check warning on line 110 in src/api_auth_guard.test.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
url,
payload: payload ?? {}
});
Expand Down
100 changes: 100 additions & 0 deletions src/api_productions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ const mockProductionManager = {
.mockImplementation((lines: any[], id: string) =>
lines.find((l) => l.id === id)
),
requireLine: jest.fn().mockImplementation((lines: any[], id: string) => {
const found = lines.find((l) => l.id === id);
if (!found) throw new Error(`Line ${id} not found`);
return found;
}),
clearWhepSourceIfPinned: jest.fn().mockResolvedValue(undefined),
updateUserLastSeen: jest
.fn()
.mockImplementation((sessionId: string) => sessionId === 'alive-session'),
Expand All @@ -180,6 +186,7 @@ const mockProductionManager = {
removeUserSession: jest
.fn()
.mockImplementation((sessionId: string) => sessionId),
emit: jest.fn(),
createUserSession: jest.fn().mockResolvedValue(undefined),
getActiveUsers: jest.fn().mockResolvedValue([]),
once: jest.fn(),
Expand Down Expand Up @@ -514,6 +521,47 @@ describe('Production API', () => {
});
});

describe('PATCH /session/:id', () => {
test('flushes a 204 once the SDP answer is handled', async () => {
const patchSession = {
_id: 'mock-session',
name: 'usersession',
productionId: '1',
lineId: '1',
endpointId: 'mock-endpoint-1',
isActive: true,
isExpired: false,
isWhip: false,
sessionDescription: { audio: { ssrcs: [1] }, video: { ssrcs: [] } }
};
const getSessionSpy = jest
.spyOn(mockDbManager, 'getSession')
.mockResolvedValue(patchSession as any);
mockProductionManager.getProduction = jest
.fn()
.mockResolvedValue(mockProductions[0]);
mockProductionManager.updateUserEndpoint = jest
.fn()
.mockResolvedValue(undefined);
mockProductionManager.updateSessionHasVideo = jest
.fn()
.mockResolvedValue(undefined);
mockCoreFunctions.handleAnswerRequest = jest
.fn()
.mockResolvedValue(undefined);

const response = await server.inject({
method: 'PATCH',
url: '/api/v1/session/mock-session',
body: { sdpAnswer: 'v=0' }
});

expect(response.statusCode).toBe(204);
expect(mockCoreFunctions.handleAnswerRequest).toHaveBeenCalled();
getSessionSpy.mockRestore();
});
});

describe('POST /production/:id/line/:id/participants', () => {
test('can do long polling for change in line participants', async () => {
mockProductionManager.once = jest
Expand Down Expand Up @@ -569,4 +617,56 @@ describe('Production API', () => {
expect(response.statusCode).toBe(410);
});
});
// The handler returns 410 when the session is gone, but that status was not
// in the declared response schema (204/400/500 only).
describe('PATCH /session/:id — session gone', () => {
test('answers 410 with the declared error shape', async () => {
const getSessionSpy = jest
.spyOn(mockDbManager, 'getSession')
.mockResolvedValue(null as any);

const response = await server.inject({
method: 'PATCH',
url: '/api/v1/session/missing-session',
body: { sdpAnswer: 'v=0' }
});

expect(response.statusCode).toBe(410);
expect(response.json().message).toContain('missing-session');
getSessionSpy.mockRestore();
});
});

describe('GET /session/:sessionId/name', () => {
test('returns the session display name', async () => {
mockProductionManager.getUserNameBySessionId = jest
.fn()
.mockResolvedValue('maximus');

const response = await server.inject({
method: 'GET',
url: '/api/v1/session/named-session/name'
});

expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
sessionId: 'named-session',
name: 'maximus'
});
});

test('returns 404 for an unknown session', async () => {
mockProductionManager.getUserNameBySessionId = jest
.fn()
.mockResolvedValue(null);

const response = await server.inject({
method: 'GET',
url: '/api/v1/session/nobody/name'
});

expect(response.statusCode).toBe(404);
expect(response.json()).toEqual({ message: 'Session not found' });
});
});
});
Loading
Loading