Add unit tests for models and services
- Implemented unit tests for the Package model, covering methods such as generateId, findById, findByName, and version management. - Created unit tests for the Repository model, including repository creation, name validation, and retrieval methods. - Added tests for the Session model, focusing on session creation, validation, and invalidation. - Developed unit tests for the User model, ensuring user creation, password hashing, and retrieval methods function correctly. - Implemented AuthService tests, validating login, token refresh, and session management. - Added TokenService tests, covering token creation, validation, and revocation processes.
This commit is contained in:
@@ -11,7 +11,7 @@ import { ToastService } from '../../core/services/toast.service';
|
||||
<div class="p-6 max-w-7xl mx-auto">
|
||||
@if (loading()) {
|
||||
<div class="flex items-center justify-center py-12">
|
||||
<svg class="animate-spin h-8 w-8 text-primary-600" fill="none" viewBox="0 0 24 24">
|
||||
<svg class="animate-spin h-8 w-8 text-primary" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||
</svg>
|
||||
@@ -20,33 +20,36 @@ import { ToastService } from '../../core/services/toast.service';
|
||||
<!-- Header -->
|
||||
<div class="flex items-start justify-between mb-8">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="w-16 h-16 bg-gray-200 dark:bg-gray-700 rounded-xl flex items-center justify-center">
|
||||
<span class="text-2xl font-medium text-gray-600 dark:text-gray-300">
|
||||
<div class="w-16 h-16 bg-muted flex items-center justify-center">
|
||||
<span class="font-mono text-2xl font-medium text-muted-foreground">
|
||||
{{ organization()!.name.charAt(0).toUpperCase() }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">{{ organization()!.displayName }}</h1>
|
||||
<p class="text-gray-500 dark:text-gray-400">@{{ organization()!.name }}</p>
|
||||
<h1 class="font-mono text-2xl font-bold text-foreground">{{ organization()!.displayName }}</h1>
|
||||
<p class="font-mono text-muted-foreground">@{{ organization()!.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
@if (organization()!.isPublic) {
|
||||
<span class="badge-default">Public</span>
|
||||
<span class="badge-accent">Public</span>
|
||||
} @else {
|
||||
<span class="badge-warning">Private</span>
|
||||
<span class="badge-primary">Private</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (organization()!.description) {
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-8">{{ organization()!.description }}</p>
|
||||
<p class="font-mono text-muted-foreground mb-8">{{ organization()!.description }}</p>
|
||||
}
|
||||
|
||||
<!-- Repositories Section -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Repositories</h2>
|
||||
<div class="section-header">
|
||||
<div class="section-indicator"></div>
|
||||
<span class="section-label">Repositories</span>
|
||||
</div>
|
||||
<button class="btn-primary btn-sm">
|
||||
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
@@ -57,26 +60,26 @@ import { ToastService } from '../../core/services/toast.service';
|
||||
|
||||
@if (repositories().length === 0) {
|
||||
<div class="card card-content text-center py-8">
|
||||
<p class="text-gray-500 dark:text-gray-400">No repositories yet</p>
|
||||
<p class="font-mono text-muted-foreground">No repositories yet</p>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@for (repo of repositories(); track repo.id) {
|
||||
<a [routerLink]="['repositories', repo.id]" class="card card-content hover:border-primary-300 dark:hover:border-primary-700 transition-colors">
|
||||
<a [routerLink]="['repositories', repo.id]" class="card card-content hover:border-primary/50 transition-colors">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 class="font-medium text-gray-900 dark:text-gray-100">{{ repo.displayName }}</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">{{ repo.name }}</p>
|
||||
<h3 class="font-mono font-medium text-foreground">{{ repo.displayName }}</h3>
|
||||
<p class="font-mono text-sm text-muted-foreground">{{ repo.name }}</p>
|
||||
</div>
|
||||
@if (repo.isPublic) {
|
||||
<span class="badge-default">Public</span>
|
||||
<span class="badge-accent">Public</span>
|
||||
}
|
||||
</div>
|
||||
@if (repo.description) {
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-2 line-clamp-2">{{ repo.description }}</p>
|
||||
<p class="font-mono text-sm text-muted-foreground mt-2 line-clamp-2">{{ repo.description }}</p>
|
||||
}
|
||||
<div class="mt-3 flex items-center gap-4">
|
||||
<div class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div class="flex items-center gap-1 font-mono text-sm text-muted-foreground">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
||||
</svg>
|
||||
@@ -95,18 +98,24 @@ import { ToastService } from '../../core/services/toast.service';
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="mb-4">
|
||||
<div class="section-header">
|
||||
<div class="section-indicator"></div>
|
||||
<span class="section-label">Statistics</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="card card-content">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Members</p>
|
||||
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">{{ organization()!.memberCount }}</p>
|
||||
<p class="font-mono text-sm text-muted-foreground">Members</p>
|
||||
<p class="font-mono text-2xl font-bold text-foreground">{{ organization()!.memberCount }}</p>
|
||||
</div>
|
||||
<div class="card card-content">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Repositories</p>
|
||||
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">{{ repositories().length }}</p>
|
||||
<p class="font-mono text-sm text-muted-foreground">Repositories</p>
|
||||
<p class="font-mono text-2xl font-bold text-foreground">{{ repositories().length }}</p>
|
||||
</div>
|
||||
<div class="card card-content">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Created</p>
|
||||
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">{{ formatDate(organization()!.createdAt) }}</p>
|
||||
<p class="font-mono text-sm text-muted-foreground">Created</p>
|
||||
<p class="font-mono text-2xl font-bold text-foreground">{{ formatDate(organization()!.createdAt) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -123,18 +132,18 @@ export class OrganizationDetailComponent implements OnInit {
|
||||
loading = signal(true);
|
||||
|
||||
ngOnInit(): void {
|
||||
const orgId = this.route.snapshot.paramMap.get('orgId');
|
||||
if (orgId) {
|
||||
this.loadData(orgId);
|
||||
const orgName = this.route.snapshot.paramMap.get('orgName');
|
||||
if (orgName) {
|
||||
this.loadData(orgName);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadData(orgId: string): Promise<void> {
|
||||
private async loadData(orgName: string): Promise<void> {
|
||||
this.loading.set(true);
|
||||
try {
|
||||
const [org, reposResponse] = await Promise.all([
|
||||
this.apiService.getOrganization(orgId).toPromise(),
|
||||
this.apiService.getRepositories(orgId).toPromise(),
|
||||
this.apiService.getOrganization(orgName).toPromise(),
|
||||
this.apiService.getRepositories(orgName).toPromise(),
|
||||
]);
|
||||
this.organization.set(org || null);
|
||||
this.repositories.set(reposResponse?.repositories || []);
|
||||
|
||||
Reference in New Issue
Block a user