Skip to content

Commit 69c1b78

Browse files
test: Use a generalized seed source for insertion to see the database state pre-test
1 parent ac35b50 commit 69c1b78

9 files changed

Lines changed: 109 additions & 211 deletions

File tree

apps/backend/src/db/BaseSeedSource.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { seed as SSBUCharacters_seed } from "@db/seeds/SSBUCharacters";
2+
import { getLogger } from "@logtape/logtape";
3+
import type { GuildSeasonRecord } from "@v1/guild/models";
4+
import type { MatchRecord } from "@v1/match/models";
5+
import type { SeasonRecord } from "@v1/season/models";
6+
import type { Knex } from "knex";
7+
8+
const log = getLogger(["grindcord", "db"]);
9+
10+
// Seek more customizable API where list of seeds can be provided to the seedsource
11+
export class BaseSeedSource {
12+
getSeeds() {
13+
return Promise.resolve(["SSBUCharacters"]);
14+
}
15+
16+
async getSeed(seed: string) {
17+
log.info(`Seeding ${seed}`);
18+
switch (seed) {
19+
case "SSBUCharacters":
20+
return {
21+
async seed(knex: Knex) {
22+
await SSBUCharacters_seed(knex);
23+
},
24+
};
25+
default:
26+
throw new Error(`Invalid seed: "${seed}"`);
27+
}
28+
}
29+
}
30+
31+
type InsertSeedSourceProps = {
32+
Match?: Array<Omit<MatchRecord, "match_id">>;
33+
Season?: Array<Omit<SeasonRecord, "season_id">>;
34+
GuildSeason?: Array<GuildSeasonRecord>;
35+
};
36+
export class InsertSeedSource {
37+
insertables: InsertSeedSourceProps;
38+
constructor(insertables: InsertSeedSourceProps) {
39+
this.insertables = insertables;
40+
}
41+
42+
getSeeds() {
43+
const keys = Object.keys(this.insertables) as Array<
44+
keyof InsertSeedSourceProps
45+
>;
46+
return Promise.resolve(keys);
47+
}
48+
49+
async getSeed(seed: keyof InsertSeedSourceProps) {
50+
const records = this.insertables[seed as keyof InsertSeedSourceProps];
51+
if (records === undefined) {
52+
throw new Error(
53+
"An undefined attribute was used as an argument in InsertSeedSource.",
54+
);
55+
}
56+
return {
57+
async seed(knex: Knex) {
58+
let i: keyof typeof records;
59+
for (i in records) {
60+
await knex(seed).insert(records[i]);
61+
}
62+
},
63+
};
64+
}
65+
}

apps/backend/src/db/init_tables.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ export async function init_tables(db: Knex = knexDb): Promise<void> {
4949
await trx.commit();
5050
}
5151

52-
export async function seed_db(
53-
seedSource: Knex.SeedSource<unknown>,
54-
db: Knex = knexDb,
55-
): Promise<void> {
56-
await db.seed.run({ seedSource });
57-
}
58-
5952
export async function init_views(db: Knex = knexDb) {
6053
const trx = await db.transaction();
6154
for (const _view of views) {

apps/backend/src/db/knexfile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BaseSeedSource } from "@db/BaseSeedSource";
1+
import { BaseSeedSource } from "@db/CustomSeedSource";
22
import config from "config";
33
import knex, { type Knex } from "knex";
44

apps/backend/src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { BaseSeedSource as SSBUCharacterSeedSource } from "@db/BaseSeedSource";
2-
import { init_tables, init_views, seed_db } from "@db/init_tables";
1+
import { BaseSeedSource as SSBUCharacterSeedSource } from "@db/CustomSeedSource";
2+
import { init_tables, init_views } from "@db/init_tables";
3+
import { knexDb } from "@db/knexfile";
34
import { serve } from "@hono/node-server";
45
import { honoLogger } from "@logtape/hono";
56
import { configure, getConsoleSink } from "@logtape/logtape";
@@ -22,7 +23,7 @@ const app = new Hono({ strict: false });
2223

2324
await init_tables();
2425

25-
await seed_db(new SSBUCharacterSeedSource());
26+
await knexDb.seed.run();
2627

2728
await init_views();
2829

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

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { init_tables, init_views, seed_db } from "@db/init_tables";
1+
import { InsertSeedSource } from "@db/CustomSeedSource";
2+
import { init_tables, init_views } from "@db/init_tables";
23
import { knexDb } from "@db/knexfile";
34
import type { GuildSeasonRecord } from "@v1/guild/models";
45
import {
@@ -12,23 +13,6 @@ import type { Knex } from "knex";
1213
import { afterEach, beforeEach, describe, expect, test } from "vitest";
1314
import { guildSeasonRecordFactory } from "./models.factories";
1415

15-
export class TestSeedSource {
16-
seeds: Array<(knex: Knex) => Promise<void>>;
17-
constructor(seeds: Array<(knex: Knex) => Promise<void>>) {
18-
this.seeds = seeds;
19-
}
20-
21-
getSeeds() {
22-
return Promise.resolve(this.seeds);
23-
}
24-
25-
async getSeed(seed: (knex: Knex) => Promise<void>) {
26-
return {
27-
seed,
28-
};
29-
}
30-
}
31-
3216
beforeEach(async () => {
3317
await init_tables(knexDb);
3418
await init_views(knexDb);
@@ -47,14 +31,9 @@ describe("GuildSeason table operations", () => {
4731
let guild_id: string;
4832
beforeEach(async () => {
4933
season_record = currentSeasonRecordFactory();
50-
await seed_db(
51-
new TestSeedSource([
52-
async (knex: Knex) => {
53-
await knex("Season").insert(season_record);
54-
},
55-
]),
56-
knexDb,
57-
);
34+
await knexDb.seed.run({
35+
seedSource: new InsertSeedSource({ Season: [season_record] }),
36+
});
5837

5938
season_id = await knexDb("Season")
6039
.first()
@@ -63,14 +42,11 @@ describe("GuildSeason table operations", () => {
6342

6443
guild_season_record = guildSeasonRecordFactory({ season_id });
6544
guild_id = guild_season_record.guild_id;
66-
await seed_db(
67-
new TestSeedSource([
68-
async (knex: Knex) => {
69-
await knex("GuildSeason").insert(guild_season_record);
70-
},
71-
]),
72-
knexDb,
73-
);
45+
await knexDb.seed.run({
46+
seedSource: new InsertSeedSource({
47+
GuildSeason: [guild_season_record],
48+
}),
49+
});
7450
});
7551
test("Nominal", async () => {
7652
expect(
@@ -85,14 +61,9 @@ describe("GuildSeason table operations", () => {
8561
let season_id: number;
8662
beforeEach(async () => {
8763
season_record = currentSeasonRecordFactory();
88-
await seed_db(
89-
new TestSeedSource([
90-
async (knex: Knex) => {
91-
await knex("Season").insert(season_record);
92-
},
93-
]),
94-
knexDb,
95-
);
64+
await knexDb.seed.run({
65+
seedSource: new InsertSeedSource({ Season: [season_record] }),
66+
});
9667

9768
season_id = await knexDb("Season")
9869
.first()
@@ -131,15 +102,11 @@ describe("GuildSeason table operations", () => {
131102
beforeEach(async () => {
132103
old_season_record = currentSeasonRecordFactory();
133104
new_season_record = currentSeasonRecordFactory();
134-
await seed_db(
135-
new TestSeedSource([
136-
async (knex: Knex) => {
137-
await knex("Season").insert(old_season_record);
138-
await knex("Season").insert(new_season_record);
139-
},
140-
]),
141-
knexDb,
142-
);
105+
await knexDb.seed.run({
106+
seedSource: new InsertSeedSource({
107+
Season: [old_season_record, new_season_record],
108+
}),
109+
});
143110
const old_season_id = await await knexDb("Season")
144111
.first()
145112
.where(new_season_record)
@@ -152,14 +119,11 @@ describe("GuildSeason table operations", () => {
152119
guild_season_record = guildSeasonRecordFactory({
153120
season_id: old_season_id,
154121
});
155-
await seed_db(
156-
new TestSeedSource([
157-
async (knex: Knex) => {
158-
await knex("GuildSeason").insert(guild_season_record);
159-
},
160-
]),
161-
knexDb,
162-
);
122+
await knexDb.seed.run({
123+
seedSource: new InsertSeedSource({
124+
GuildSeason: [guild_season_record],
125+
}),
126+
});
163127
});
164128
describe("Updating an existing `GuildSeason` record", () => {
165129
test("Nominal", async () => {

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

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { init_tables, init_views, seed_db } from "@db/init_tables";
1+
import { init_tables, init_views } from "@db/init_tables";
22
import { knexDb } from "@db/knexfile";
33
import type { MatchCharacterRecord } from "@v1/match/models";
44
import type { MatchQuery, MatchReport } from "@v1/match/schemas";
@@ -9,10 +9,10 @@ import {
99
getMatches,
1010
reportMatch,
1111
} from "@v1/match/service";
12-
import { seed as MatchFactorySeed } from "@v1/match/test/models.factories";
1312
import { matchReportFactory } from "@v1/match/test/schemas.factories";
1413
import { MatchReportDerivedRow } from "@v1/match/views";
1514
import type { Knex } from "knex";
15+
import { InsertSeedSource } from "@db/CustomSeedSource";
1616
import { afterEach, beforeEach, describe, expect, test } from "vitest";
1717

1818
// const _fakeMatchReport = MatchReport.parse({
@@ -32,30 +32,10 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest";
3232
// });
3333
const fakeMatchReport = matchReportFactory();
3434

35-
export class TestSeedSource {
36-
getSeeds() {
37-
return Promise.resolve(["FakeMatches"]);
38-
}
39-
40-
async getSeed(seed: string) {
41-
switch (seed) {
42-
case "FakeMatches":
43-
return {
44-
async seed(knex: Knex) {
45-
await MatchFactorySeed(knex);
46-
},
47-
};
48-
default:
49-
throw new Error(`Invalid seed: "${seed}"`);
50-
}
51-
}
52-
}
5335

5436
beforeEach(async () => {
5537
await init_tables(knexDb);
5638
await init_views(knexDb);
57-
58-
await seed_db(new TestSeedSource(), knexDb);
5939
});
6040
afterEach(async () => {
6141
await knexDb.destroy();

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

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -39,57 +39,3 @@ export const matchCharacterRecordFactory = (
3939
...match_character_record,
4040
};
4141
};
42-
43-
/** TODO: Might not be necessary to insert so many random records without a way to test them.
44-
* Find a way to exploit all that coverage.
45-
* Generate MatchReport data that matches the database records so that we can check parity?
46-
*/
47-
export async function seed(knex: Knex) {
48-
// Ten total players, one hundred games between them in a single guild.
49-
const guild_id = snowflake();
50-
const user_id_list = Array.from({ length: 10 }, snowflake);
51-
52-
// Generate all distinct pairs (i,j); i != j of users
53-
const possible_pairs = [];
54-
for (let i = 0; i < 10; i++) {
55-
for (let j = i + 1; j < 10; j++) {
56-
possible_pairs.push([i, j]);
57-
}
58-
}
59-
60-
// Choose from these pairs at random, 40 times with replacement
61-
for (let _ = 0; _ < 40; _++) {
62-
const pair = possible_pairs[randint(possible_pairs.length)];
63-
64-
const max_score = 1 + 2 * randint(25);
65-
const scores: Array<number> = ((w) => [w, randint(w)])(randint(max_score) + 1);
66-
67-
const match_id = (await knex("Match").insert({ guild_id }))[0];
68-
// Randomize who gets inserted first
69-
// Randomize who gets the winning score
70-
const swap = randint(4);
71-
const swap_score = Math.floor(swap / 2);
72-
const swap_user_id = Number(swap % 2 === 0);
73-
await knex("MatchPlayer").insert({
74-
match_id,
75-
user_id: user_id_list[pair[swap_user_id]],
76-
win_count: scores[swap_score],
77-
});
78-
await knex("MatchPlayer").insert({
79-
match_id,
80-
user_id: user_id_list[pair[(swap_user_id + 1) % 2]],
81-
win_count: scores[(swap_score + 1) % 2],
82-
});
83-
84-
for (let p_i = 0; p_i < pair.length; p_i++) {
85-
const characters = rand_character_array();
86-
for (let c_i = 0; c_i < characters.length; c_i++) {
87-
await knex("MatchCharacter").insert({
88-
match_id,
89-
user_id: user_id_list[pair[p_i]],
90-
fighter_number: SSBUCharEnumToFighterNumber.decode(characters[c_i]),
91-
});
92-
}
93-
}
94-
}
95-
}

0 commit comments

Comments
 (0)