Skip to content

Commit 637283d

Browse files
server: Seed, Location, & User Collections Refactor (#93)
* feat: seed mongoose module * feat: seed zod module * feat: seed routes * docs: add swagger comments * feat: location module + migrate location dependencies * chore: update test suites * feat: integrate Location routes * feat: user module * refactor: shared hooks * feat: integrate v2 user routes * chore: flatten user schema, prettify import statements, leave notes for future * chore: minor backwards compatability fixes
1 parent 28e03dd commit 637283d

39 files changed

+2849
-170
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import {
2+
afterAll,
3+
beforeAll,
4+
beforeEach,
5+
describe,
6+
expect,
7+
test
8+
} from '@jest/globals';
9+
import { MongoMemoryServer } from 'mongodb-memory-server';
10+
import mongoose from 'mongoose';
11+
12+
import { HubType, LocationType } from '../../../utils/constants';
13+
import Location from '../location.model';
14+
15+
describe('Location Model', () => {
16+
let mongoServer: MongoMemoryServer;
17+
18+
beforeAll(async () => {
19+
// Connect once at the start
20+
mongoServer = await MongoMemoryServer.create();
21+
await mongoose.connect(mongoServer.getUri());
22+
await Location.ensureIndexes();
23+
});
24+
25+
afterAll(async () => {
26+
// Disconnect once at the end
27+
await mongoose.connection.close();
28+
await mongoServer.stop();
29+
});
30+
31+
beforeEach(async () => {
32+
// Clear the database before each test
33+
await Location.deleteMany({});
34+
});
35+
36+
// Test schema validation
37+
test('valid location creation (basic)', async () => {
38+
const validLocation = {
39+
hubName: 'Test Hub',
40+
hubType: HubType.ESTABLISHMENT,
41+
locationType: LocationType.ROOFTOP,
42+
address: '123 Main St, City, State'
43+
};
44+
45+
const location = new Location(validLocation);
46+
const savedLocation = await location.save();
47+
48+
expect(savedLocation._id).toBeDefined();
49+
expect(savedLocation.hubName).toBe('Test Hub');
50+
expect(savedLocation.hubType).toBe(HubType.ESTABLISHMENT);
51+
expect(savedLocation.locationType).toBe(LocationType.ROOFTOP);
52+
expect(savedLocation.address).toBe('123 Main St, City, State');
53+
});
54+
55+
test('invalid location - missing required fields', async () => {
56+
const invalidLocation = {
57+
// Missing all required fields
58+
};
59+
60+
const location = new Location(invalidLocation);
61+
await expect(location.save()).rejects.toThrow();
62+
});
63+
64+
test('invalid location - missing hubName', async () => {
65+
const invalidLocation = {
66+
hubType: HubType.ESTABLISHMENT,
67+
locationType: LocationType.ROOFTOP,
68+
address: '123 Main St'
69+
};
70+
71+
const location = new Location(invalidLocation);
72+
await expect(location.save()).rejects.toThrow();
73+
});
74+
75+
test('invalid location - missing hubType', async () => {
76+
const invalidLocation = {
77+
hubName: 'Test Hub',
78+
locationType: LocationType.ROOFTOP,
79+
address: '123 Main St'
80+
};
81+
82+
const location = new Location(invalidLocation);
83+
await expect(location.save()).rejects.toThrow();
84+
});
85+
86+
test('invalid location - missing locationType', async () => {
87+
const invalidLocation = {
88+
hubName: 'Test Hub',
89+
hubType: HubType.ESTABLISHMENT,
90+
address: '123 Main St'
91+
};
92+
93+
const location = new Location(invalidLocation);
94+
await expect(location.save()).rejects.toThrow();
95+
});
96+
97+
test('invalid location - missing address', async () => {
98+
const invalidLocation = {
99+
hubName: 'Test Hub',
100+
hubType: HubType.ESTABLISHMENT,
101+
locationType: LocationType.ROOFTOP
102+
};
103+
104+
const location = new Location(invalidLocation);
105+
await expect(location.save()).rejects.toThrow();
106+
});
107+
108+
test('invalid location - invalid hubType enum', async () => {
109+
const invalidLocation = {
110+
hubName: 'Test Hub',
111+
hubType: 'INVALID_HUB_TYPE',
112+
locationType: LocationType.ROOFTOP,
113+
address: '123 Main St'
114+
};
115+
116+
const location = new Location(invalidLocation);
117+
await expect(location.save()).rejects.toThrow();
118+
});
119+
120+
test('invalid location - invalid locationType enum', async () => {
121+
const invalidLocation = {
122+
hubName: 'Test Hub',
123+
hubType: HubType.ESTABLISHMENT,
124+
locationType: 'INVALID_LOCATION_TYPE',
125+
address: '123 Main St'
126+
};
127+
128+
const location = new Location(invalidLocation);
129+
await expect(location.save()).rejects.toThrow();
130+
});
131+
132+
// Test uniqueness constraints
133+
test('duplicate hubName should fail', async () => {
134+
const locationData = {
135+
hubName: 'Duplicate Hub',
136+
hubType: HubType.ESTABLISHMENT,
137+
locationType: LocationType.ROOFTOP,
138+
address: '123 Main St'
139+
};
140+
141+
// Create first location
142+
const location1 = new Location(locationData);
143+
await location1.save();
144+
145+
// Try to create second location with same hubName
146+
const location2 = new Location(locationData);
147+
await expect(location2.save()).rejects.toThrow();
148+
});
149+
150+
test('duplicate address should fail', async () => {
151+
const locationData = {
152+
hubName: 'Test Hub',
153+
hubType: HubType.ESTABLISHMENT,
154+
locationType: LocationType.ROOFTOP,
155+
address: '123 Duplicate St'
156+
};
157+
158+
// Create first location
159+
const location1 = new Location(locationData);
160+
await location1.save();
161+
162+
// Try to create second location with same address
163+
const location2 = new Location({
164+
...locationData,
165+
hubName: 'Different Hub'
166+
});
167+
await expect(location2.save()).rejects.toThrow();
168+
});
169+
170+
test('can create multiple locations with different hubName and address', async () => {
171+
const location1 = new Location({
172+
hubName: 'Hub One',
173+
hubType: HubType.ESTABLISHMENT,
174+
locationType: LocationType.ROOFTOP,
175+
address: '123 First St'
176+
});
177+
178+
const location2 = new Location({
179+
hubName: 'Hub Two',
180+
hubType: HubType.CHURCH,
181+
locationType: LocationType.APPROXIMATE,
182+
address: '456 Second Ave'
183+
});
184+
185+
const savedLocation1 = await location1.save();
186+
const savedLocation2 = await location2.save();
187+
188+
expect(savedLocation1.hubName).toBe('Hub One');
189+
expect(savedLocation2.hubName).toBe('Hub Two');
190+
expect(savedLocation1.address).toBe('123 First St');
191+
expect(savedLocation2.address).toBe('456 Second Ave');
192+
expect(savedLocation1._id).not.toEqual(savedLocation2._id);
193+
});
194+
195+
// Test edge cases
196+
test('empty string hubName should fail', async () => {
197+
const location = new Location({
198+
hubName: '',
199+
hubType: HubType.ESTABLISHMENT,
200+
locationType: LocationType.ROOFTOP,
201+
address: '123 Main St'
202+
});
203+
204+
await expect(location.save()).rejects.toThrow();
205+
});
206+
207+
test('empty string address should fail', async () => {
208+
const location = new Location({
209+
hubName: 'Test Hub',
210+
hubType: HubType.ESTABLISHMENT,
211+
locationType: LocationType.ROOFTOP,
212+
address: ''
213+
});
214+
215+
await expect(location.save()).rejects.toThrow();
216+
});
217+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import mongoose, { InferSchemaType, Model, Schema } from 'mongoose';
2+
3+
import { HubType, LocationType } from '@/database/utils/constants';
4+
5+
const locationSchema = new Schema(
6+
{
7+
hubName: { type: String, required: true, unique: true },
8+
hubType: { type: String, enum: HubType, required: true },
9+
locationType: { type: String, enum: LocationType, required: true },
10+
address: { type: String, required: true, unique: true }
11+
},
12+
{ strict: 'throw' }
13+
);
14+
15+
export type ILocation = InferSchemaType<typeof locationSchema>;
16+
const Location: Model<ILocation> = mongoose.model<ILocation>(
17+
'Location',
18+
locationSchema
19+
);
20+
export default Location;

0 commit comments

Comments
 (0)