40227-vm/backend/src/services/staff_awards_reviews.test.ts

226 lines
6.8 KiB
TypeScript

import { afterEach, describe, mock, test } from 'node:test';
import assert from 'node:assert/strict';
import { Op } from 'sequelize';
import db from '@/db/models';
import StaffAwardsReviewsService from '@/services/staff_awards_reviews';
import { FEATURE_PERMISSIONS } from '@/shared/constants/product-permissions';
import { ROLE_NAMES, ROLE_SCOPES } from '@/shared/constants/roles';
import { createTestUser } from '@/test-utils';
function permission(name: string) {
return { name };
}
function isRecord(value: unknown): value is Record<PropertyKey, unknown> {
return typeof value === 'object' && value !== null;
}
function staffUser(overrides: Record<string, unknown> = {}) {
const row = {
id: 'staff-1',
email: 'teacher@flatlogic.com',
firstName: 'Emily',
lastName: 'Johnson',
organizationId: 'org-1',
schoolId: 'school-1',
campusId: 'campus-1',
app_role: { name: ROLE_NAMES.TEACHER },
campus: { name: 'Tigers Campus', schoolId: 'school-1' },
school: { name: 'Demo School' },
...overrides,
};
return {
...row,
get: () => row,
};
}
function awardRecord(overrides: Record<string, unknown> = {}) {
const row = {
id: 'record-1',
title: 'Teacher of the Year',
record_type: 'award',
year_label: '2025-2026',
notes: 'Outstanding classroom management.',
file_name: null,
file_url: null,
file_is_image: false,
staffUserId: 'staff-1',
staffUser: staffUser(),
createdAt: new Date('2026-06-26T12:00:00Z'),
updatedAt: new Date('2026-06-26T12:00:00Z'),
...overrides,
};
return {
...row,
get: () => row,
};
}
afterEach(() => {
mock.restoreAll();
});
describe('StaffAwardsReviewsService', () => {
test('non-managers list only their own awards and reviews', async () => {
const actor = createTestUser({
id: 'teacher-1',
organizationId: 'org-1',
organizations: { id: 'org-1' },
campusId: 'campus-1',
app_role: {
name: ROLE_NAMES.TEACHER,
scope: ROLE_SCOPES.CLASS,
globalAccess: false,
permissions: [permission(FEATURE_PERMISSIONS.READ_AWARDS_REVIEWS)],
},
});
let capturedWhere: unknown = null;
mock.method(db.staff_awards_reviews, 'findAndCountAll', async (options: unknown) => {
if (isRecord(options)) {
capturedWhere = options.where;
}
return { rows: [awardRecord({ staffUserId: 'teacher-1' })], count: 1 };
});
const result = await StaffAwardsReviewsService.list({ staffUserId: 'other-staff' }, actor);
assert.equal(result.canManage, false);
assert.equal(result.selectedStaffUserId, 'teacher-1');
assert.equal(isRecord(capturedWhere), true);
if (isRecord(capturedWhere)) {
assert.equal(capturedWhere.staffUserId, 'teacher-1');
assert.equal(capturedWhere.campusId, 'campus-1');
}
});
test('managers list staff using school descendant scope', async () => {
const actor = createTestUser({
id: 'principal-1',
organizationId: 'org-1',
organizations: { id: 'org-1' },
schoolId: 'school-1',
campusId: null,
app_role: {
name: ROLE_NAMES.PRINCIPAL,
scope: ROLE_SCOPES.SCHOOL,
globalAccess: false,
permissions: [
permission(FEATURE_PERMISSIONS.READ_AWARDS_REVIEWS),
permission(FEATURE_PERMISSIONS.MANAGE_AWARDS_REVIEWS),
],
},
});
let capturedWhere: unknown = null;
mock.method(db.users, 'findAndCountAll', async (options: unknown) => {
if (isRecord(options)) {
capturedWhere = options.where;
}
return { rows: [staffUser()], count: 1 };
});
const result = await StaffAwardsReviewsService.listStaff({}, actor);
assert.equal(result.count, 1);
assert.equal(result.rows[0]?.name, 'Emily Johnson');
assert.equal(isRecord(capturedWhere), true);
if (isRecord(capturedWhere)) {
assert.equal(Object.getOwnPropertySymbols(capturedWhere).includes(Op.or), true);
}
});
test('manager create stores selected staff tenant and campus fields', async () => {
const actor = createTestUser({
id: 'director-1',
organizationId: 'org-1',
organizations: { id: 'org-1' },
campusId: 'campus-1',
app_role: {
name: ROLE_NAMES.DIRECTOR,
scope: ROLE_SCOPES.CAMPUS,
globalAccess: false,
permissions: [
permission(FEATURE_PERMISSIONS.READ_AWARDS_REVIEWS),
permission(FEATURE_PERMISSIONS.MANAGE_AWARDS_REVIEWS),
],
},
});
let staffWhere: unknown = null;
let createdPayload: unknown = null;
mock.method(db.users, 'findOne', async (options: unknown) => {
if (isRecord(options)) {
staffWhere = options.where;
}
return staffUser({
id: 'teacher-1',
organizationId: 'org-1',
schoolId: null,
campusId: 'campus-1',
campus: { schoolId: 'school-1', name: 'Tigers Campus' },
});
});
mock.method(db.sequelize, 'transaction', async () => ({
commit: async () => undefined,
rollback: async () => undefined,
}));
mock.method(db.staff_awards_reviews, 'create', async (payload: unknown) => {
createdPayload = payload;
return awardRecord(isRecord(payload) ? payload : {});
});
const created = await StaffAwardsReviewsService.create(
{
staffUserId: 'teacher-1',
title: ' Teacher of the Year ',
type: 'award',
year: '2025-2026',
notes: ' Great progress. ',
fileName: 'award.pdf',
fileUrl: '/private/files/award.pdf',
fileIsImage: false,
},
actor,
);
assert.equal(created.title, 'Teacher of the Year');
assert.equal(isRecord(staffWhere), true);
if (isRecord(staffWhere)) {
assert.equal(staffWhere.id, 'teacher-1');
assert.equal(Object.getOwnPropertySymbols(staffWhere).includes(Op.or), true);
}
assert.equal(isRecord(createdPayload), true);
if (isRecord(createdPayload)) {
assert.equal(createdPayload.organizationId, 'org-1');
assert.equal(createdPayload.schoolId, 'school-1');
assert.equal(createdPayload.campusId, 'campus-1');
assert.equal(createdPayload.staffUserId, 'teacher-1');
assert.equal(createdPayload.createdById, 'director-1');
assert.equal(createdPayload.updatedById, 'director-1');
}
});
test('staff list requires awards/reviews management permission', async () => {
const actor = createTestUser({
id: 'teacher-1',
organizationId: 'org-1',
organizations: { id: 'org-1' },
campusId: 'campus-1',
app_role: {
name: ROLE_NAMES.TEACHER,
scope: ROLE_SCOPES.CLASS,
globalAccess: false,
permissions: [permission(FEATURE_PERMISSIONS.READ_AWARDS_REVIEWS)],
},
});
await assert.rejects(
() => StaffAwardsReviewsService.listStaff({}, actor),
{ name: 'ForbiddenError' },
);
});
});