rwadurian/backend/services/referral-service/test/integration/mocks/prisma.mock.ts

285 lines
10 KiB
TypeScript

/**
* Prisma Mock for Integration Tests
* 不依赖真实数据库的模拟实现
*/
type MockReferralRelationship = {
id: bigint;
userId: bigint;
accountSequence: string; // 格式: D + YYMMDD + 5位序号
referrerId: bigint | null;
rootUserId: bigint | null;
myReferralCode: string;
usedReferralCode: string | null;
ancestorPath: bigint[];
depth: number;
directReferralCount: number;
activeDirectCount: number;
createdAt: Date;
updatedAt: Date;
};
type MockTeamStatistics = {
id: bigint;
userId: bigint;
directReferralCount: number;
totalTeamCount: number;
selfPlantingCount: number;
totalTeamPlantingCount: number;
effectivePlantingCountForRanking: number;
maxSingleTeamPlantingCount: number;
provinceCityDistribution: Record<string, Record<string, number>>;
lastCalcAt: Date | null;
createdAt: Date;
updatedAt: Date;
};
type MockDirectReferral = {
id: bigint;
referrerId: bigint;
referralId: bigint;
referralSequence: bigint;
teamPlantingCount: number;
createdAt: Date;
updatedAt: Date;
};
export class MockPrismaService {
private _referralRelationshipsStore: Map<bigint, MockReferralRelationship> = new Map();
private _teamStatisticsStore: Map<bigint, MockTeamStatistics> = new Map();
private _directReferralsStore: Map<string, MockDirectReferral> = new Map();
private idCounter = 1n;
// ReferralRelationship operations
referralRelationship = {
upsert: async (args: {
where: { userId: bigint };
create: Partial<MockReferralRelationship>;
update: Partial<MockReferralRelationship>;
}) => {
const existing = this._referralRelationshipsStore.get(args.where.userId);
if (existing) {
const updated = { ...existing, ...args.update, updatedAt: new Date() };
this._referralRelationshipsStore.set(args.where.userId, updated);
return updated;
}
const created: MockReferralRelationship = {
id: this.idCounter++,
userId: args.where.userId,
accountSequence: args.create.accountSequence ?? `D${Date.now()}`,
referrerId: args.create.referrerId ?? null,
rootUserId: args.create.rootUserId ?? null,
myReferralCode: args.create.myReferralCode!,
usedReferralCode: args.create.usedReferralCode ?? null,
ancestorPath: args.create.ancestorPath ?? [],
depth: args.create.depth ?? 0,
directReferralCount: 0,
activeDirectCount: 0,
createdAt: new Date(),
updatedAt: new Date(),
};
this._referralRelationshipsStore.set(args.where.userId, created);
return created;
},
findUnique: async (args: { where: { userId?: bigint; myReferralCode?: string }; select?: unknown }) => {
if (args.where.userId) {
return this._referralRelationshipsStore.get(args.where.userId) ?? null;
}
if (args.where.myReferralCode) {
for (const r of this._referralRelationshipsStore.values()) {
if (r.myReferralCode === args.where.myReferralCode) return r;
}
}
return null;
},
findMany: async (args: { where?: { referrerId?: bigint }; orderBy?: unknown }) => {
const results: MockReferralRelationship[] = [];
for (const r of this._referralRelationshipsStore.values()) {
if (!args.where?.referrerId || r.referrerId === args.where.referrerId) {
results.push(r);
}
}
return results;
},
count: async (args: { where: { myReferralCode?: string; userId?: bigint } }) => {
let count = 0;
for (const r of this._referralRelationshipsStore.values()) {
if (args.where.myReferralCode && r.myReferralCode === args.where.myReferralCode) count++;
if (args.where.userId && r.userId === args.where.userId) count++;
}
return count;
},
};
// TeamStatistics operations
teamStatistics = {
upsert: async (args: {
where: { userId: bigint };
create: Partial<MockTeamStatistics>;
update: Partial<MockTeamStatistics>;
}) => {
const existing = this._teamStatisticsStore.get(args.where.userId);
if (existing) {
const updated = { ...existing, ...args.update, updatedAt: new Date() };
this._teamStatisticsStore.set(args.where.userId, updated);
return updated;
}
const created: MockTeamStatistics = {
id: this.idCounter++,
userId: args.where.userId,
directReferralCount: args.create.directReferralCount ?? 0,
totalTeamCount: args.create.totalTeamCount ?? 0,
selfPlantingCount: args.create.selfPlantingCount ?? 0,
totalTeamPlantingCount: args.create.totalTeamPlantingCount ?? 0,
effectivePlantingCountForRanking: args.create.effectivePlantingCountForRanking ?? 0,
maxSingleTeamPlantingCount: args.create.maxSingleTeamPlantingCount ?? 0,
provinceCityDistribution: (args.create.provinceCityDistribution as Record<string, Record<string, number>>) ?? {},
lastCalcAt: args.create.lastCalcAt ?? new Date(),
createdAt: new Date(),
updatedAt: new Date(),
};
this._teamStatisticsStore.set(args.where.userId, created);
return created;
},
create: async (args: { data: Partial<MockTeamStatistics> }) => {
const created: MockTeamStatistics = {
id: this.idCounter++,
userId: args.data.userId!,
directReferralCount: args.data.directReferralCount ?? 0,
totalTeamCount: args.data.totalTeamCount ?? 0,
selfPlantingCount: args.data.selfPlantingCount ?? 0,
totalTeamPlantingCount: args.data.totalTeamPlantingCount ?? 0,
effectivePlantingCountForRanking: args.data.effectivePlantingCountForRanking ?? 0,
maxSingleTeamPlantingCount: args.data.maxSingleTeamPlantingCount ?? 0,
provinceCityDistribution: (args.data.provinceCityDistribution as Record<string, Record<string, number>>) ?? {},
lastCalcAt: args.data.lastCalcAt ?? new Date(),
createdAt: new Date(),
updatedAt: new Date(),
};
this._teamStatisticsStore.set(created.userId, created);
return created;
},
findUnique: async (args: { where: { userId: bigint } }) => {
return this._teamStatisticsStore.get(args.where.userId) ?? null;
},
findMany: async (args: {
where?: { userId?: { in: bigint[] }; effectivePlantingCountForRanking?: { gt: number } };
orderBy?: unknown[];
skip?: number;
take?: number;
select?: unknown;
}) => {
let results = Array.from(this._teamStatisticsStore.values());
if (args.where?.userId?.in) {
results = results.filter((s) => args.where!.userId!.in.includes(s.userId));
}
if (args.where?.effectivePlantingCountForRanking?.gt !== undefined) {
results = results.filter(
(s) => s.effectivePlantingCountForRanking > args.where!.effectivePlantingCountForRanking!.gt,
);
}
// Sort by score descending
results.sort((a, b) => b.effectivePlantingCountForRanking - a.effectivePlantingCountForRanking);
if (args.skip) results = results.slice(args.skip);
if (args.take) results = results.slice(0, args.take);
return results;
},
count: async (args: { where: { effectivePlantingCountForRanking?: { gt: number } } }) => {
let count = 0;
for (const s of this._teamStatisticsStore.values()) {
if (
args.where.effectivePlantingCountForRanking?.gt !== undefined &&
s.effectivePlantingCountForRanking > args.where.effectivePlantingCountForRanking.gt
) {
count++;
}
}
return count;
},
update: async (args: { where: { userId: bigint }; data: Partial<MockTeamStatistics> }) => {
const existing = this._teamStatisticsStore.get(args.where.userId);
if (!existing) throw new Error('Not found');
const updated = { ...existing, ...args.data, updatedAt: new Date() };
this._teamStatisticsStore.set(args.where.userId, updated);
return updated;
},
};
// DirectReferral operations
directReferral = {
upsert: async (args: {
where: { uk_referrer_referral: { referrerId: bigint; referralId: bigint } };
create: Partial<MockDirectReferral>;
update: Partial<MockDirectReferral> | { teamPlantingCount: { increment: number } };
}) => {
const key = `${args.where.uk_referrer_referral.referrerId}-${args.where.uk_referrer_referral.referralId}`;
const existing = this._directReferralsStore.get(key);
if (existing) {
let teamPlantingCount = existing.teamPlantingCount;
if ('teamPlantingCount' in args.update) {
if (typeof args.update.teamPlantingCount === 'object' && 'increment' in args.update.teamPlantingCount) {
teamPlantingCount += args.update.teamPlantingCount.increment;
} else {
teamPlantingCount = args.update.teamPlantingCount as number;
}
}
const updated = { ...existing, teamPlantingCount, updatedAt: new Date() };
this._directReferralsStore.set(key, updated);
return updated;
}
const created: MockDirectReferral = {
id: this.idCounter++,
referrerId: args.where.uk_referrer_referral.referrerId,
referralId: args.where.uk_referrer_referral.referralId,
referralSequence: args.create.referralSequence!,
teamPlantingCount: args.create.teamPlantingCount ?? 0,
createdAt: new Date(),
updatedAt: new Date(),
};
this._directReferralsStore.set(key, created);
return created;
},
findMany: async (args: { where: { referrerId: bigint }; select?: unknown }) => {
const results: MockDirectReferral[] = [];
for (const dr of this._directReferralsStore.values()) {
if (dr.referrerId === args.where.referrerId) {
results.push(dr);
}
}
return results;
},
};
// Transaction support
$transaction = async <T>(fn: (tx: MockPrismaService) => Promise<T>): Promise<T> => {
return fn(this);
};
// Cleanup for tests
$cleanup() {
this._referralRelationshipsStore.clear();
this._teamStatisticsStore.clear();
this._directReferralsStore.clear();
this.idCounter = 1n;
}
// Lifecycle methods
async onModuleInit() {}
async onModuleDestroy() {}
}