Skip to content

Commit a180b96

Browse files
committed
add seedprovider tests
1 parent c22d307 commit a180b96

File tree

9 files changed

+15636
-51108
lines changed

9 files changed

+15636
-51108
lines changed

.yarn/install-state.gz

11.5 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "@react-native-async-storage/async-storage/jest/async-storage-mock";

components/SeedProvider.tsx

+46-29
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,39 @@ import { useEffect, useState } from "react";
1111
import { useAsyncStorage } from "@react-native-async-storage/async-storage";
1212
import { ActivityIndicator } from "react-native";
1313
import { getRandomQuestion } from "../services/questions";
14+
import { useToastController } from "@tamagui/toast";
1415

15-
const seed = async () => {
16+
const seedNecessarySets = async () => {
1617
try {
17-
const allValues = seedVals.questions.map(
18-
({
19-
id,
20-
questionText: question,
21-
answers,
22-
correctAnswer,
23-
level,
24-
category,
25-
questionSet,
26-
}) => ({
27-
id,
28-
question,
29-
answers,
30-
category: categories.includes(category as any)
31-
? (category as any)
32-
: "unknown",
33-
level: levels.includes(level as any) ? (level as any) : "N5",
34-
correctAnswer,
35-
questionSet,
36-
}),
18+
const availableQuestionSetIds = new Set(
19+
(
20+
await db
21+
.selectDistinct({ id: questions.questionSet })
22+
.from(questions)
23+
.execute()
24+
).map((set) => set.id)
3725
);
26+
3827
console.log("Seeding database...");
39-
await db.insert(questionSets).values(seedVals.questionSets).execute();
28+
29+
await db
30+
.insert(questionSets)
31+
.values(
32+
seedVals.questionSets.filter(
33+
(set) => !availableQuestionSetIds.has(set.id)
34+
)
35+
)
36+
.execute();
37+
38+
const allValues = seedVals.questions.filter(
39+
(question) => !availableQuestionSetIds.has(question.questionSet)
40+
);
4041

4142
// insert 1K questions at a time.
4243
for (let i = 0; i < allValues.length / 1000; i++) {
4344
await db
4445
.insert(questions)
45-
.values(allValues.slice(i * 1000, (i + 1) * 1000))
46+
.values(allValues.slice(i * 1000, (i + 1) * 1000) as unknown as any)
4647
.execute();
4748
}
4849
} catch (e) {
@@ -55,7 +56,7 @@ export const resetAndReseed = async () => {
5556
const answerObjects = await db.delete(answers).returning().execute();
5657
await db.delete(questions).execute();
5758
await db.delete(questionSets).execute();
58-
await seed();
59+
await seedNecessarySets();
5960

6061
// try to restore answers
6162
await db.insert(answers).values(answerObjects).execute();
@@ -64,21 +65,37 @@ export const resetAndReseed = async () => {
6465
}
6566
};
6667

68+
export const checkShouldSeed = async () => {
69+
const availableQuestionSetIds = new Set(
70+
(
71+
await db
72+
.selectDistinct({ id: questions.questionSet })
73+
.from(questions)
74+
.execute()
75+
).map((set) => set.id)
76+
);
77+
78+
if (
79+
seedVals.questionSets.some((set) => !availableQuestionSetIds.has(set.id))
80+
) {
81+
return true;
82+
}
83+
return false;
84+
};
85+
6786
export default function SeedProvider({ children }: React.PropsWithChildren) {
6887
const [shouldSeed, setShouldSeed] = useState<boolean | null>(null);
6988
const seeded = useAsyncStorage("seeded");
7089

7190
useEffect(() => {
72-
Promise.all([getRandomQuestion(), seeded.getItem()]).then(([q, val]) => {
73-
if (!q) return setShouldSeed(true);
74-
75-
setShouldSeed(val === "true" ? false : true);
91+
checkShouldSeed().then((isSeedingRequired) => {
92+
setShouldSeed(isSeedingRequired);
7693
});
7794
}, []);
7895

7996
useEffect(() => {
8097
if (shouldSeed === true) {
81-
seed().then(() => {
98+
seedNecessarySets().then(() => {
8299
seeded.setItem("true");
83100
setShouldSeed(false);
84101
});
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import * as React from "react";
2+
import {
3+
act,
4+
render,
5+
screen,
6+
waitFor,
7+
TestRenderer,
8+
} from "@testing-library/react-native";
9+
import * as z from "zod";
10+
import {
11+
levels,
12+
categories,
13+
questions,
14+
questionSets,
15+
answers,
16+
} from "../../db/schema";
17+
import SeedProvider from "../SeedProvider";
18+
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
19+
import migrations from "../../drizzle/migrations";
20+
import { db } from "@/services/db";
21+
import { count, eq, sql } from "drizzle-orm";
22+
import { Text } from "react-native";
23+
24+
jest.useFakeTimers();
25+
jest.mock("@/services/db");
26+
jest.mock("@react-native-async-storage/async-storage");
27+
28+
describe("SeedProvider", () => {
29+
beforeAll(() => {
30+
migrate(db, { migrationsFolder: "./drizzle/" });
31+
});
32+
33+
it("should render children after seed", async () => {
34+
render(
35+
<SeedProvider>
36+
<Text>Hi</Text>
37+
</SeedProvider>
38+
);
39+
40+
expect(screen.queryByText("Hi")).toBeFalsy();
41+
42+
await act(async () => {
43+
await jest.runAllTimersAsync();
44+
});
45+
46+
expect(screen.getByText("Hi")).toBeTruthy();
47+
});
48+
49+
it("should seed on empty db", async () => {
50+
expect((await db.select({ num: count() }).from(questions))[0].num).toBe(0);
51+
52+
render(<SeedProvider />);
53+
54+
await act(async () => {
55+
await jest.runAllTimers();
56+
});
57+
58+
await waitFor(async () => {
59+
expect(
60+
(await db.select({ num: count() }).from(questions))[0].num
61+
).toBeGreaterThan(0);
62+
});
63+
});
64+
65+
it("should seed missing question set", async () => {
66+
await db
67+
.insert(questionSets)
68+
.values({ id: 1, name: "Missing Set" })
69+
.execute();
70+
await db
71+
.insert(questions)
72+
.values({
73+
question: "What is the capital of France?",
74+
answers: ["Paris", "London", "Berlin", "Madrid"],
75+
correctAnswer: 0,
76+
level: "easy",
77+
category: "geography",
78+
questionSet: 1,
79+
})
80+
.execute();
81+
82+
render(<SeedProvider />);
83+
84+
await act(async () => {
85+
await jest.runAllTimers();
86+
});
87+
88+
await waitFor(async () => {
89+
expect(
90+
(
91+
await db
92+
.select({ num: count() })
93+
.from(questions)
94+
.where(eq(questions.questionSet, 1))
95+
)[0].num
96+
).toBe(1);
97+
98+
expect(
99+
(await db.select({ num: count() }).from(questions))[0].num
100+
).toBeGreaterThan(1);
101+
});
102+
});
103+
104+
beforeEach(async () => {
105+
jest.clearAllMocks();
106+
// delete all tables
107+
await db.delete(questions).execute();
108+
await db.delete(questionSets).execute();
109+
await db.delete(answers).execute();
110+
});
111+
});
112+
113+
describe("seed.json", () => {
114+
it("should be valid", () => {
115+
const seedFile = require("../../constants/seed.json");
116+
117+
const questionSchema = z.array(
118+
z.object({
119+
id: z.number(),
120+
question: z.string(),
121+
answers: z.array(z.string()),
122+
correctAnswer: z.number(),
123+
level: z.union(levels.map(z.literal)),
124+
category: z.union(categories.map(z.literal)),
125+
questionSet: z.number(),
126+
})
127+
);
128+
questionSchema.parse(seedFile.questions);
129+
});
130+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`SeedProvider should render correctly 1`] = `<ActivityIndicator />`;

0 commit comments

Comments
 (0)