forked from wangziqi/gongxue-base
fix permissions and teacher attendance workflows
This commit is contained in:
174
apps/server/src/integration/dingtalk.service.spec.ts
Normal file
174
apps/server/src/integration/dingtalk.service.spec.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { DingTalkService } from './dingtalk.service';
|
||||
|
||||
describe('DingTalkService — queryShifts', () => {
|
||||
const originalAppKey = process.env.DINGTALK_APP_KEY;
|
||||
const originalAppSecret = process.env.DINGTALK_APP_SECRET;
|
||||
let service: DingTalkService;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.DINGTALK_APP_KEY = 'test-app-key';
|
||||
process.env.DINGTALK_APP_SECRET = 'test-app-secret';
|
||||
|
||||
service = new DingTalkService({} as never, {} as never);
|
||||
Object.assign(service, {
|
||||
accessToken: 'test-token',
|
||||
tokenExpiresAt: Date.now() + 3_600_000,
|
||||
});
|
||||
jest.spyOn(service as never, 'rateLimit').mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
global.fetch = undefined as unknown as typeof fetch;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (originalAppKey === undefined) delete process.env.DINGTALK_APP_KEY;
|
||||
else process.env.DINGTALK_APP_KEY = originalAppKey;
|
||||
if (originalAppSecret === undefined) delete process.env.DINGTALK_APP_SECRET;
|
||||
else process.env.DINGTALK_APP_SECRET = originalAppSecret;
|
||||
});
|
||||
|
||||
it('unwraps the paged result object returned by DingTalk', async () => {
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
json: jest.fn().mockResolvedValue({
|
||||
errcode: 0,
|
||||
errmsg: 'ok',
|
||||
result: {
|
||||
cursor: 678215070,
|
||||
has_more: false,
|
||||
result: [
|
||||
{ id: 677995086, name: 'A' },
|
||||
{ id: 678215070, name: 'B' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
}) as jest.MockedFunction<typeof fetch>;
|
||||
|
||||
await expect(service.queryShifts('manager')).resolves.toEqual([
|
||||
{ id: 677995086, name: 'A' },
|
||||
{ id: 678215070, name: 'B' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('requests subsequent pages using the cursor returned by DingTalk', async () => {
|
||||
global.fetch = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
json: jest.fn().mockResolvedValue({
|
||||
errcode: 0,
|
||||
errmsg: 'ok',
|
||||
result: {
|
||||
cursor: 200,
|
||||
has_more: true,
|
||||
result: [{ id: 100, name: '早班' }],
|
||||
},
|
||||
}),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
json: jest.fn().mockResolvedValue({
|
||||
errcode: 0,
|
||||
errmsg: 'ok',
|
||||
result: {
|
||||
cursor: 300,
|
||||
has_more: false,
|
||||
result: [{ id: 200, name: '晚班' }],
|
||||
},
|
||||
}),
|
||||
}) as jest.MockedFunction<typeof fetch>;
|
||||
|
||||
await expect(service.queryShifts('manager')).resolves.toEqual([
|
||||
{ id: 100, name: '早班' },
|
||||
{ id: 200, name: '晚班' },
|
||||
]);
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledTimes(2);
|
||||
expect(JSON.parse((global.fetch as jest.Mock).mock.calls[0][1].body)).toEqual({
|
||||
op_user_id: 'manager',
|
||||
cursor: 0,
|
||||
});
|
||||
expect(JSON.parse((global.fetch as jest.Mock).mock.calls[1][1].body)).toEqual({
|
||||
op_user_id: 'manager',
|
||||
cursor: 200,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DingTalkService — attendance machine only group', () => {
|
||||
const originalAppKey = process.env.DINGTALK_APP_KEY;
|
||||
const originalAppSecret = process.env.DINGTALK_APP_SECRET;
|
||||
let service: DingTalkService;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.DINGTALK_APP_KEY = 'test-app-key';
|
||||
process.env.DINGTALK_APP_SECRET = 'test-app-secret';
|
||||
service = new DingTalkService({} as never, {} as never);
|
||||
Object.assign(service, {
|
||||
accessToken: 'test-token',
|
||||
tokenExpiresAt: Date.now() + 3_600_000,
|
||||
});
|
||||
jest.spyOn(service as never, 'rateLimit').mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
global.fetch = undefined as unknown as typeof fetch;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (originalAppKey === undefined) delete process.env.DINGTALK_APP_KEY;
|
||||
else process.env.DINGTALK_APP_KEY = originalAppKey;
|
||||
if (originalAppSecret === undefined) delete process.env.DINGTALK_APP_SECRET;
|
||||
else process.env.DINGTALK_APP_SECRET = originalAppSecret;
|
||||
});
|
||||
|
||||
it('accepts the success envelope returned when updating an attendance group', async () => {
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
json: jest.fn().mockResolvedValue({
|
||||
success: true,
|
||||
result: { id: 123, name: '排课_冲刺班' },
|
||||
request_id: 'request-1',
|
||||
}),
|
||||
}) as jest.MockedFunction<typeof fetch>;
|
||||
|
||||
await expect(
|
||||
service.updateAttendanceGroup({
|
||||
id: 123,
|
||||
name: '排课_冲刺班',
|
||||
type: 'TURN',
|
||||
owner: 'manager',
|
||||
members: [{ role: 'Attendance', type: 'StaffMember', user_id: 'student-1' }],
|
||||
shift_ids: [456],
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('disables mobile-oriented punching when creating a machine-only group', async () => {
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
json: jest.fn().mockResolvedValue({
|
||||
errcode: 0,
|
||||
errmsg: 'ok',
|
||||
result: { id: 123 },
|
||||
}),
|
||||
}) as jest.MockedFunction<typeof fetch>;
|
||||
|
||||
await service.createAttendanceGroup({
|
||||
name: '排课_冲刺班',
|
||||
type: 'TURN',
|
||||
owner: 'manager',
|
||||
members: [{ role: 'Attendance', type: 'StaffMember', user_id: 'student-1' }],
|
||||
shift_ids: [456],
|
||||
attendance_machine_only: true,
|
||||
});
|
||||
|
||||
const body = JSON.parse((global.fetch as jest.Mock).mock.calls[0][1].body);
|
||||
expect(body.top_group).toEqual(expect.objectContaining({
|
||||
enable_emp_select_class: false,
|
||||
disable_check_without_schedule: true,
|
||||
enable_outside_check: false,
|
||||
enable_position_ble: false,
|
||||
positions: [],
|
||||
wifis: [],
|
||||
}));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user