Merged PR 1885: Remi Add Flow - ohne offener Remi

Related work items: #5135
This commit is contained in:
Lorenz Hilpert
2025-07-17 13:53:36 +00:00
committed by Nino Righi
parent b015e97e1f
commit 442670bdd0
47 changed files with 4271 additions and 2076 deletions

View File

@@ -0,0 +1,222 @@
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { CatalougeSearchService } from './catalouge-search.service';
import { SearchService } from '@generated/swagger/cat-search-api';
import { Item } from '../models';
import { SearchByTermInput } from '../schemas/catalouge-search.schemas';
describe('CatalougeSearchService', () => {
let service: CatalougeSearchService;
let searchServiceSpy: jest.Mocked<SearchService>;
beforeEach(() => {
const searchServiceMock = {
SearchByEAN: jest.fn(),
SearchSearch: jest.fn(),
};
TestBed.configureTestingModule({
providers: [
CatalougeSearchService,
{ provide: SearchService, useValue: searchServiceMock },
],
});
service = TestBed.inject(CatalougeSearchService);
searchServiceSpy = TestBed.inject(SearchService) as jest.Mocked<SearchService>;
});
it('should be created', () => {
expect(service).toBeTruthy();
});
describe('searchByEans', () => {
it('should return items when search is successful', (done) => {
// Arrange
const mockItems: Item[] = [
{ id: 1, product: { name: 'Item 1' }, catalogAvailability: { available: true } } as unknown as Item,
{ id: 2, product: { name: 'Item 2' }, catalogAvailability: { available: true } } as unknown as Item,
];
const mockResponse = {
error: false,
result: mockItems,
};
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
// Act
service.searchByEans('123456789', '987654321').subscribe({
next: (result) => {
// Assert
expect(result).toEqual(mockItems);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith(['123456789', '987654321']);
done();
},
error: done.fail,
});
});
it('should throw error when response has error', (done) => {
// Arrange
const mockResponse = {
error: true,
message: 'Search failed',
};
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
// Act
service.searchByEans('123456789').subscribe({
next: () => done.fail('Should have thrown error'),
error: (error) => {
// Assert
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe('Search failed');
done();
},
});
});
it('should handle single EAN', (done) => {
// Arrange
const mockItems: Item[] = [{ id: 1, product: { name: 'Item 1' }, catalogAvailability: { available: true } } as unknown as Item];
const mockResponse = {
error: false,
result: mockItems,
};
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
// Act
service.searchByEans('123456789').subscribe({
next: (result) => {
// Assert
expect(result).toEqual(mockItems);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith(['123456789']);
done();
},
error: done.fail,
});
});
it('should handle empty EAN array', (done) => {
// Arrange
const mockResponse = {
error: false,
result: [],
};
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
// Act
service.searchByEans().subscribe({
next: (result) => {
// Assert
expect(result).toEqual([]);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith([]);
done();
},
error: done.fail,
});
});
});
describe('searchByTerm', () => {
it('should return search results when successful', async () => {
// Arrange
const mockItems: Item[] = [
{ id: 1, product: { name: 'Test Item' }, catalogAvailability: { available: true } } as unknown as Item,
];
const mockResponse = {
error: false,
result: mockItems,
total: 1,
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: SearchByTermInput = {
searchTerm: 'test',
skip: 0,
take: 10,
};
const abortController = new AbortController();
// Act
const result = await service.searchByTerm(params, abortController.signal);
// Assert
expect(result).toEqual(mockResponse);
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
input: { qs: 'test' },
skip: 0,
take: 10,
doNotTrack: true,
});
});
it('should throw error when response has error', async () => {
// Arrange
const mockResponse = {
error: true,
message: 'Search failed',
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: SearchByTermInput = {
searchTerm: 'test',
skip: 0,
take: 10,
};
const abortController = new AbortController();
// Act & Assert
await expect(service.searchByTerm(params, abortController.signal))
.rejects
.toThrow('Search failed');
});
it('should handle abort signal', async () => {
// Arrange
const abortController = new AbortController();
const mockResponse = {
error: false,
result: [],
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: SearchByTermInput = {
searchTerm: 'test',
skip: 0,
take: 10,
};
// Act
const result = await service.searchByTerm(params, abortController.signal);
// Assert
expect(result).toEqual(mockResponse);
expect(searchServiceSpy.SearchSearch).toHaveBeenCalled();
});
it('should use default values when not provided', async () => {
// Arrange
const mockResponse = {
error: false,
result: [],
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: SearchByTermInput = {
searchTerm: 'test',
};
const abortController = new AbortController();
// Act
await service.searchByTerm(params, abortController.signal);
// Assert
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
input: { qs: 'test' },
skip: 0,
take: 20,
doNotTrack: true,
});
});
});
});

View File

@@ -1,52 +1,53 @@
import { inject, Injectable } from '@angular/core';
import { SearchService } from '@generated/swagger/cat-search-api';
import { firstValueFrom, map, Observable } from 'rxjs';
import { takeUntilAborted } from '@isa/common/data-access';
import { Item } from '../models';
import {
SearchByTermInput,
SearchByTermSchema,
} from '../schemas/catalouge-search.schemas';
import { ListResponseArgs } from '@isa/common/data-access';
@Injectable({ providedIn: 'root' })
export class CatalougeSearchService {
#searchService = inject(SearchService);
searchByEans(...ean: string[]): Observable<Item[]> {
return this.#searchService.SearchByEAN(ean).pipe(
map((res) => {
if (res.error) {
throw new Error(res.message);
}
return res.result as Item[];
}),
);
}
async searchByTerm(
params: SearchByTermInput,
abortSignal: AbortSignal,
): Promise<ListResponseArgs<Item>> {
const { searchTerm, skip, take } = SearchByTermSchema.parse(params);
const req$ = this.#searchService
.SearchSearch({
filter: {
qs: searchTerm,
},
skip,
take,
})
.pipe(takeUntilAborted(abortSignal));
const res = await firstValueFrom(req$);
if (res.error) {
throw new Error(res.message);
}
return res as ListResponseArgs<Item>;
}
}
import { inject, Injectable } from '@angular/core';
import { SearchService } from '@generated/swagger/cat-search-api';
import { firstValueFrom, map, Observable } from 'rxjs';
import { takeUntilAborted } from '@isa/common/data-access';
import { Item } from '../models';
import {
SearchByTermInput,
SearchByTermSchema,
} from '../schemas/catalouge-search.schemas';
import { ListResponseArgs } from '@isa/common/data-access';
@Injectable({ providedIn: 'root' })
export class CatalougeSearchService {
#searchService = inject(SearchService);
searchByEans(...ean: string[]): Observable<Item[]> {
return this.#searchService.SearchByEAN(ean).pipe(
map((res) => {
if (res.error) {
throw new Error(res.message);
}
return res.result as Item[];
}),
);
}
async searchByTerm(
params: SearchByTermInput,
abortSignal: AbortSignal,
): Promise<ListResponseArgs<Item>> {
const { searchTerm, skip, take } = SearchByTermSchema.parse(params);
const req$ = this.#searchService
.SearchSearch({
input: {
qs: searchTerm,
},
skip,
take,
doNotTrack: true,
})
.pipe(takeUntilAborted(abortSignal));
const res = await firstValueFrom(req$);
if (res.error) {
throw new Error(res.message);
}
return res as ListResponseArgs<Item>;
}
}