feat(core): add mock implementations (users, articles, auth, telemetry)
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
import { injectable } from "inversify";
|
||||||
|
|
||||||
|
import type { IArticlesRepository } from "@/application/repositories/articles.repository.interface.js";
|
||||||
|
import type { Article } from "@/entities/models/article.js";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MockArticlesRepository implements IArticlesRepository {
|
||||||
|
private _articles: Article[] = [];
|
||||||
|
|
||||||
|
async getArticle(id: string): Promise<Article | undefined> {
|
||||||
|
return this._articles.find((a) => a.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getArticles(options?: {
|
||||||
|
status?: string;
|
||||||
|
authorId?: string;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}): Promise<Article[]> {
|
||||||
|
let result = [...this._articles];
|
||||||
|
if (options?.status) {
|
||||||
|
result = result.filter((a) => a.status === options.status);
|
||||||
|
}
|
||||||
|
if (options?.authorId) {
|
||||||
|
result = result.filter((a) => a.authorId === options.authorId);
|
||||||
|
}
|
||||||
|
const offset = options?.offset ?? 0;
|
||||||
|
const limit = options?.limit ?? 50;
|
||||||
|
return result.slice(offset, offset + limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createArticle(input: Article): Promise<Article> {
|
||||||
|
this._articles.push(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateArticle(
|
||||||
|
id: string,
|
||||||
|
input: Partial<Article>
|
||||||
|
): Promise<Article | undefined> {
|
||||||
|
const index = this._articles.findIndex((a) => a.id === id);
|
||||||
|
if (index === -1) return undefined;
|
||||||
|
this._articles[index] = { ...this._articles[index]!, ...input };
|
||||||
|
return this._articles[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { injectable } from "inversify";
|
||||||
|
|
||||||
|
import type { IUsersRepository } from "@/application/repositories/users.repository.interface.js";
|
||||||
|
import type { User } from "@/entities/models/user.js";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MockUsersRepository implements IUsersRepository {
|
||||||
|
private _users: User[] = [
|
||||||
|
{ id: "1", username: "alice", passwordHash: "hashed_password_alice" },
|
||||||
|
{ id: "2", username: "bob", passwordHash: "hashed_password_bob" },
|
||||||
|
];
|
||||||
|
|
||||||
|
async getUser(id: string): Promise<User | undefined> {
|
||||||
|
return this._users.find((u) => u.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserByUsername(username: string): Promise<User | undefined> {
|
||||||
|
return this._users.find((u) => u.username === username);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createUser(input: User): Promise<User> {
|
||||||
|
this._users.push(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { inject, injectable } from "inversify";
|
||||||
|
|
||||||
|
import type { IAuthenticationService } from "@/application/services/auth.service.interface.js";
|
||||||
|
import type { IUsersRepository } from "@/application/repositories/users.repository.interface.js";
|
||||||
|
import { UnauthenticatedError } from "@/entities/errors/auth.js";
|
||||||
|
import { sessionSchema, type Session } from "@/entities/models/session.js";
|
||||||
|
import type { Cookie } from "@/entities/models/cookie.js";
|
||||||
|
import type { User } from "@/entities/models/user.js";
|
||||||
|
import { DI_SYMBOLS } from "@/di/types.js";
|
||||||
|
import { SESSION_COOKIE } from "@/config.js";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MockAuthenticationService implements IAuthenticationService {
|
||||||
|
private _sessions: Record<string, { session: Session; user: User }> = {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@inject(DI_SYMBOLS.IUsersRepository)
|
||||||
|
private _usersRepository: IUsersRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
generateUserId(): string {
|
||||||
|
return (Math.random() + 1).toString(36).substring(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
async hashPassword(password: string): Promise<string> {
|
||||||
|
return `hashed_${password}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyPassword(hash: string, password: string): Promise<boolean> {
|
||||||
|
return hash === `hashed_${password}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateSession(
|
||||||
|
sessionId: string
|
||||||
|
): Promise<{ user: User; session: Session }> {
|
||||||
|
const result = this._sessions[sessionId];
|
||||||
|
if (!result) {
|
||||||
|
throw new UnauthenticatedError("Unauthenticated");
|
||||||
|
}
|
||||||
|
const user = await this._usersRepository.getUser(result.user.id);
|
||||||
|
if (!user) {
|
||||||
|
throw new UnauthenticatedError("Unauthenticated");
|
||||||
|
}
|
||||||
|
return { user, session: result.session };
|
||||||
|
}
|
||||||
|
|
||||||
|
async createSession(
|
||||||
|
user: User
|
||||||
|
): Promise<{ session: Session; cookie: Cookie }> {
|
||||||
|
const session = sessionSchema.parse({
|
||||||
|
id: "session_" + user.id,
|
||||||
|
userId: user.id,
|
||||||
|
expiresAt: new Date(Date.now() + 86400000 * 7),
|
||||||
|
});
|
||||||
|
const cookie: Cookie = {
|
||||||
|
name: SESSION_COOKIE,
|
||||||
|
value: session.id,
|
||||||
|
attributes: {},
|
||||||
|
};
|
||||||
|
this._sessions[session.id] = { session, user };
|
||||||
|
return { session, cookie };
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidateSession(
|
||||||
|
sessionId: string
|
||||||
|
): Promise<{ blankCookie: Cookie }> {
|
||||||
|
delete this._sessions[sessionId];
|
||||||
|
return {
|
||||||
|
blankCookie: { name: SESSION_COOKIE, value: "", attributes: {} },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { injectable } from "inversify";
|
||||||
|
|
||||||
|
import type { ITelemetryService } from "@/application/services/telemetry.service.interface.js";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MockTelemetryService implements ITelemetryService {
|
||||||
|
async startSpan<T>(_name: string, fn: () => T | Promise<T>): Promise<T> {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user