fix(mana-core-auth): complete production readiness with test fixes

- Fix LoggerService mock in better-auth.service.spec.ts
- Fix name assertion in auth.controller.spec.ts (empty string fallback)
- Fix createRemoteJWKSet mock in jwt-auth.guard.spec.ts
- Add Grafana dashboard for Auth Service monitoring
- Add 10 auth-specific Prometheus alert rules
- Update production readiness plan to 100% complete

All 199 unit tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-01 14:18:58 +01:00
parent e3774ca08b
commit fe33f4b355
14 changed files with 1282 additions and 25 deletions

View file

@ -4,7 +4,7 @@
* Common utilities for writing tests
*/
import { ConfigService } from '@nestjs/config';
import { type ConfigService } from '@nestjs/config';
/**
* Create mock ConfigService

View file

@ -148,7 +148,7 @@ export class AnalyticsService implements OnModuleInit, OnModuleDestroy {
/**
* Get user growth over time
*/
async getUserGrowth(days: number = 90): Promise<GrowthData[]> {
async getUserGrowth(days = 90): Promise<GrowthData[]> {
if (!this.duckdb) return [];
const result = await this.duckdb.all(
@ -171,7 +171,7 @@ export class AnalyticsService implements OnModuleInit, OnModuleDestroy {
/**
* Get monthly aggregated metrics
*/
async getMonthlyMetrics(months: number = 12): Promise<MonthlyMetrics[]> {
async getMonthlyMetrics(months = 12): Promise<MonthlyMetrics[]> {
if (!this.duckdb) return [];
const result = await this.duckdb.all(

View file

@ -139,7 +139,8 @@ describe('AuthController', () => {
expect(betterAuthService.registerB2C).toHaveBeenCalledWith({
email: registerDto.email,
password: registerDto.password,
name: undefined, // Controller passes undefined when name is not provided
name: '', // Controller passes empty string as fallback when name is not provided
sourceAppUrl: undefined,
});
});

View file

@ -8,7 +8,12 @@ import { ReferralsModule } from '../referrals/referrals.module';
@Module({
imports: [forwardRef(() => ReferralsModule)],
controllers: [AuthController, BetterAuthPassthroughController, OidcController, OidcLoginController],
controllers: [
AuthController,
BetterAuthPassthroughController,
OidcController,
OidcLoginController,
],
providers: [BetterAuthService],
exports: [BetterAuthService],
})

View file

@ -22,9 +22,9 @@
*/
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { type ConfigService } from '@nestjs/config';
import { SignJWT, jwtVerify, errors } from 'jose';
import { JWTCustomPayload } from './better-auth.config';
import { type JWTCustomPayload } from './better-auth.config';
import { createMockConfigService } from '../__tests__/utils/test-helpers';
import { mockUserFactory } from '../__tests__/utils/mock-factories';

View file

@ -14,6 +14,7 @@ import type { TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { ConflictException, NotFoundException, ForbiddenException } from '@nestjs/common';
import { BetterAuthService } from './better-auth.service';
import { LoggerService } from '../../common/logger';
import { createMockConfigService } from '../../__tests__/utils/test-helpers';
import { silentError } from '../../__tests__/utils/silent-error.decorator';
@ -68,6 +69,15 @@ const mockReferralTrackingService = {
applyReferral: jest.fn().mockResolvedValue({ success: true }),
};
const mockLoggerService = {
setContext: jest.fn().mockReturnThis(),
log: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
verbose: jest.fn(),
};
describe('BetterAuthService', () => {
let service: BetterAuthService;
let configService: ConfigService;
@ -116,6 +126,10 @@ describe('BetterAuthService', () => {
provide: ReferralTrackingService,
useValue: mockReferralTrackingService,
},
{
provide: LoggerService,
useValue: mockLoggerService,
},
],
}).compile();

View file

@ -17,11 +17,16 @@ import { LoggerService } from '../logger';
import { createMockConfigService, httpMockHelpers } from '../../__tests__/utils/test-helpers';
import { mockTokenFactory } from '../../__tests__/utils/mock-factories';
import { silentError } from '../../__tests__/utils/silent-error.decorator';
import { jwtVerify } from 'jose';
import { jwtVerify, createRemoteJWKSet } from 'jose';
// Mock jose (auto-mocked via jest.config.js moduleNameMapper)
jest.mock('jose');
// Setup mock for createRemoteJWKSet to return a defined JWKS function
const mockJWKS = jest.fn();
const mockCreateRemoteJWKSet = createRemoteJWKSet as jest.MockedFunction<typeof createRemoteJWKSet>;
mockCreateRemoteJWKSet.mockReturnValue(mockJWKS as any);
// Mock LoggerService
const createMockLoggerService = (): LoggerService =>
({
@ -43,6 +48,9 @@ describe('JwtAuthGuard', () => {
// Reset mocks
jest.clearAllMocks();
// Ensure createRemoteJWKSet returns a defined value after clearing
mockCreateRemoteJWKSet.mockReturnValue(mockJWKS as any);
const module: TestingModule = await Test.createTestingModule({
providers: [
JwtAuthGuard,

View file

@ -23,7 +23,7 @@ import { BadRequestException, ForbiddenException, NotFoundException } from '@nes
import { CreditsController } from './credits.controller';
import { CreditsService } from './credits.service';
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
import { CurrentUserData } from '../common/decorators/current-user.decorator';
import { type CurrentUserData } from '../common/decorators/current-user.decorator';
import {
mockBalanceFactory,
mockTransactionFactory,

View file

@ -466,7 +466,7 @@ export class FraudDetectionService {
/**
* Get pending review items
*/
async getPendingReviews(limit: number = 50, offset: number = 0): Promise<ReviewQueueItem[]> {
async getPendingReviews(limit = 50, offset = 0): Promise<ReviewQueueItem[]> {
const db = this.getDb();
return db

View file

@ -107,7 +107,7 @@ export class ReferralTierService {
calculateBonus(
eventType: keyof typeof BONUS_AMOUNTS,
tier: TierName,
isReferrer: boolean = true
isReferrer = true
): { base: number; multiplier: number; final: number } {
const bonusConfig = BONUS_AMOUNTS[eventType];
const base = isReferrer ? bonusConfig.referrer : bonusConfig.referee;

View file

@ -501,8 +501,8 @@ export class ReferralTrackingService {
async getReferredUsers(
userId: string,
status?: string,
limit: number = 20,
offset: number = 0
limit = 20,
offset = 0
): Promise<PaginatedResponse<ReferredUser>> {
const db = this.getDb();
@ -734,7 +734,7 @@ export class ReferralTrackingService {
): Promise<number> {
// Basic fraud score calculation
// Full fraud detection will be implemented in Phase 3
let score = 0;
const score = 0;
// For now, just return 0 (no fraud detected)
// TODO: Implement full fraud detection in Phase 3