fix(jarvis-teams): enforce maxMembers capacity in addMember, return null on full/missing team (25 tests)
This commit is contained in:
parent
5088507400
commit
573f54888c
@ -117,8 +117,48 @@ describe('addMember', () => {
|
||||
role: 'member',
|
||||
invitedBy: 'user_1',
|
||||
});
|
||||
expect(member.status).toBe('invited');
|
||||
expect(member.role).toBe('member');
|
||||
expect(member).not.toBeNull();
|
||||
expect(member!.status).toBe('invited');
|
||||
expect(member!.role).toBe('member');
|
||||
});
|
||||
|
||||
it('returns null when team is at capacity', () => {
|
||||
// starter plan = 5 max, owner counts as 1
|
||||
const team = createTeam({ name: 'Full', ownerId: 'user_1' });
|
||||
// Add 4 more to fill capacity (owner = 1, so 4 more = 5)
|
||||
for (let i = 2; i <= 5; i++) {
|
||||
const m = addMember({
|
||||
teamId: team.teamId,
|
||||
userId: `user_${i}`,
|
||||
email: `u${i}@b.com`,
|
||||
displayName: `U${i}`,
|
||||
role: 'member',
|
||||
invitedBy: 'user_1',
|
||||
});
|
||||
expect(m).not.toBeNull();
|
||||
}
|
||||
// 6th should be rejected
|
||||
const rejected = addMember({
|
||||
teamId: team.teamId,
|
||||
userId: 'user_6',
|
||||
email: 'u6@b.com',
|
||||
displayName: 'U6',
|
||||
role: 'member',
|
||||
invitedBy: 'user_1',
|
||||
});
|
||||
expect(rejected).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for non-existent team', () => {
|
||||
const result = addMember({
|
||||
teamId: 'nope',
|
||||
userId: 'u1',
|
||||
email: 'a@b.com',
|
||||
displayName: 'A',
|
||||
role: 'member',
|
||||
invitedBy: 'u0',
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@ -133,7 +173,8 @@ describe('acceptInvite', () => {
|
||||
role: 'member',
|
||||
invitedBy: 'user_1',
|
||||
});
|
||||
const accepted = acceptInvite(member.id);
|
||||
expect(member).not.toBeNull();
|
||||
const accepted = acceptInvite(member!.id);
|
||||
expect(accepted?.status).toBe('active');
|
||||
expect(accepted?.joinedAt).toBeTruthy();
|
||||
});
|
||||
@ -156,7 +197,8 @@ describe('updateMemberRole', () => {
|
||||
role: 'member',
|
||||
invitedBy: 'user_1',
|
||||
});
|
||||
const updated = updateMemberRole(member.id, 'manager');
|
||||
expect(member).not.toBeNull();
|
||||
const updated = updateMemberRole(member!.id, 'manager');
|
||||
expect(updated?.role).toBe('manager');
|
||||
});
|
||||
|
||||
@ -178,8 +220,9 @@ describe('removeMember', () => {
|
||||
role: 'member',
|
||||
invitedBy: 'user_1',
|
||||
});
|
||||
expect(removeMember(member.id)).toBe(true);
|
||||
expect(getMember(member.id)).toBeUndefined();
|
||||
expect(member).not.toBeNull();
|
||||
expect(removeMember(member!.id)).toBe(true);
|
||||
expect(getMember(member!.id)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('cannot remove owner', () => {
|
||||
|
||||
@ -104,7 +104,15 @@ export function addMember(input: {
|
||||
displayName: string;
|
||||
role: 'owner' | 'manager' | 'member';
|
||||
invitedBy: string;
|
||||
}): TeamMember {
|
||||
}): TeamMember | null {
|
||||
// Enforce capacity (owner auto-add is exempt)
|
||||
if (input.role !== 'owner') {
|
||||
const team = teams.get(input.teamId);
|
||||
if (!team) return null;
|
||||
const currentCount = getTeamMembers(input.teamId).length;
|
||||
if (currentCount >= team.maxMembers) return null;
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const id = `member_${crypto.randomUUID()}`;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user