Skip to content

Commit df12c59

Browse files
feat: Reporting a match now fails when there is no matching GuildSeason for that guild.
1 parent 8794311 commit df12c59

5 files changed

Lines changed: 71 additions & 15 deletions

File tree

apps/backend/src/v1/match/models.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { z } from "zod";
33

44
export const MatchRecord = z.object({
55
match_id: z.int(),
6+
season_id: z.int(),
67
guild_id: z.string(),
78
created_at: z.iso.datetime(),
89
});
@@ -11,9 +12,23 @@ export const MatchTable = {
1112
table_name: "Match",
1213
initialize(table: Knex.TableBuilder) {
1314
table.increments("match_id");
14-
table.string("guild_id").index("guild_id_idx");
15+
table.integer("season_id").unsigned().index("season_id_idx");
16+
table.string("guild_id");
1517
// https://github.com/knex/knex/issues/6283
1618
table.timestamp("created_at").defaultTo(new Date().toISOString());
19+
20+
table
21+
.foreign("guild_id")
22+
.references("guild_id")
23+
.inTable("GuildSeason")
24+
.onUpdate("CASCADE")
25+
.onDelete("RESTRICT");
26+
table
27+
.foreign("season_id")
28+
.references("season_id")
29+
.inTable("Season")
30+
.onUpdate("CASCADE")
31+
.onDelete("RESTRICT");
1732
},
1833
};
1934

apps/backend/src/v1/match/schemas.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const MatchQuery = z
3838
.object({
3939
match_id: z.coerce.number(),
4040
guild_id: z.string(),
41+
season_id: z.string(),
4142
user_id: z.string(),
4243
character: SSBUCharEnumToFighterNumber,
4344
before_datetime: z.iso.datetime(),

apps/backend/src/v1/match/service.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { knexDb } from "@db/knexfile";
2+
import { getGuildSeason } from "@v1/guild/service";
23
import type {
34
MatchCharacterRecord,
45
MatchPlayerRecord,
@@ -41,7 +42,14 @@ export async function createMatch(
4142
guild_id: string,
4243
db: Knex = knexDb,
4344
): Promise<number> {
44-
const match_id = await db<MatchRecord>("Match").insert({ guild_id });
45+
const guild_season_record = await getGuildSeason(guild_id, db);
46+
if (!guild_season_record) {
47+
throw Error("The guild is not registered in a season.");
48+
}
49+
const match_id = await db<MatchRecord>("Match").insert({
50+
guild_id,
51+
season_id: guild_season_record.season_id,
52+
});
4553
return match_id[0];
4654
}
4755

apps/backend/src/v1/match/test/integration.test.ts

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { InsertSeedSource } from "@db/CustomSeedSource";
22
import { init_tables, init_views } from "@db/init_tables";
33
import { knexDb } from "@db/knexfile";
4+
import type { GuildSeasonRecord } from "@v1/guild/models";
5+
import { guildSeasonRecordFactory } from "@v1/guild/test/models.factories";
46
import type {
57
MatchCharacterRecord,
68
MatchPlayerRecord,
@@ -21,10 +23,11 @@ import {
2123
} from "@v1/match/service";
2224
import { matchReportFactory } from "@v1/match/test/schemas.factories";
2325
import { MatchReportDerivedRow } from "@v1/match/views";
26+
import { currentSeasonRecordFactory } from "@v1/season/test/models.factories";
2427
import { afterEach, beforeEach, describe, expect, test } from "vitest";
2528
import { matchPlayerRecordFactory, matchRecordFactory } from "./models.factories";
2629

27-
// Nominal fake data that is not inserted should be defined here
30+
// Nominal fake schema data should be defined here
2831
const nominal_match_report = matchReportFactory({
2932
guild_id: "123456789",
3033
players: [
@@ -41,9 +44,33 @@ const nominal_match_report = matchReportFactory({
4144
],
4245
});
4346

47+
// Seed a GuildSeason record for each test
48+
const guild_id = nominal_match_report.guild_id;
49+
let season_id: number;
4450
beforeEach(async () => {
4551
await init_tables(knexDb);
4652
await init_views(knexDb);
53+
54+
const season_record = currentSeasonRecordFactory();
55+
await knexDb.seed.run({
56+
seedSource: new InsertSeedSource({
57+
Season: [season_record],
58+
}),
59+
});
60+
season_id = await knexDb("Season")
61+
.first()
62+
.where(season_record)
63+
.then((res) => res.season_id);
64+
65+
const guild_season_record = guildSeasonRecordFactory({
66+
guild_id,
67+
season_id,
68+
});
69+
await knexDb.seed.run({
70+
seedSource: new InsertSeedSource({
71+
GuildSeason: [guild_season_record],
72+
}),
73+
});
4774
});
4875
afterEach(async () => {
4976
await knexDb.destroy();
@@ -54,15 +81,19 @@ describe("Match table operations", () => {
5481
describe("Use `createMatch`", () => {
5582
describe("Nominal", () => {
5683
test("Insert a Match record with `createMatch`", async () => {
57-
const match_id = await createMatch(
58-
nominal_match_report.guild_id,
59-
knexDb,
60-
);
84+
const match_id = await createMatch(guild_id, knexDb);
6185
const created_match = await knexDb<MatchRecord>("Match")
6286
.first()
6387
.where({ match_id });
6488
expect(created_match?.guild_id).toEqual(nominal_match_report.guild_id);
65-
console.log(created_match);
89+
expect(created_match?.season_id).toEqual(season_id);
90+
});
91+
});
92+
describe("Negative", () => {
93+
test("A Match record cannot be created when no matching GuildSeason is provided", async () => {
94+
await expect(createMatch("seasonoflove", knexDb)).rejects.toThrowError(
95+
"The guild is not registered in a season.",
96+
);
6697
});
6798
});
6899
});
@@ -73,7 +104,7 @@ describe("MatchPlayer table operations", () => {
73104
let match_record: Omit<MatchRecord, "match_id" | "created_at">;
74105
let match_id: number;
75106
beforeEach(async () => {
76-
match_record = matchRecordFactory({ guild_id: "123456789" });
107+
match_record = matchRecordFactory({ guild_id });
77108
await knexDb.seed.run({
78109
seedSource: new InsertSeedSource({ Match: [match_record] }),
79110
});
@@ -140,7 +171,7 @@ describe("MatchCharacter table operations", () => {
140171
let match_player_characters_2: Array<SSBUCharFighterNumber>;
141172
beforeEach(async () => {
142173
match_record = matchRecordFactory({
143-
guild_id: nominal_match_report.guild_id,
174+
guild_id,
144175
});
145176
await knexDb.seed.run({
146177
seedSource: new InsertSeedSource({
@@ -238,10 +269,8 @@ describe("MatchCharacter table operations", () => {
238269

239270
describe("Operations across Match, MatchPlayer, and MatchCharacter tables", () => {
240271
describe("Use `reportMatch`", async () => {
241-
describe("Random", () => {
272+
describe("Nominal", () => {
242273
test("Report a match", async () => {
243-
const { guild_id } = nominal_match_report;
244-
245274
const match_id = await reportMatch(nominal_match_report, knexDb);
246275

247276
const match_player_records = await knexDb("MatchPlayer")
@@ -280,10 +309,9 @@ describe("Operations across Match, MatchPlayer, and MatchCharacter tables", () =
280309
});
281310

282311
describe("Use `getMatches`", async () => {
283-
describe("Random", () => {
312+
describe("Nominal", () => {
284313
test("Retrieve a derived row from MatchReportView by match_id", async () => {
285314
const match_id = await reportMatch(nominal_match_report, knexDb);
286-
const { guild_id } = nominal_match_report;
287315

288316
const match_query: MatchQuery = { match_id };
289317
const result = await getMatches(match_query, knexDb);
@@ -298,6 +326,7 @@ describe("Operations across Match, MatchPlayer, and MatchCharacter tables", () =
298326
expected.push(
299327
MatchReportDerivedRow.parse({
300328
match_id,
329+
season_id,
301330
guild_id,
302331
user_id,
303332
win_count,

apps/backend/src/v1/match/views.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const MatchWinnerView = (db: Knex) => ({
2222

2323
export const MatchReportDerivedRow = z.object({
2424
match_id: z.int(),
25+
season_id: z.int(),
2526
guild_id: z.string(),
2627
user_id: z.string(),
2728
win_count: z.int(),
@@ -34,6 +35,7 @@ export const MatchReportView = (db: Knex) => ({
3435
initialize(view: Knex.ViewBuilder) {
3536
view.columns([
3637
"match_id",
38+
"season_id",
3739
"guild_id",
3840
"user_id",
3941
"win_count",
@@ -46,6 +48,7 @@ export const MatchReportView = (db: Knex) => ({
4648
.leftJoin("MatchCharacter", "Match.match_id", "MatchCharacter.match_id")
4749
.select(
4850
"Match.match_id as match_id",
51+
"Match.season_id as season_id",
4952
"Match.guild_id as guild_id",
5053
"MatchPlayer.user_id as user_id",
5154
"MatchPlayer.win_count as win_count",

0 commit comments

Comments
 (0)