fix(jarvis-teams): enforce maxMembers capacity in addMember, return null on full/missing team (25 tests)

This commit is contained in:
saravanakumardb1 2026-03-01 17:03:49 -08:00
parent 5088507400
commit 573f54888c
2 changed files with 58 additions and 7 deletions

View File

@ -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', () => {

View File

@ -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()}`;