Skip to content

Commit 7823b7f

Browse files
committed
#4212-fixes
1 parent 57b8dc0 commit 7823b7f

2 files changed

Lines changed: 68 additions & 44 deletions

File tree

src/backend/src/prisma/processes/seed-runner.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,35 @@ export class SeedRunner {
2121
const outputs = new Map<string, any>();
2222
const context: Record<string, any> = {};
2323

24+
const mergeOutputs = (target: Record<string, any>, source: Record<string, any>, sourceName: string) => {
25+
const duplicateKeys = Object.keys(source).filter((key) => key in target);
26+
27+
if (duplicateKeys.length > 0) {
28+
throw new Error(`Duplicate seed output keys from ${sourceName}: ${duplicateKeys.join(', ')}`);
29+
}
30+
31+
return Object.assign(target, source);
32+
};
33+
2434
for (const instance of this.instances) {
2535
instance.prisma = this.prisma;
2636

27-
const depOutputs = instance.dependencies().reduce((acc, depClass) => {
37+
const depOutputs = instance.dependencies().reduce<Record<string, any>>((acc, depClass) => {
2838
const output = outputs.get(depClass.name);
2939
if (!output) throw new Error(`Missing output for dependency: ${depClass.name}`);
30-
return { ...acc, ...output };
40+
41+
return mergeOutputs(acc, output, depClass.name);
3142
}, {});
3243

3344
console.log(`Running ${instance.constructor.name} (seed ${GLOBAL_SEED})...`);
3445
const output = await instance.run(depOutputs);
3546

3647
outputs.set(instance.constructor.name, output);
37-
Object.assign(context, output);
48+
mergeOutputs(context, output, instance.constructor.name);
3849

3950
console.log(`${instance.constructor.name} complete`);
4051
}
4152

4253
return context;
4354
}
44-
}
55+
}

src/backend/src/prisma/seed/team.process.ts

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { OrganizationOutput, OrganizationProcess } from './organization.process.
44
import { UsersOutput, UsersProcess } from './user.process.js';
55
import { seedTeamConfigs, teamCreateInput } from '../factories/teams.factory.js';
66
import { SeedProcess } from '../processes/seed-process.js';
7+
import type { FullUser } from '../context.js';
78

8-
const EXPECTED_TEAM_COUNT = 20;
99
const MIN_LEADS_PER_TEAM = 1;
1010
const MAX_LEADS_PER_TEAM = 3;
1111
const MIN_MEMBERS_PER_TEAM = 8;
@@ -25,10 +25,6 @@ export class TeamProcess extends SeedProcess<TeamInput, TeamOutput> {
2525
}
2626

2727
async run({ organization, admins, heads, leadership, members, teamTypes }: TeamInput): Promise<TeamOutput> {
28-
if (seedTeamConfigs.length !== EXPECTED_TEAM_COUNT) {
29-
throw new Error(`TeamProcess expected ${EXPECTED_TEAM_COUNT} teams but found ${seedTeamConfigs.length}.`);
30-
}
31-
3228
if (admins.length === 0 || heads.length === 0 || leadership.length === 0 || members.length === 0) {
3329
throw new Error('TeamProcess requires admins, heads, leadership, and members to create teams.');
3430
}
@@ -44,40 +40,57 @@ export class TeamProcess extends SeedProcess<TeamInput, TeamOutput> {
4440
return acc;
4541
}, {});
4642

47-
const possibleHeads = [...heads, ...admins, ...leadership];
48-
const possibleLeads = [...leadership, ...heads, ...admins];
49-
50-
const teams = await Promise.all(
51-
seedTeamConfigs.map((config, index) => {
52-
const head = possibleHeads[index % possibleHeads.length];
53-
54-
if (!head) {
55-
throw new Error('TeamProcess could not find a head for a team.');
56-
}
57-
58-
const leadPool = possibleLeads.filter((user) => user.userId !== head.userId);
59-
60-
const leads = this.faker.helpers.arrayElements(
61-
leadPool,
62-
this.faker.number.int({
63-
min: MIN_LEADS_PER_TEAM,
64-
max: MAX_LEADS_PER_TEAM
65-
})
66-
);
67-
68-
const teamMembers = this.faker.helpers.arrayElements(
69-
members,
70-
this.faker.number.int({
71-
min: MIN_MEMBERS_PER_TEAM,
72-
max: MAX_MEMBERS_PER_TEAM
73-
})
74-
);
75-
76-
return this.prisma.team.create({
77-
data: teamCreateInput(this.faker, organization.organizationId, head, leads, teamMembers, teamTypesByName, config)
78-
});
79-
})
80-
);
43+
const leadershipCandidates = [...heads, ...admins, ...leadership];
44+
45+
if (leadershipCandidates.length < seedTeamConfigs.length) {
46+
throw new Error(
47+
`Not enough head candidates (${leadershipCandidates.length}) for ${seedTeamConfigs.length} teams.`
48+
);
49+
}
50+
51+
const usedLeadIds = new Set<string>();
52+
53+
const getLeadsForTeam = (head: FullUser): FullUser[] => {
54+
const unusedLeadPool = leadershipCandidates.filter(
55+
(user) => user.userId !== head.userId && !usedLeadIds.has(user.userId)
56+
);
57+
58+
const fallbackLeadPool = leadershipCandidates.filter((user) => user.userId !== head.userId);
59+
const leadPool = unusedLeadPool.length >= MIN_LEADS_PER_TEAM ? unusedLeadPool : fallbackLeadPool;
60+
61+
if (leadPool.length < MIN_LEADS_PER_TEAM) {
62+
throw new Error('TeamProcess could not find enough leads for a team.');
63+
}
64+
65+
const leads = this.faker.helpers.arrayElements(
66+
leadPool,
67+
this.faker.number.int({
68+
min: MIN_LEADS_PER_TEAM,
69+
max: Math.min(MAX_LEADS_PER_TEAM, leadPool.length)
70+
})
71+
);
72+
73+
leads.forEach((lead) => usedLeadIds.add(lead.userId));
74+
75+
return leads;
76+
};
77+
78+
const teamCreateInputs = seedTeamConfigs.map((config, index) => {
79+
const head = leadershipCandidates[index];
80+
const leads = getLeadsForTeam(head);
81+
82+
const teamMembers = this.faker.helpers.arrayElements(
83+
members,
84+
this.faker.number.int({
85+
min: MIN_MEMBERS_PER_TEAM,
86+
max: MAX_MEMBERS_PER_TEAM
87+
})
88+
);
89+
90+
return teamCreateInput(this.faker, organization.organizationId, head, leads, teamMembers, teamTypesByName, config);
91+
});
92+
93+
const teams = await Promise.all(teamCreateInputs.map((data) => this.prisma.team.create({ data })));
8194

8295
const teamsByName = teams.reduce<Record<string, Team>>((acc, team) => {
8396
acc[team.teamName] = team;
@@ -92,4 +105,4 @@ export class TeamProcess extends SeedProcess<TeamInput, TeamOutput> {
92105

93106
return { teams, financeTeam, teamsByName };
94107
}
95-
}
108+
}

0 commit comments

Comments
 (0)