-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
169 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,58 @@ | ||
import { QueryFailedError, Repository } from "typeorm"; | ||
import { Repository } from "typeorm"; | ||
import { User } from "../entities/User"; | ||
import { DataSource } from "typeorm"; | ||
|
||
import AppDataSource from "../config/ormconfig"; | ||
|
||
export class UserService { | ||
private userRepository: Repository<User>; | ||
|
||
constructor(dataSource: DataSource) { | ||
this.userRepository = dataSource.getRepository(User); | ||
} | ||
|
||
async createUser(data: Partial<User>): Promise<User> { | ||
|
||
try { | ||
const user = this.userRepository.create(data); | ||
return await this.userRepository.save(user); | ||
} catch (error) { | ||
if (error instanceof QueryFailedError && error.message.includes("UNIQUE constraint failed")) { | ||
throw new Error("The wallet address is already in use. Please use a unique wallet address."); | ||
} | ||
if (error.code === '23505' || error.code === 'SQLITE_CONSTRAINT') { // Handle unique constraint errors | ||
if (error.message.includes('UQ_fc71cd6fb73f95244b23e2ef113')) { | ||
throw new Error('The wallet address is already in use. Please use a unique wallet address.'); | ||
} | ||
} | ||
throw error; | ||
private userRepository: Repository<User>; | ||
|
||
constructor() { | ||
this.userRepository = AppDataSource.getRepository(User); | ||
} | ||
|
||
async createUser(data: Partial<User>): Promise<User> { | ||
try { | ||
const user = this.userRepository.create(data); | ||
return await this.userRepository.save(user); | ||
} catch (error: any) { | ||
if (error.code === "23505" || error.code === "SQLITE_CONSTRAINT") { | ||
if (error.message.includes("UQ_fc71cd6fb73f95244b23e2ef113")) { | ||
throw new Error("The wallet address is already in use. Please use a unique wallet address."); | ||
} | ||
|
||
} | ||
|
||
async getUserById(id: number): Promise<User | null> { | ||
return await this.userRepository.findOneBy({ id }); | ||
} | ||
throw error; | ||
} | ||
|
||
async updateUser(id: number, data: Partial<User>): Promise<User | null> { | ||
try { | ||
await this.userRepository.update(id, data); | ||
return await this.getUserById(id); | ||
} catch (error) { | ||
if (error.code === '23505' || error.code === 'SQLITE_CONSTRAINT') { // Handle unique constraint errors | ||
if (error.message.includes('UQ_fc71cd6fb73f95244b23e2ef113')) { | ||
throw new Error('The wallet address is already in use. Please use a unique wallet address.'); | ||
} | ||
} | ||
|
||
if (error.code === '23505' || error.code === 'SQLITE_CONSTRAINT') { // Handle unique constraint errors | ||
if (error.message.includes('UQ_fc71cd6fb73f95244b23e2ef113')) { | ||
throw new Error('The wallet address is already in use. Please use a unique wallet address.'); | ||
} | ||
} | ||
throw error; | ||
} | ||
|
||
async getUserById(id: number): Promise<User | null> { | ||
return await this.userRepository.findOneBy({ id }); | ||
} | ||
|
||
async updateUser(id: number, data: Partial<User>): Promise<User | null> { | ||
try { | ||
const user = await this.userRepository.findOneBy({ id }); | ||
if (!user) { | ||
return null; | ||
} | ||
|
||
Object.assign(user, data); | ||
|
||
return await this.userRepository.save(user); | ||
} catch (error: any) { | ||
if (error.code === "23505" || error.code === "SQLITE_CONSTRAINT") { | ||
if (error.message.includes("UQ_fc71cd6fb73f95244b23e2ef113")) { | ||
throw new Error("The wallet address is already in use. Please use a unique wallet address."); | ||
} | ||
} | ||
throw error; | ||
} | ||
|
||
async deleteUser(id: number): Promise<boolean> { | ||
const result = await this.userRepository.delete(id); | ||
return result.affected === 1; | ||
} | ||
|
||
async getAllUsers(): Promise<User[]> { | ||
return await this.userRepository.find(); | ||
} | ||
} | ||
|
||
async deleteUser(id: number): Promise<boolean> { | ||
const result = await this.userRepository.delete(id); | ||
return result.affected === 1; | ||
} | ||
|
||
async getAllUsers(): Promise<User[]> { | ||
return await this.userRepository.find(); | ||
} | ||
} | ||
|
||
export default UserService; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,54 @@ | ||
import { setupTestDB, teardownTestDB } from "../../utils/test-utils"; | ||
import { Repository } from "typeorm"; | ||
import AppDataSource from "../../config/ormconfig"; | ||
import { TestService } from "../../services/test.service"; | ||
import { TestEntity } from "../../entities/testEntity"; | ||
|
||
jest.mock("../../config/ormconfig", () => ({ | ||
getRepository: jest.fn(), | ||
})); | ||
|
||
describe("TestService", () => { | ||
let service: TestService; | ||
let mockRepo: jest.Mocked<Repository<TestEntity>>; | ||
|
||
beforeAll(async () => { | ||
await setupTestDB(); | ||
service = new TestService(AppDataSource); | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
|
||
const repo = AppDataSource.getRepository(TestEntity); | ||
await repo.save({ name: "Existing Entity" }); | ||
}); | ||
mockRepo = { | ||
find: jest.fn(), | ||
findOneBy: jest.fn(), | ||
save: jest.fn(), | ||
create: jest.fn(), | ||
delete: jest.fn(), | ||
} as unknown as jest.Mocked<Repository<TestEntity>>; | ||
|
||
(AppDataSource.getRepository as jest.Mock).mockReturnValue(mockRepo); | ||
|
||
afterAll(async () => { | ||
await teardownTestDB(); | ||
service = new TestService(); | ||
}); | ||
|
||
it("should return true if the entity exists", async () => { | ||
mockRepo.findOneBy.mockResolvedValue({ id: 1, name: "Existing Entity" } as TestEntity); | ||
|
||
const result = await service.validateEntityExists("Existing Entity"); | ||
expect(result).toBe(true); | ||
expect(mockRepo.findOneBy).toHaveBeenCalledWith({ name: "Existing Entity" }); | ||
}); | ||
|
||
it("should return false if the entity does not exist", async () => { | ||
mockRepo.findOneBy.mockResolvedValue(null); | ||
|
||
const result = await service.validateEntityExists("Nonexistent Entity"); | ||
expect(result).toBe(false); | ||
expect(mockRepo.findOneBy).toHaveBeenCalledWith({ name: "Nonexistent Entity" }); | ||
}); | ||
|
||
it("should retrieve an entity by name", async () => { | ||
const entity = await service.getEntityByName("Existing Entity"); | ||
expect(entity).toBeDefined(); | ||
expect(entity?.name).toBe("Existing Entity"); | ||
const entity = { id: 1, name: "Existing Entity" } as TestEntity; | ||
mockRepo.findOneBy.mockResolvedValue(entity); | ||
|
||
const result = await service.getEntityByName("Existing Entity"); | ||
expect(result).toEqual(entity); | ||
expect(mockRepo.findOneBy).toHaveBeenCalledWith({ name: "Existing Entity" }); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,116 @@ | ||
import { setupTestDB, teardownTestDB } from "../../utils/test-utils"; | ||
import { Repository } from "typeorm"; | ||
import AppDataSource from "../../config/ormconfig"; | ||
import { UserService } from "../../services/User.service"; | ||
import { User } from "../../entities/User"; | ||
|
||
jest.mock("../../config/ormconfig", () => ({ | ||
getRepository: jest.fn(), | ||
})); | ||
|
||
describe("UserService", () => { | ||
let service: UserService; | ||
let mockRepo: jest.Mocked<Repository<User>>; | ||
|
||
beforeAll(async () => { | ||
await setupTestDB(); | ||
service = new UserService(AppDataSource); | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
|
||
const userRepo = AppDataSource.getRepository(User); | ||
await userRepo.save({ | ||
name: "Existing User", | ||
email: "[email protected]", | ||
walletAddress: "0x06D97198756295A96C2158a23963306f507b2f69", | ||
role: "buyer" as "buyer", // Explicit type cast | ||
}); | ||
}); | ||
mockRepo = { | ||
find: jest.fn(), | ||
findOneBy: jest.fn(), | ||
save: jest.fn(), | ||
create: jest.fn(), | ||
delete: jest.fn(), | ||
update: jest.fn(), | ||
} as unknown as jest.Mocked<Repository<User>>; | ||
|
||
(AppDataSource.getRepository as jest.Mock).mockReturnValue(mockRepo); | ||
|
||
afterAll(async () => { | ||
await teardownTestDB(); | ||
service = new UserService(); | ||
}); | ||
|
||
it("should create a new user", async () => { | ||
const newUser: Partial<User> = { | ||
name: "New User", | ||
email: "[email protected]", | ||
walletAddress: "0x07D97198756295A96C2158a23963306f507b2f70", | ||
role: "buyer", // Ensure it's a valid value | ||
role: "buyer", | ||
}; | ||
|
||
const createdUser = await service.createUser(newUser); | ||
expect(createdUser).toBeDefined(); | ||
expect(createdUser.name).toBe("New User"); | ||
expect(createdUser.email).toBe("[email protected]"); | ||
const createdUser = { id: 1, ...newUser } as User; | ||
mockRepo.create.mockReturnValue(createdUser); | ||
mockRepo.save.mockResolvedValue(createdUser); | ||
|
||
const result = await service.createUser(newUser); | ||
expect(result).toEqual(createdUser); | ||
expect(mockRepo.create).toHaveBeenCalledWith(newUser); | ||
expect(mockRepo.save).toHaveBeenCalledWith(createdUser); | ||
}); | ||
|
||
it("should not create a user with a duplicate wallet address", async () => { | ||
const duplicateUser: Partial<User> = { | ||
name: "Duplicate User", | ||
email: "[email protected]", | ||
walletAddress: "0x06D97198756295A96C2158a23963306f507b2f69", // Same as pre-populated wallet address | ||
role: "buyer", // Ensure it's a valid value | ||
walletAddress: "0x06D97198756295A96C2158a23963306f507b2f69", | ||
role: "buyer", | ||
}; | ||
|
||
const error = new Error("The wallet address is already in use. Please use a unique wallet address.") as any; | ||
error.code = "SQLITE_CONSTRAINT"; | ||
mockRepo.save.mockRejectedValue(error); | ||
|
||
await expect(service.createUser(duplicateUser)).rejects.toThrowError( | ||
"The wallet address is already in use. Please use a unique wallet address." | ||
); | ||
}); | ||
|
||
it("should retrieve an existing user by ID", async () => { | ||
const userRepo = AppDataSource.getRepository(User); | ||
const user = await userRepo.findOne({ where: { email: "[email protected]" } }); | ||
const user = { id: 1, name: "Existing User", email: "[email protected]" } as User; | ||
mockRepo.findOneBy.mockResolvedValue(user); | ||
|
||
const fetchedUser = await service.getUserById(user?.id || 0); | ||
expect(fetchedUser).toBeDefined(); | ||
expect(fetchedUser?.email).toBe("[email protected]"); | ||
const result = await service.getUserById(1); | ||
expect(result).toEqual(user); | ||
expect(mockRepo.findOneBy).toHaveBeenCalledWith({ id: 1 }); | ||
}); | ||
|
||
it("should return null if the user does not exist", async () => { | ||
const nonExistentUser = await service.getUserById(9999); | ||
expect(nonExistentUser).toBeNull(); | ||
mockRepo.findOneBy.mockResolvedValue(null); | ||
|
||
const result = await service.getUserById(9999); | ||
expect(result).toBeNull(); | ||
expect(mockRepo.findOneBy).toHaveBeenCalledWith({ id: 9999 }); | ||
}); | ||
|
||
it("should update a user's information", async () => { | ||
const userRepo = AppDataSource.getRepository(User); | ||
const user = await userRepo.findOne({ where: { email: "[email protected]" } }); | ||
const user = { id: 1, name: "Existing User", email: "[email protected]" } as User; | ||
const updatedData = { name: "Updated User" }; | ||
const updatedUser = { ...user, ...updatedData } as User; | ||
|
||
mockRepo.findOneBy.mockResolvedValue(user); | ||
mockRepo.save.mockResolvedValue(updatedUser); | ||
|
||
const updatedUser = await service.updateUser(user?.id || 0, { name: "Updated User" }); | ||
expect(updatedUser).toBeDefined(); | ||
expect(updatedUser?.name).toBe("Updated User"); | ||
const result = await service.updateUser(1, updatedData); | ||
expect(result).toEqual(updatedUser); | ||
expect(mockRepo.save).toHaveBeenCalledWith({ ...user, ...updatedData }); | ||
}); | ||
|
||
it("should delete a user", async () => { | ||
const userRepo = AppDataSource.getRepository(User); | ||
const user = await userRepo.findOne({ where: { email: "[email protected]" } }); | ||
mockRepo.delete.mockResolvedValue({ affected: 1 } as any); | ||
|
||
const result = await service.deleteUser(1); | ||
expect(result).toBe(true); | ||
expect(mockRepo.delete).toHaveBeenCalledWith(1); | ||
}); | ||
|
||
it("should retrieve all users", async () => { | ||
const users = [ | ||
{ id: 1, name: "User 1", email: "[email protected]" }, | ||
{ id: 2, name: "User 2", email: "[email protected]" }, | ||
] as User[]; | ||
|
||
const success = await service.deleteUser(user?.id || 0); | ||
expect(success).toBe(true); | ||
mockRepo.find.mockResolvedValue(users); | ||
|
||
const deletedUser = await userRepo.findOne({ where: { email: "[email protected]" } }); | ||
expect(deletedUser).toBeNull(); | ||
const result = await service.getAllUsers(); | ||
expect(result).toEqual(users); | ||
expect(mockRepo.find).toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,21 @@ | ||
import testDataSource from "../config/ormconfig.test"; | ||
import AppDataSource from "../config/ormconfig"; | ||
|
||
export const setupTestDB = async () => { | ||
if (!testDataSource.isInitialized) { | ||
await testDataSource.initialize(); | ||
export async function setupTestDB() { | ||
console.log("Initializing database..."); | ||
if (!AppDataSource.isInitialized) { | ||
await AppDataSource.initialize(); | ||
console.log("Database initialized."); | ||
} | ||
await testDataSource.synchronize(true); // Sync | ||
}; | ||
export const teardownTestDB = async () => { | ||
if (testDataSource.isInitialized) { | ||
await testDataSource.destroy(); | ||
|
||
console.log("Synchronizing database..."); | ||
await AppDataSource.synchronize(true); // Forzar sincronización | ||
console.log("Database synchronized."); | ||
} | ||
|
||
export async function teardownTestDB() { | ||
console.log("Destroying database..."); | ||
if (AppDataSource.isInitialized) { | ||
await AppDataSource.destroy(); | ||
console.log("Database destroyed."); | ||
} | ||
}; | ||
} |