From bac76d16e2af298a1a64e3fcaa4b06b947b64f75 Mon Sep 17 00:00:00 2001 From: Kai Moritz Date: Sun, 12 Oct 2025 09:20:07 +0200 Subject: [PATCH] feat: Introduced method `createUser()` in `UserService` * The method requests the creation of a new user and remembers the created instance of `User`, if the requests ist successful. * The remembered `User` can be retrieved via `UserService.getUser()`. * Also added a pact between the `UserService` and the `ChatBackendController`, that formulates the expectations of the `UserService`, that are not implemented yet by the backend. --- pacts | 2 +- src/app/chatroom/chatroom.model.ts | 6 +++ src/app/chatroom/chatroom.service.ts | 4 +- src/app/user/user.component.spec.ts | 4 ++ src/app/user/user.component.ts | 2 +- src/app/user/user.service.pact.spec.ts | 61 ++++++++++++++++++++++++++ src/app/user/user.service.spec.ts | 11 ++++- src/app/user/user.service.ts | 37 ++++++++++++++-- 8 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 src/app/user/user.service.pact.spec.ts diff --git a/pacts b/pacts index a0e97a51..6d555b93 160000 --- a/pacts +++ b/pacts @@ -1 +1 @@ -Subproject commit a0e97a512e5e56f69035853d1510ae5efdde4d64 +Subproject commit 6d555b93a94e959095008ce51b4dacd986998eec diff --git a/src/app/chatroom/chatroom.model.ts b/src/app/chatroom/chatroom.model.ts index 179bc586..9eaa9a8c 100644 --- a/src/app/chatroom/chatroom.model.ts +++ b/src/app/chatroom/chatroom.model.ts @@ -1,3 +1,9 @@ +export interface User +{ + id: string, + shard: number +} + export interface Chatroom { id: string, diff --git a/src/app/chatroom/chatroom.service.ts b/src/app/chatroom/chatroom.service.ts index f18f8e72..a50ba3c3 100644 --- a/src/app/chatroom/chatroom.service.ts +++ b/src/app/chatroom/chatroom.service.ts @@ -14,8 +14,8 @@ class FatalError extends Error { } }) export class ChatroomService { - private http = inject(HttpClient); - private backendUri: string; + private readonly http = inject(HttpClient); + private readonly backendUri: string; private chatroom?: Chatroom; diff --git a/src/app/user/user.component.spec.ts b/src/app/user/user.component.spec.ts index b0a097d2..e69ff5cf 100644 --- a/src/app/user/user.component.spec.ts +++ b/src/app/user/user.component.spec.ts @@ -2,6 +2,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { UserComponent } from './index'; import { APP_PROPS } from '../app.tokens'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; describe('UserComponent', () => { let component: UserComponent; @@ -14,6 +16,8 @@ describe('UserComponent', () => { UserComponent, ], providers: [ + provideHttpClient(), + provideHttpClientTesting(), { provide: APP_PROPS, useValue: {} }, ], }) diff --git a/src/app/user/user.component.ts b/src/app/user/user.component.ts index dcb4dad1..4d7b3ab0 100644 --- a/src/app/user/user.component.ts +++ b/src/app/user/user.component.ts @@ -22,7 +22,7 @@ export class UserComponent { updateName(): void { let input = this.usernameForm.getRawValue(); if (input !== null) { - this.userService.setUser(input.trim()); + this.userService.setUserName(input.trim()); this.router.navigate(['chatrooms']) } } diff --git a/src/app/user/user.service.pact.spec.ts b/src/app/user/user.service.pact.spec.ts new file mode 100644 index 00000000..e4802f72 --- /dev/null +++ b/src/app/user/user.service.pact.spec.ts @@ -0,0 +1,61 @@ +import { Matchers, Pact, SpecificationVersion } from '@pact-foundation/pact'; +import { TestBed } from '@angular/core/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { lastValueFrom } from 'rxjs'; +import { ChatroomService } from './chatroom.service'; +import { Chatroom } from './chatroom.model'; +import { APP_PROPS } from '../app.tokens'; +import { User } from '../chatroom'; +import { UserService } from './user.service'; + + +describe('Pact between the UserService and the backend', () => { + + const provider = new Pact({ + consumer: 'UserService', + provider: 'ChatBackendController', + spec: SpecificationVersion.SPECIFICATION_VERSION_V4, + dir: 'pacts', + logLevel: 'debug', + }); + + + const EXAMPLE_USER: User = { + id: "5c73531c-6fc4-426c-adcb-afc5c140a0f7", + shard: 2 + }; + + + it('POST /user/create', async () => { + + await provider + .addInteraction() + .given('there are 10 shards') + .given('the server is responsible for shard 2') + .uponReceiving('a request to create a user') + .withRequest('POST', '/user/create', (builder) => { + builder.headers({ + Accept: Matchers.like('application/json'), + }); + }) + .willRespondWith(200, (builder) => { + builder.headers({ 'Content-Type': 'application/json' }); + builder.jsonBody(EXAMPLE_USER); + }) + .executeTest(async (mockserver) => { + await TestBed.configureTestingModule({ + providers: [ + UserService, + {provide: APP_PROPS, useValue: {backendUri: mockserver.url + '/'}}, + provideHttpClient(), + ] + }); + + const service = TestBed.inject(UserService); + const user = await service.createUser(); + + expect(user).toEqual(EXAMPLE_USER); + expect(service.getUser()).toEqual(EXAMPLE_USER); + }); + }); +}); diff --git a/src/app/user/user.service.spec.ts b/src/app/user/user.service.spec.ts index 4144cfd7..d439cf5f 100644 --- a/src/app/user/user.service.spec.ts +++ b/src/app/user/user.service.spec.ts @@ -1,12 +1,21 @@ import { TestBed } from '@angular/core/testing'; import { UserService } from './index'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { APP_PROPS } from '../app.tokens'; describe('UserService', () => { let service: UserService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + { provide: APP_PROPS, useValue: {} }, + ], + }); service = TestBed.inject(UserService); }); diff --git a/src/app/user/user.service.ts b/src/app/user/user.service.ts index bc38fef2..24bb23e6 100644 --- a/src/app/user/user.service.ts +++ b/src/app/user/user.service.ts @@ -1,15 +1,29 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Router } from "@angular/router"; +import { HttpClient } from '@angular/common/http'; +import { APP_PROPS } from '../app.tokens'; +import { User } from '../chatroom'; +import { firstValueFrom, Observable, tap } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserService { + private readonly router = inject(Router); + + private readonly http = inject(HttpClient); + private readonly backendUri: string; + private unknown: boolean = true; + private user: User|undefined = undefined; private name = ''; - constructor(private router: Router) { } + + constructor() { + const props = inject(APP_PROPS); + this.backendUri = props.backendUri; + } assertUserisKnown(callback: Function): void { if(this.unknown) { @@ -20,13 +34,28 @@ export class UserService { } } - setUser(name: string): void { + async createUser(): Promise { + return await firstValueFrom( + this + .http + .post(this.backendUri + 'user/create', undefined) + .pipe(tap((user: User) => { + console.log('created a new user: ' + user); + this.user = user; + }))); + } + + getUser(): User|undefined { + return this.user; + } + + setUserName(name: string): void { console.log("New user: " + name); this.name = name; this.unknown = false; } - getUser(): string { + getUserName(): string { return this.name; } } -- 2.39.5